BlockNote
BlockNote copied to clipboard
Can't call `blocksToHTMLLossy` on document with React custom blocks without instantiating a BlockNoteView
Describe the bug <what's going wrong!?>
I want to render a document of blocks to HTML, "headlessly" ie. without showing the editor. But when some of my custom blocks are React-rendered (defined via createReactBlockSpec
), an error is thrown when exporting to HTML without having instantiated a BlockNoteView
.
blocksToHTMLLossy()
, somewhere down the stack, depends on contentComponent
being set on the inner editor._tiptapEditor
(here), which is done by a useEffect
in EditorContent
that mutates the editor passed in , and EditorContent
doesn't come into play without having a BlockNoteView
.
I think this specific state of affairs was introduced in https://github.com/TypeCellOS/BlockNote/pull/641, but I haven't checked whether this would have worked before that.
For now, I'm just rendering a BlockNoteView on a throwaway React root, in order to get the mutation from EditorContent applied, which is fine, but a bit clunky.
I appreciate this is probably not a common use case 🙃
To Reproduce
const toHTML = async (blocks, schema=null) => BlockNoteEditor.create({
initialContent: blocks,
...(schema ? { schema } : {})
}).blocksToHTMLLossy();
// Default schema block works fine:
console.log(await toHTML([{ type: 'paragraph', content: 'heya' }]));
// React-based custom element doesn't work...
const somethingBlock = createReactBlockSpec(
{ type: 'something', content: 'none', propSchema: {} },
{ render() { return <i>A constant something</i> } }
)
const customSchema = BlockNoteSchema.create({ blockSpecs: { something: somethingBlock } })
// ...Throws error:
console.log(await toHTML([{type: 'something'}], customSchema))
// But this works:
const editor = BlockNoteEditor.create({ initialContent: [{type: 'something'}], schema: customSchema });
await new Promise((resolve, reject) => {
const tmpRoot = ReactDOMClient.createRoot(document.createElement('div'));
tmpRoot.render(React.createElement(() => {
useEffect(() => { resolve(); }, []);
return React.createElement(BlockNoteView, { editor });
}));
});
console.log(await editor.blocksToHTMLLossy());
The error thrown is:
Uncaught (in promise) TypeError: t._tiptapEditor.contentComponent is undefined
Ce ReactRenderUtil.ts:13
toExternalHTML ReactBlockSpec.tsx:213
Kt sharedHTMLConversion.ts:70
serializeNodeInner externalHTMLExporter.ts:66
serializeFragment index.js:3273
forEach index.js:250
serializeFragment index.js:3247
Kt sharedHTMLConversion.ts:109
serializeNodeInner externalHTMLExporter.ts:66
serializeFragment index.js:3273
forEach index.js:250
serializeFragment index.js:3247
Jt sharedHTMLConversion.ts:124
exportProseMirrorFragment externalHTMLExporter.ts:79
exportBlocks externalHTMLExporter.ts:90
blocksToHTMLLossy BlockNoteEditor.ts:907
<clear steps to reproduce are super helpful! Best is to provide an online sandbox, click to create one>
Misc
- Node version:
- Package manager:
- Browser:
- [ ] I'm a sponsor and would appreciate if you could look into this sooner than later 💖