name: blocknote description: Work with BlockNote, a block-based rich text editor for React. Use when the user mentions "blocknote", "BlockNote editor", "block-based editor", "insertBlocks", "updateBlock", "removeBlocks", "replaceBlocks", "editor.document", "InlineContent", "StyledText", "nestBlock", "unnestBlock", "addStyles", "toggleStyles", "removeStyles", "createLink", "getTextCursorPosition", "setTextCursorPosition", "getSelection", "setSelection", "forEachBlock", "useCreateBlockNote", "BlockNoteView", "paragraph block", "heading block", "bulletListItem", "PartialBlock", or asks how to manipulate blocks, style text, insert content, or read document structure in BlockNote. metadata: author: Martin Clasen version: 1.0.0 category: rich-text-editor tags: [blocknote, editor, react, blocks, rich-text, inline-content]
BlockNote
Skill for working with BlockNote, a block-based rich text editor for React.
Core Mental Model
Everything in a BlockNote document is a Block. A paragraph is a block, a heading is a block, a list item is a block. Blocks can nest (children), and each block holds its rich text as an array of InlineContent objects.
Document
├── Block (paragraph) ← top-level block
│ └── InlineContent[] ← rich text inside
├── Block (heading)
│ └── InlineContent[]
└── Block (bulletListItem)
├── InlineContent[]
└── Block (nested child) ← indented sub-item
└── InlineContent[]
Block Type
type Block = {
id: string;
type: string; // "paragraph" | "heading" | "bulletListItem" | ...
props: Record<string, boolean | number | string>; // type-specific properties
content: InlineContent[] | TableContent | undefined; // rich text (undefined for images, columns)
children: Block[]; // nested child blocks
};
When creating or updating blocks, use PartialBlock — all fields are optional:
editor.insertBlocks([{ type: "paragraph", content: "Hello!" }], refBlock);
editor.updateBlock(blockId, { props: { level: 2 } });
InlineContent Types
type StyledText = { type: "text"; text: string; styles: Styles };
type Link = { type: "link"; content: StyledText[]; href: string };
type InlineContent = StyledText | Link | CustomInlineContent;
Styles is a key/value map of active style attributes (e.g. { bold: true, textColor: "red" }).
See references/document-structure.md for full type definitions including TableContent and column blocks.
Quick API Reference
Reading
const all = editor.document; // all top-level blocks
const block = editor.getBlock(id); // by id or Block ref
const prev = editor.getPrevBlock(id);
const next = editor.getNextBlock(id);
const parent = editor.getParentBlock(id);
editor.forEachBlock((block) => {
console.log(block.id, block.type);
return true; // false stops traversal
});
Inserting
// Insert before (default) or after a reference block
editor.insertBlocks(
[{ type: "heading", content: "Title", props: { level: 2 } }],
referenceBlock,
"after",
);
Updating
editor.updateBlock(blockId, { type: "heading", props: { level: 1 } });
editor.updateBlock(blockId, { content: "New text" });
Removing & Replacing
editor.removeBlocks([id1, id2]);
editor.replaceBlocks([oldId], [{ type: "paragraph", content: "Replacement" }]);
Moving & Nesting
editor.moveBlocksUp();
editor.moveBlocksDown();
if (editor.canNestBlock()) editor.nestBlock();
if (editor.canUnnestBlock()) editor.unnestBlock();
Inline Content at Cursor
editor.insertInlineContent("plain text");
editor.insertInlineContent([
{ type: "text", text: "Bold", styles: { bold: true } },
{ type: "link", content: "BlockNote", href: "https://blocknotejs.org" },
]);
Styles on Selection
editor.addStyles({ bold: true, textColor: "red" });
editor.removeStyles({ bold: true });
editor.toggleStyles({ italic: true });
const active = editor.getActiveStyles(); // styles at cursor / selection end
const text = editor.getSelectedText();
Links
editor.createLink("https://example.com"); // wraps selected text
editor.createLink("https://example.com", "Custom label");
const url = editor.getSelectedLinkUrl();
Cursor & Selection
const pos = editor.getTextCursorPosition(); // { block, prevBlock, nextBlock, ... }
editor.setTextCursorPosition(blockId, "start"); // "start" | "end"
const sel = editor.getSelection();
editor.setSelection(startBlockId, endBlockId);
See references/block-api.md for the complete API reference with all parameters and examples.
Common Patterns
Insert block after the current cursor position
const { block } = editor.getTextCursorPosition();
editor.insertBlocks(
[{ type: "paragraph", content: "Inserted after cursor" }],
block,
"after",
);
Convert the current block to a heading
const { block } = editor.getTextCursorPosition();
editor.updateBlock(block, { type: "heading", props: { level: 2 } });
Build a section programmatically
const anchor = editor.document[0];
editor.insertBlocks(
[
{ type: "heading", content: "Section Title", props: { level: 2 } },
{ type: "paragraph", content: "Intro paragraph." },
{ type: "bulletListItem", content: "Item 1" },
{ type: "bulletListItem", content: "Item 2" },
],
anchor,
"after",
);
Apply bold to selected text
editor.addStyles({ bold: true });
Collect all blocks of a given type
const headings: Block[] = [];
editor.forEachBlock((block) => {
if (block.type === "heading") headings.push(block);
return true;
});
Replace a block with a different type, preserving content
const block = editor.getBlock(blockId)!;
editor.replaceBlocks([block], [{ type: "heading", content: block.content, props: { level: 1 } }]);
Key Gotchas
-
contentcan beundefined: Image blocks, column blocks, andcolumnListblocks have no rich-text content — theircontentfield isundefined. Always check before accessing. -
childrenvscontent: Nested sub-blocks live inchildren, notcontent.contentis only the inline rich text of the block itself. -
editor.documentis a snapshot: It returns the document state at the time of the call. Subscribe toonChangeto keep a reactive copy. -
PartialBlockfor writes: When inserting or updating you never need to supply all fields. Omit anything you don't want to change. -
forEachBlockis depth-first: It traverses the entire tree including nested children, not just top-level blocks. -
Column restrictions:
columnListchildren must becolumnblocks;columnchildren must be regular blocks; acolumnListneeds at least 2 columns. -
moveBlocksUp/Downacts on the selection: Position the cursor or set a selection before calling these.
chat Comments (0)
Sign in to join the discussion and leave a comment.
Skill Details
Related Skills
Build your own?
Join 12,000+ developers contributing to the Claude ecosystem.
No comments yet. Be the first to share your thoughts!