# ProseMirror Flat list (Alpha)

**URL:** <https://discuss.prosemirror.net/t/prosemirror-flat-list-alpha/5191>\
**Category:** Show\
**Created:** [February 6, 2023, 2:23pm UTC](https://discuss.prosemirror.net/t/prosemirror-flat-list-alpha/5191 "2023-02-06T14:23:14Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![ocavue](https://discuss.prosemirror.net/user_avatar/discuss.prosemirror.net/ocavue/32/2239_2.png) [@ocavue](https://discuss.prosemirror.net/u/ocavue)\
**Post date:** [February 6, 2023, 2:23pm UTC](https://discuss.prosemirror.net/t/prosemirror-flat-list-alpha/5191/1 "2023-02-06T14:23:15Z")

</div>

Hello everyone, I’m excited to share ProseMirror Flat List, an alternative to the official `prosemirror-schema-list`. My aim is to provide a top-notch and native feeling list that is equal to or even better than what Google Docs and Notion have currently.

You can read more about the difference between this project and `prosemirror-schema-list` in the project README.

- [GitHub repo](https://github.com/ocavue/prosemirror-flat-list)
- [Online demo](https://remirror-extension-flat-list.netlify.app/)

This project is still in the alpha stage. Any comments are appreciated!

---

<div class="post-metadata">

**Author:** ![marijn](https://discuss.prosemirror.net/user_avatar/discuss.prosemirror.net/marijn/32/15_2.png) [@marijn](https://discuss.prosemirror.net/u/marijn)\
**Post date:** [February 6, 2023, 4:00pm UTC](https://discuss.prosemirror.net/t/prosemirror-flat-list-alpha/5191/2 "2023-02-06T16:00:46Z")

</div>

Very cool!

One thing that might cause issues for people is the use of Tab and Shift-Tab—keyboard-only users _need_ those to move focus in the page, so it is recommended to not override them for custom functionality.

---

<div class="post-metadata">

**Author:** ![gethari](https://discuss.prosemirror.net/user_avatar/discuss.prosemirror.net/gethari/32/3168_2.png) [@gethari](https://discuss.prosemirror.net/u/gethari)\
**Post date:** [February 7, 2023, 12:04pm UTC](https://discuss.prosemirror.net/t/prosemirror-flat-list-alpha/5191/3 "2023-02-07T12:04:05Z")

</div>

Do you have plans to support `listType` as well ? Basically when we go nested based on the level the type will change

![image](https://discuss.prosemirror.net/uploads/secondsite/original/2X/d/d471ccf8089a3c63834bbf38096f1fb6efcea4f7.png)

---

<div class="post-metadata">

**Author:** ![TeemuKoivisto](https://discuss.prosemirror.net/user_avatar/discuss.prosemirror.net/teemukoivisto/32/1185_2.png) [@TeemuKoivisto](https://discuss.prosemirror.net/u/TeemuKoivisto)\
**Post date:** [February 8, 2023, 8:39am UTC](https://discuss.prosemirror.net/t/prosemirror-flat-list-alpha/5191/4 "2023-02-08T08:39:44Z")

</div>

Cool! I also spent a considerable amount of time fixing a list implementation that used flat list items (like this [czi-prosemirror demo](http://cdn.summitlearning.org/assets/czi_prosemirror_0_0_1_1_20190509151928_index.html)). Don’t know if you update the ordered list numbers but that was a doozy to build. I still kept the original `<ul>` and `<ol>` tags and just changed `<li>` indent property when tabbed. Don’t think I allowed nesting block nodes inside `<li>` elements though. I also included changing of list-style based on indent depth.

I could make a PR if you want to take a look and decide what to incorporate if anything. Surprisingly difficult, I must say. TipTap seemed to have a pretty good version as well. You should also make some copy-paste tests for at least Docs, Word, Notion and Apple Notes. Apple Notes uses some annoyingly weird schema.

---

<div class="post-metadata">

**Author:** ![Saul-Mirone](https://discuss.prosemirror.net/user_avatar/discuss.prosemirror.net/saul-mirone/32/4160_2.png) [@Saul-Mirone](https://discuss.prosemirror.net/u/Saul-Mirone)\
**Post date:** [February 10, 2023, 8:58am UTC](https://discuss.prosemirror.net/t/prosemirror-flat-list-alpha/5191/5 "2023-02-10T08:58:54Z")

</div>

Really cool project. I think it would be better if you can let the users pass the `group` attribute in spec.

---

<div class="post-metadata">

**Author:** ![gethari](https://discuss.prosemirror.net/user_avatar/discuss.prosemirror.net/gethari/32/3168_2.png) [@gethari](https://discuss.prosemirror.net/u/gethari)\
**Post date:** [February 10, 2023, 9:20am UTC](https://discuss.prosemirror.net/t/prosemirror-flat-list-alpha/5191/6 "2023-02-10T09:20:31Z")

</div>

Can you please share the source for `list style` based on depth ? I assume you override the tiptap functionality ?

---

<div class="post-metadata">

**Author:** ![TeemuKoivisto](https://discuss.prosemirror.net/user_avatar/discuss.prosemirror.net/teemukoivisto/32/1185_2.png) [@TeemuKoivisto](https://discuss.prosemirror.net/u/TeemuKoivisto)\
**Post date:** [February 10, 2023, 10:39am UTC](https://discuss.prosemirror.net/t/prosemirror-flat-list-alpha/5191/7 "2023-02-10T10:39:53Z")

</div>

I didn’t use TipTap, it was just vanilla ProseMirror. Here’s ordered list

```auto
export const ordered_list: NodeSpec = {
  attrs: {
    indent: { default: 0 },
    start: { default: 1 },
  },
  content: 'list_item+',
  group: 'block',
  parseDOM: [
    {
      tag: 'ol',
      getAttrs: dom => {
        if (dom instanceof HTMLElement) {
          return {
            indent: parseInt(dom.getAttribute('data-indent') || '0'),
            start: parseInt(dom.getAttribute('start') || '1'),
          }
        }
        return null
      },
    },
  ],
  toDOM: (node: PMNode) => {
    const { indent, start } = node.attrs
    const listStyle =
      indent % 3 === 0 ? 'decimal' : indent % 3 === 1 ? 'lower-alpha' : 'lower-roman'
    const attrs = {
      'data-indent': indent,
      start,
      style: `list-style: ${listStyle};`,
    }
    return ['ol', attrs, 0]
  },
}

```

---

<div class="post-metadata">

**Author:** ![gethari](https://discuss.prosemirror.net/user_avatar/discuss.prosemirror.net/gethari/32/3168_2.png) [@gethari](https://discuss.prosemirror.net/u/gethari)\
**Post date:** [February 11, 2023, 6:06am UTC](https://discuss.prosemirror.net/t/prosemirror-flat-list-alpha/5191/8 "2023-02-11T06:06:47Z")

</div>

Thanks for sharing, how does the value of `data-indent` gets updated ? I tried to replicate the same indent extension from czi repo, but when I hit tab on a list item my cursor is automatically navigating to the next line.

---

<div class="post-metadata">

**Author:** ![TeemuKoivisto](https://discuss.prosemirror.net/user_avatar/discuss.prosemirror.net/teemukoivisto/32/1185_2.png) [@TeemuKoivisto](https://discuss.prosemirror.net/u/TeemuKoivisto)\
**Post date:** [February 11, 2023, 1:04pm UTC](https://discuss.prosemirror.net/t/prosemirror-flat-list-alpha/5191/9 "2023-02-11T13:04:12Z")

</div>

Np! As I said, there’s some insane logic to keep that synchronized so 😅 Basically you just check all the lists that were changed and then update their indents & start counters as necessary. I can make that PR to this repo incase @ocavue is interested.

---

<div class="post-metadata">

**Author:** ![gethari](https://discuss.prosemirror.net/user_avatar/discuss.prosemirror.net/gethari/32/3168_2.png) [@gethari](https://discuss.prosemirror.net/u/gethari)\
**Post date:** [February 12, 2023, 6:40am UTC](https://discuss.prosemirror.net/t/prosemirror-flat-list-alpha/5191/10 "2023-02-12T06:40:41Z")

</div>

If you could atleast share a simple repo or a gist with full code that would be really helpful, because in the @ocavue repo, when I inspected the list elements are not ul li at all. It seems to be a different logic all together.

---

<div class="post-metadata">

**Author:** ![ocavue](https://discuss.prosemirror.net/user_avatar/discuss.prosemirror.net/ocavue/32/2239_2.png) [@ocavue](https://discuss.prosemirror.net/u/ocavue)\
**Post date:** [February 13, 2023, 7:31am UTC](https://discuss.prosemirror.net/t/prosemirror-flat-list-alpha/5191/11 "2023-02-13T07:31:11Z")

</div>

Keyboard-only users is a good point! Thank you for pointing that.

We might still use `Tab` and `Shift-Tab` in our app, but it’s a good idea to set `Mod-[` and `Mod-]` as default keyboard shortcuts in the open-source library.

---

<div class="post-metadata">

**Author:** ![ocavue](https://discuss.prosemirror.net/user_avatar/discuss.prosemirror.net/ocavue/32/2239_2.png) [@ocavue](https://discuss.prosemirror.net/u/ocavue)\
**Post date:** [February 13, 2023, 7:48am UTC](https://discuss.prosemirror.net/t/prosemirror-flat-list-alpha/5191/12 "2023-02-13T07:48:54Z")

</div>

I prefer to implement this will CSS, maybe something like this:

```auto
ul, 
div.prosemirror-flat-list {
	list-style-type: square;
}

ul > ul,
div.prosemirror-flat-list > div.prosemirror-flat-list {
	list-style-type: circle;
}

```

You can use CSS to set difference marker styles for, let’s say, 6 indent depths , and just give a fixed marker for any indent depths greater or equal than 7. We don’t usually need a very depth list anyway.

Synchronizing indent depth with ProseMirror seems too complex and might hurt performance in my option.

---

<div class="post-metadata">

**Author:** ![ocavue](https://discuss.prosemirror.net/user_avatar/discuss.prosemirror.net/ocavue/32/2239_2.png) [@ocavue](https://discuss.prosemirror.net/u/ocavue)\
**Post date:** [February 13, 2023, 7:55am UTC](https://discuss.prosemirror.net/t/prosemirror-flat-list-alpha/5191/13 "2023-02-13T07:55:34Z")

</div>

> [@TeemuKoivisto](#):
>
> just changed `<li>` indent property when tabbed.

I’ve considered this approach too, but it doesn’t seems to match the ProseMirror’s tree data structure and might bring some unexpected complexity, so I didn’t choose in the end.

I am very glad that someone has implemented this method!

> You should also make some copy-paste tests for at least Docs, Word, Notion and Apple Notes. Apple Notes uses some annoyingly weird schema.

Good point. I will do these tests. Thank you!

---

<div class="post-metadata">

**Author:** ![ocavue](https://discuss.prosemirror.net/user_avatar/discuss.prosemirror.net/ocavue/32/2239_2.png) [@ocavue](https://discuss.prosemirror.net/u/ocavue)\
**Post date:** [February 13, 2023, 7:57am UTC](https://discuss.prosemirror.net/t/prosemirror-flat-list-alpha/5191/14 "2023-02-13T07:57:42Z")

</div>

> [@Saul-Mirone](#):
>
> it would be better if you can let the users pass the `group` attribute in spec.

I can add this, but I’m not sure in which case people would want it.

---

<div class="post-metadata">

**Author:** ![ocavue](https://discuss.prosemirror.net/user_avatar/discuss.prosemirror.net/ocavue/32/2239_2.png) [@ocavue](https://discuss.prosemirror.net/u/ocavue)\
**Post date:** [February 13, 2023, 8:06am UTC](https://discuss.prosemirror.net/t/prosemirror-flat-list-alpha/5191/15 "2023-02-13T08:06:30Z")

</div>

[Here](https://github.com/ocavue/prosemirror-flat-list/tree/af74ba83e32a8ad1b6d40e346054b078fc02d539/packages/remirror-playground) is the source code for the online deme. It’s based on Remirror (instead of pure ProseMirror), but you should able to get the idea. I might also add a pure ProseMirror demo afterward.

> the list elements are not ul li at all

This’s intended. By removing `<ul>` and replacing `<li>` with `<div>`, I “flat” the list structure and make certain things easier.

> It seems to be a different logic all together.

You are right, it’s a complete rewrite compare to `prosemirror-schema-list`. However I hope to provide a familiar feeling for end-users.

---

<div class="post-metadata">

**Author:** ![gethari](https://discuss.prosemirror.net/user_avatar/discuss.prosemirror.net/gethari/32/3168_2.png) [@gethari](https://discuss.prosemirror.net/u/gethari)\
**Post date:** [February 13, 2023, 8:54am UTC](https://discuss.prosemirror.net/t/prosemirror-flat-list-alpha/5191/16 "2023-02-13T08:54:28Z")

</div>

Thanks for the confirmation. But you mentioned that you will be implementing the `list-style` with CSS. But it cannot be matched to `word/google docs` implementation just with css right ? based on the indentation level `circle / dot / square` are alternating

## Word

 ![image](https://discuss.prosemirror.net/uploads/secondsite/original/2X/9/9f1a303c63b915f518f07edf92528ae0f5f7b087.png)

## Google Docs

 ![image](https://discuss.prosemirror.net/uploads/secondsite/original/2X/c/c773b1d839c7175329784f7d7061156bb8f05bc4.png)

---

<div class="post-metadata">

**Author:** ![ocavue](https://discuss.prosemirror.net/user_avatar/discuss.prosemirror.net/ocavue/32/2239_2.png) [@ocavue](https://discuss.prosemirror.net/u/ocavue)\
**Post date:** [February 13, 2023, 9:07am UTC](https://discuss.prosemirror.net/t/prosemirror-flat-list-alpha/5191/17 "2023-02-13T09:07:01Z")

</div>

> [@gethari](#):
>
> based on the indentation level `circle / dot / square` are alternating

You can write some not-so-long CSS to set markers separately for depths from 1 to 12, for example, set the marker as `circle` for depth 2, 5, 8 and 11. It’s a simple solution and good enough in 99.9% cases.

---

<div class="post-metadata">

**Author:** ![TeemuKoivisto](https://discuss.prosemirror.net/user_avatar/discuss.prosemirror.net/teemukoivisto/32/1185_2.png) [@TeemuKoivisto](https://discuss.prosemirror.net/u/TeemuKoivisto)\
**Post date:** [February 13, 2023, 11:34am UTC](https://discuss.prosemirror.net/t/prosemirror-flat-list-alpha/5191/18 "2023-02-13T11:34:19Z")

</div>

Okay! How are you going to sync the start counters for ordered lists though? I think you’ll have to come up with something similar either way. But I’ll make some example repo so its easier for those interested to understand the context better.

---

<div class="post-metadata">

**Author:** ![ocavue](https://discuss.prosemirror.net/user_avatar/discuss.prosemirror.net/ocavue/32/2239_2.png) [@ocavue](https://discuss.prosemirror.net/u/ocavue)\
**Post date:** [February 13, 2023, 1:42pm UTC](https://discuss.prosemirror.net/t/prosemirror-flat-list-alpha/5191/19 "2023-02-13T13:42:13Z")

</div>

> [@TeemuKoivisto](#):
>
> How are you going to sync the start counters for ordered lists though

I’m using CSS rule [`counter-reset`](https://developer.mozilla.org/en-US/docs/Web/CSS/counter-reset), `counter-increment` and `counter-set` for ordered lists, so I don’t need to use ProseMirror to manger the counter number.

> <https://github.com/ocavue/prosemirror-flat-list/blob/31105cecec1823ad00cfbdea6e9e8e0e6f96ddc3/packages/prosemirror-package/src/style.css#L32-L44>

---

<div class="post-metadata">

**Author:** ![TeemuKoivisto](https://discuss.prosemirror.net/user_avatar/discuss.prosemirror.net/teemukoivisto/32/1185_2.png) [@TeemuKoivisto](https://discuss.prosemirror.net/u/TeemuKoivisto)\
**Post date:** [February 15, 2023, 8:24am UTC](https://discuss.prosemirror.net/t/prosemirror-flat-list-alpha/5191/20 "2023-02-15T08:24:19Z")

</div>

Oh that’s pretty smart. I added a couple of issues I found by fiddling around. I’ll see when I get around cleaning up my experiment.

[Next page](https://discuss.prosemirror.net/t/prosemirror-flat-list-alpha/5191.md?page=2)
