How’s this for cool.
It’s possible to embed Gutenberg into any backend plugin page, with a custom save callback. You can get access to the rendered Gutenberg HTML or raw blocks array to do fun things with.

This is the minimal amount of code to render a functional Gutenberg Block Editor in the backend:
<div className="embed-gutenberg edit-post-visual-editor">
<SlotFillProvider>
<DropZoneProvider>
<BlockEditorProvider
value={blocks}
onInput={onChange}
onChange={onChange}
className="embed-gutenberg__wrapper"
>
<div className="embed-gutenberg__editor">
<BlockEditorKeyboardShortcuts/>
<WritingFlow>
<ObserveTyping>
<BlockList/>
</ObserveTyping>
</WritingFlow>
</div>
<div className="embed-gutenberg__sidebar">
<BlockInspector/>
</div>
<Popover.Slot/>
</BlockEditorProvider>
</DropZoneProvider>
</SlotFillProvider>
</div>
The onChange
callback can serialize the full html change from the Gutenberg block array using serialize()
like this:
let fullHtml = ''
blocks.forEach(block => {
const blockHtml = serialize(block);
fullHtml = fullHtml + "\n" + blockHtml;
})
And a default block array for Gutenberg might look something like this:
const defaultBlockArray = [{
"clientId": "812f33be-af57-475e-9987-af1936fee811",
"name": "core/paragraph",
"isValid": true,
"attributes": {"content": "This is the first Paragraph block", "dropCap": false},
"innerBlocks": []
}, {
"clientId": "1e4e538e-5b75-4c93-894f-9bad6aaea746",
"name": "core/heading",
"isValid": true,
"attributes": {"content": "This is a header block", "level": 2},
"innerBlocks": []
} ]