Source with Preview Blocks
Some blocks are authored as source code but are more useful shown as the thing that code produces — a LaTeX formula rendered as a formula, or Mermaid source rendered as a diagram. Unlike a code block, these blocks show the rendered preview in place, while the source is edited in a popup.
The components on this page are only available in React
(@blocknote/react).
BlockNote's math and diagram blocks are built on this pattern, and the same building blocks are available to create your own:
SourceBlockWithPreview(from@blocknote/react) — for custom blocks.SourceInlineContentWithPreview(from@blocknote/react) — for custom inline content.
Both render the preview you give them in place of the block/inline content, and manage the editable source popup for you. The popup behavior itself is driven by editor-wide extensions that BlockNote registers by default — a spec opts in simply by setting hasPreview: true in its meta.
Custom Block
Three pieces make a source-with-preview block — the example below implements them all:
1. A block config with "plain" content — the source is stored as the block's plain text content:
const createMyBlockConfig = createBlockConfig(
() =>
({
type: "myBlock" as const,
propSchema: {},
content: "plain" as const,
}) as const,
);2. A render component that reads the source, renders it however you like, and hands the result to SourceBlockWithPreview:
// The block's content as plain text, i.e. the source to render.
const source = plainContentToString(props.block.content).trim();
// Your own rendering, returning a preview element or an error for invalid
// source - the example below renders CSV to a table.
const { preview, error } = renderMySource(source);
return (
<SourceBlockWithPreview
block={props.block}
editor={props.editor}
contentRef={props.contentRef}
source={source}
// The last successfully rendered preview, or `undefined` - an errored
// source then shows the error state instead of an empty preview.
preview={preview}
// Shown below the source in the popup while editing.
error={error}
/>
);A few more props customize the states: errorPreview for the compact error state shown in place of the preview, emptySourcePlaceholder for when the source is empty (a string customizes the default placeholder's text, an element — e.g. the exported PreviewPlaceholder with your own icon — replaces it entirely), and sourcePlaceholder for the popup input's placeholder. See the SourceWithPreviewProps type for the full list.
3. The spec's meta, opting into the popup:
const createMyBlockSpec = createReactBlockSpec(createMyBlockConfig, {
meta: {
code: true,
// Marks the block as rendering a preview with an editable source popup.
hasPreview: true,
// What Enter does while the popup is open: "enter" inserts a newline
// (multiline sources, like diagrams), "shift+enter" closes the popup
// (single-line sources, like math).
hardBreakShortcut: "enter",
},
render: MyBlockPreview,
});Because the block uses "plain" content, you can also syntax-highlight the source in the popup (as the math and diagram blocks do): add a highlight callback to the meta that returns the source language, then add the syntax highlighting extension to your editor.
Custom Inline Content
Inline content works the same way, with two differences: the component takes the inline-content render props (node, getPos), and for "plain" inline content the source is already a plain string:
<SourceInlineContentWithPreview
editor={props.editor}
node={props.node}
getPos={props.getPos}
contentRef={props.contentRef}
source={props.inlineContent.content.trim()}
preview={preview}
error={error}
/>The spec is created with createReactInlineContentSpec, opting in via meta: { code: true, hasPreview: true }. Unlike blocks — which toggle the popup on click — inline content opens its popup exactly while the selection is inside its source, so it's always shown when selected.
Example
A complete implementation of both — a CSV table block and a color chip inline content:
The @blocknote/math-block and @blocknote/diagram-block packages are production implementations of the same pattern.