Block structure editor by prosemirror

Hey all ! i have built my editor based on prosemirror, include calendar, drawio, table… seem really cooool.
Recently i experienced the notion all-in-one workspace, it turn out that the editor in notion origanize the content by block (which is not same as “block” in prosemirror), notion’s block is an independent contenteditable dom, which only has an independent node (table / calendar or other nodeType),it’s structure is as below:

I am wandering if separate all my schema into each mini-prosemirror editor and have a doc container that holds all the mini-prosemirror, can i achieve the block structure style new editor as notion.

Any suggestions on that or is it seems a feasible plan?
besides, doc contains too many prosemirror instance will affect performance severely ?

You can, but I don’t see why you’d want that. Separating your document into multiple editors breaks things like cross-block selections. This type of setup complicates user interaction, and I never really understood what advantage it brings.

in my opinion, notion block structure makes it flexible to save the data, cus doc can be described as two part:

  • block id list [block-id-1, bock-id-2, block-id3]
  • block content data {block-id-1: {nodeType: table, …rest} }, {block-id-2: {nodeType: calendar, …rest} }…

besides, each block has it's own menubar popover, so user don't need move mouse up to top menubar and down to editor frequently, make user experience better.
in my project, i want treat table as database to make table data accessable and sharable between different user's docs, maybe block style can be a little bit easier...also, draft version can separate into each block instead of all the doc itselft...

my editor now output htmlstring , i benefit it in doc diff and multiple user content merge from the serializeFragment API, however it make both the front and back end engineer struggle handing the doc content in some very difficult scene.
In fact, i have noticed that the toJSON API make it available to implicitly “block” the editor in data layer, may be worth a try !

None of these require splitting the editor into multiple separate editable elements, is what I’m saying.

ok, i get it . i am trying “block” the editor in data layer by toJSON api

Notion’s solution to this is quite nice: dragging a selection across two blocks creates a selection containing the whole of both blocks.

Presumably it allows for larger documents without scaling issues, as the editor only needs to run calculations for the block currently being edited.

I’m doing that with prosemirror, you just have to check what top level blocks was modified for each transaction and do your calculations on those nodes only.

Do you have some examples of that you’d be able to share? I keep trying to wrap my head around the positioning system and failing miserably.

I don’t know if this is perfect, but it seems to work for me:

function modifiedRanges(transactions) {
        let ranges = []
        for (let i = 0, l = transactions.length; i < l; i++) {
            let tr = transactions[i]
            if (tr.mapping) {
                tr.mapping.maps.forEach(m => {
                    ranges.forEach(r => {
                        r.start =
                        r.end =
                    if (m.ranges.length) {
                            new Range(m.ranges[0], m.ranges[0] + m.ranges[2])
        return ranges

and then check what doc children falls into these ranges

edit: Range is my own class but it’s just an object with start and end properties.

1 Like

you have to use applyTransaction instead of just apply so you get an array of all transactions.

hello @ zlv-thisF could you share your progress please I am interested in the topic, I would like to have for example: text, a special table, text, a graph, text, thank you very much

Hi, what would be the best way to do something like notion or coda with prosemirror, or how does the Attlasian kit solve that problem?

It depends on what you want from you selection and the way your users want to interact with the editor. If the main goal of the editor being built is text editing then Notion solution is atrocious (we use Notion heavily, I’m not trying to say that the product is bad). You can select text in different blocks as you can only select full blocks. This is not really the best way to work on texts.

That said I think it really depends on the end goal. Notion has its purpose and it works well (I still find myself trying to select one sentence in one block and drag the selection to another block just to be reminded of the limitation of the block structure). But for “text editing” experience the Notion style block structure is far from optimal.

Notion style block editor can be done using Prosemirror without multiple editors as you can hook into hover events and get all the information you need using posAtCoords.

not much progress in fact, i try independent menubar on each block by the view , state, apply methods of plugin, seems work.

i am trying to refactor the code inspired by remirror and altalskit-mk-2 and have gived up block the doc into mini-prosemirror editor ( block the serialized data seems good enough )

what struggles our team now is not notion style at all, the main challenge is that we are all not that experienced in prosemirror, have not that much control on all pm’ powerful API and our editor’s code designed …sad

The editor for Atlassian is open source if you’d want to check that out.

There’s some ProseMirror API for positioning: posAtCoords, coordsAtPos, domAtPos. I saw that being used to create a drag handle plugin when block nodes were being hovered upon.