Skip to content

Atomic server‑side insert/move/delete API for BlockNote (Y.XmlFragment.insert without array round‑trip) #1874

@matteotarantino-algor

Description

@matteotarantino-algor

Is your feature request related to a problem? Please describe.
I’m building a server‑side document processor that manipulates BlockNote documents entirely in a Node runtime (no browser / DOM).
Today, to add, move or delete a block I have to

1. Read the whole "blocknote" Y.XmlFragment
2. Convert it to an array      (yXmlFragmentToBlocks)
3. Splice / modify that array
4. Re‑serialize the array      (blocksToYXmlFragment)
  • This round‑trip is expensive on large documents (extra parsing, allocations, GC).
  • While step 1→4 is running, a remote update can arrive, so my snapshot may already be stale → potential race conditions and lost updates.

Describe the solution you'd like
A first‑class server API (or official recipe) that lets me insert / move / delete blocks atomically and in O(1), without converting the whole fragment:

const fragment = doc.getXmlFragment("blocknote");       // the CRDT list of blocks
const tools = doc.getMap("tools");
const newBlock  = { id, type: "custom-tool", props: {refId: id} };

doc.transact(() => {
  serverEditor.insertBlock(fragment, newBlock, {          // <— wanted API
    refBlockId,                                           //  target block
    placement: "after"                                    //  before|after
  });

  // ← in the same transaction I can also update my custom Y.Map "tools"
  tools.set(newBlock.id, buildCustomToolEntry(...));
});

Key points I’d love to see:

  • Works for all block types (standard and custom)
  • Accepts an optional { refBlockId, placement } to position relative to an existing block
  • Updates can be batched in a single doc.transact, so no race with concurrent edits

Describe alternatives you've considered

  • Manual implementation: create a Y.XmlElement, locate the reference node, fragment.insert it, then remember to update tools.
    For sure it is a fragile serializer (and it doesn't work rn) and must keep up with BlockNote schema changes.

  • Stay with the array round‑trip (read-blocks&push-new-block in a single transact): fine for tiny docs, but inefficient and risky.

Additional context
Algor Education wants to build an AI‑powered block‑based editor (“Algor Card”) built on BlockNote + Yjs that revolutionize the learning process in the EdTech sector.
Our backend routinely generates blocks, both custom (mindmap, quiz, ...) & standard (paragraph, heading, ...) and must guarantee document integrity under heavy real‑time collaboration.

For every custom block (flashcards, mind‑map, summary, quiz…) we:

  • Insert a visual BlockNote node inside the blocknote fragment (with an attribute refId=blockId).
  • Store the rich data for that custom block in the shared tools Y.Map under the same blockId, this enable us to build a "custom editor" on top of Y.js for each tool (e.g. "mindmap tool" requires a custom editor to manipulate graph).

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions