From 71cdb89795f28fa2321fd280f700d01fc35fb9fa Mon Sep 17 00:00:00 2001 From: yousefed Date: Wed, 21 Jan 2026 07:38:52 +0100 Subject: [PATCH 1/2] fix: add contaxt,nestingLevel to toExternalHTML --- .../html/util/serializeBlocksExternalHTML.ts | 3 +++ packages/core/src/schema/blocks/createSpec.ts | 6 ++++-- packages/core/src/schema/blocks/types.ts | 9 +++++++++ packages/react/src/schema/ReactBlockSpec.tsx | 13 ++++++++++--- 4 files changed, 26 insertions(+), 5 deletions(-) diff --git a/packages/core/src/api/exporters/html/util/serializeBlocksExternalHTML.ts b/packages/core/src/api/exporters/html/util/serializeBlocksExternalHTML.ts index c458b292a2..fb993f8309 100644 --- a/packages/core/src/api/exporters/html/util/serializeBlocksExternalHTML.ts +++ b/packages/core/src/api/exporters/html/util/serializeBlocksExternalHTML.ts @@ -207,6 +207,9 @@ function serializeBlock< {}, { ...block, props } as any, editor as any, + { + nestingLevel, + }, ) || blockImplementation.render.call( {}, diff --git a/packages/core/src/schema/blocks/createSpec.ts b/packages/core/src/schema/blocks/createSpec.ts index 1c9fa38f9a..26e447b13c 100644 --- a/packages/core/src/schema/blocks/createSpec.ts +++ b/packages/core/src/schema/blocks/createSpec.ts @@ -242,7 +242,7 @@ export function addNodeAndExtensionsToSpec< }, // TODO: this should not have wrapInBlockStructure and generally be a lot simpler // post-processing in externalHTMLExporter should not be necessary - toExternalHTML: (block, editor) => { + toExternalHTML: (block, editor, context) => { const blockContentDOMAttributes = node.options.domAttributes?.blockContent || {}; @@ -251,6 +251,7 @@ export function addNodeAndExtensionsToSpec< { blockContentDOMAttributes }, block as any, editor as any, + context, ) ?? blockImplementation.render.call( { blockContentDOMAttributes, renderType: "dom", props: undefined }, @@ -393,11 +394,12 @@ export function createBlockSpec< ...blockImplementation, // TODO: this should not have wrapInBlockStructure and generally be a lot simpler // post-processing in externalHTMLExporter should not be necessary - toExternalHTML(block, editor) { + toExternalHTML(block, editor, context) { const output = blockImplementation.toExternalHTML?.call( { blockContentDOMAttributes: this.blockContentDOMAttributes }, block as any, editor as any, + context, ); if (output === undefined) { diff --git a/packages/core/src/schema/blocks/types.ts b/packages/core/src/schema/blocks/types.ts index 11d62f0c24..06270c6d82 100644 --- a/packages/core/src/schema/blocks/types.ts +++ b/packages/core/src/schema/blocks/types.ts @@ -139,6 +139,9 @@ export type LooseBlockSpec< toExternalHTML?: ( block: any, editor: BlockNoteEditor, + context: { + nestingLevel: number; + }, ) => | { dom: HTMLElement | DocumentFragment; @@ -193,6 +196,9 @@ export type BlockSpecs = { toExternalHTML?: ( block: any, editor: BlockNoteEditor, + context: { + nestingLevel: number; + }, ) => | { dom: HTMLElement | DocumentFragment; @@ -463,6 +469,9 @@ export type BlockImplementation< editor: BlockNoteEditor< Record> >, + context: { + nestingLevel: number; + }, ) => | { dom: HTMLElement | DocumentFragment; diff --git a/packages/react/src/schema/ReactBlockSpec.tsx b/packages/react/src/schema/ReactBlockSpec.tsx index 1a341cc341..184f9e2e5c 100644 --- a/packages/react/src/schema/ReactBlockSpec.tsx +++ b/packages/react/src/schema/ReactBlockSpec.tsx @@ -3,10 +3,10 @@ import { BlockImplementation, BlockNoDefaults, BlockNoteEditor, - Extension, BlockSpec, camelToDataKebab, CustomBlockImplementation, + Extension, getBlockFromPos, mergeCSSClasses, Props, @@ -51,7 +51,13 @@ export type ReactCustomBlockImplementation< "render" | "toExternalHTML" > & { render: FC>; - toExternalHTML?: FC>; + toExternalHTML?: FC< + ReactCustomBlockRenderProps & { + context: { + nestingLevel: number; + }; + } + >; }; export type ReactCustomBlockSpec< @@ -224,7 +230,7 @@ export function createReactBlockSpec< config: blockConfig, implementation: { ...blockImplementation, - toExternalHTML(block, editor) { + toExternalHTML(block, editor, context) { const BlockContent = blockImplementation.toExternalHTML || blockImplementation.render; const output = renderToDOMSpec((refCB) => { @@ -247,6 +253,7 @@ export function createReactBlockSpec< ); } }} + context={context} /> ); From 0df09432f8655910cf25f9190999c3ddb6f1d921 Mon Sep 17 00:00:00 2001 From: Matthew Lipski Date: Tue, 27 Jan 2026 18:26:49 +0100 Subject: [PATCH 2/2] Added docs --- docs/content/docs/features/custom-schemas/custom-blocks.mdx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/content/docs/features/custom-schemas/custom-blocks.mdx b/docs/content/docs/features/custom-schemas/custom-blocks.mdx index 6bee335c2e..ce5514fd23 100644 --- a/docs/content/docs/features/custom-schemas/custom-blocks.mdx +++ b/docs/content/docs/features/custom-schemas/custom-blocks.mdx @@ -122,6 +122,7 @@ type ReactCustomBlockImplementation = { block: Block; editor: BlockNoteEditor; contentRef?: (node: HTMLElement | null) => void; + contest: { nestingLevel: number }; }>; parse?: (element: HTMLElement) => PartialBlock["props"] | undefined; runsBefore?: string[]; @@ -144,7 +145,9 @@ type ReactCustomBlockImplementation = { - `contentRef:` A React `ref` you can use to mark which element in your block is editable, this is only available if your block config contains `content: "inline"`. -`toExternalHTML?:` This component is used whenever the block is being exported to HTML for use outside BlockNote, for example when copying it to the clipboard. If it's not defined, BlockNote will just use `render` for the HTML conversion. Takes the same props as `render`. +`toExternalHTML?:` This component is used whenever the block is being exported to HTML for use outside BlockNote, for example when copying it to the clipboard. If it's not defined, BlockNote will just use `render` for the HTML conversion. Takes the same props as `render` and an additional `context` prop, which is. an object with the following attributes: + +- `nestingLevel`: How deep the block being exported in nested inside other blocks. 0 means it's at the top level of the document. _Note that your component passed to `toExternalHTML` is rendered and