Automatic joining of lists, blockquotes, and similar

Many WYSIWYG editors will automatically join adjacent lists or other kinds of nodes on some operations. They, for the most part, seem to allow adjacent but separate lists, but many actions, such as creating or pasting a list item, will join the list to the one above (but not below, in Google Docs).

This has the advantage that it is usually what the user meant – actual adjacent lists are a rare thing (but do come up). It has the disadvantage of being somewhat magical.

I initially thought that having a ‘join nodes’ control (see the joinUp/joinDown commands) would be a better, more predictable interface to this. And to a certain extent, I still like this approach — it gives control over whether you want to have your lists melt together or not, is general (also useful for joining list items or sections), and allows you to explicitly join nodes after the fact, which, in Google Docs, I could not find a way to do except removing and re-adding list style to the whole list.

But this approach is not what people are used to, and I expect it’ll be a bit abstract and unexpected for many users.

So I want to ask the community about expectations and mental models that you have. Do you like automatic joining of lists? Would you prefer it to happen always, or only in response to certain user actions? Which of these actions should join?

  • Creating a new list above, below, or between existing lists
  • Pasting a list / list item above, below, or between
  • Typing 3. below a two-item numbered list
  • Typing 1. below a numbered list
  • Deleting across two lists
  • Deleting a paragraph that sat between lists
  • Lifting a list out of a parent node so that it appears above/below another list
  • Joining two parent nodes so that the newly joined node now has two adjacent lists

Any ideas? Other relevant considerations?



Yes, I prefer automatic joining of lists. I don’t like the fact that in Google doc I often have to merge 2 items then break them up again to ensure they’re in the same list.

I can’t think of a case of adjacent lists that are not separated at least by a blank paragraph, so for me if it’s exactly adjacent it should be merged.

Basically I think merging should happen most of the time, the user can then break the lists by hitting delete when in an empty item. You end up with an empty paragraph between the 2 lists.

Breaking the lists is useful to undo a merge happen that you don’t want, for example you delete a paragraph between 2 lists but want to type a new one between them. Also when you paste a list right after an other one, but want to type text in between.

Hi Marijn, i created a WYSIWYG editor once. Althou i am not as proud of it as i would had i done it opensource and without following the rules given to me by the company i worked for. i still feel very strongly about the issue you are raising.


Autojoining lists can be incredibly annoying when you want to do some out of the ordinary formatting / writing in your document. also when you just want to groom your document copying/pasting and iserting newlines you may trigger the occasional auto join in most standard editors.


i totally get the desire of the mainstream editor user for the automatic help and the potential time savings.

i do not have a proposal for a complete solution but i would like to make some suggestions

at least allow the option to make magical editor behavior of any kind invoke the open of user interference. for autojoining that could be a highlighting of the to be joined text in combination with a hotkey to execute the joining. i would love it if you could configure if the editor takes one further step and do the joining automatically and then allow opting out via hotkey.

a integration of the editor could present that option in an advanced menu.

keep up the good work best regards sol

Agreed! Easier to introduce a newline and break up the merged lists than it is for a user to understand joinup/joindown.

Side note: I’ve already implemented most of these auto joins as I felt they were needed for proper list behavior.

I see it the same way as @wes-r and @erwanl. Something I learned from our user tests is that it’s really hard for users to understand abstract commands like joinup and joindown.

I’d be interested to see what your implementation looks like. What triggers it, and how does it decide what to join?

@marijn I believe my current triggers are:

  • deleting empty line
  • backspace (joinBackward)
  • deleting forward (joinForward)
  • wrapping in a list item

This gets into another example of why I want transform helpers. I’ve setup my list commands to do the normal operations of the above triggers PLUS they call transform helper versions of joinUp and joinDown. That way all operations are handled within a single transform.

@wes-r Please take a look at this commit, which adds a generic wrapper to the prosemirror-commands package that can wrap another command to make it auto-join certain nodes. It demonstrates the onAction chaining I referred to in the other discussion.

Hmmm, I think I like the autoJoin helper but I’m not sure about the extendTransformAction pattern. At first I was thinking this is great as the existing commands can be left unchanged. With extendTransformAction I still can’t chain multiple existing commands. I can wrap them and extend them but I can’t utilize multiple ones since they take state and not transform.

I’m still thinking there should be a big library of transform manipulations and commands would be using some combination of these.

There is, it’s called the Transform object. Which commands did you want to apply together?

So far I’ve copied the following commands and turned them into transform helpers because I needed their operations in conjunction with one or more other transform operations:

  • wrapIn
  • joinUp
  • joinDown
  • lift
  • wrapInList
  • joinBackward
  • joinForward
  • splitBlock
1 Like