Exporting documents
Three functions turn a live store into a plain string — JSON, HTML, or plain text — plus a drop-in button + modal (DocumentExportButton) that wraps all three behind one "View source" UI, so a host app doesn't have to hand-roll the export/modal/copy plumbing itself.
DocumentExportButton were added in 0.1.2; the simpler exportDocumentSimpleJSON/importDocumentSimpleJSON pair further down landed in a later 0.1.x release.Drop-in button
Mount it once anywhere under <NoteloomEditor>, as children:
import { useEditor, NoteloomEditor, DocumentExportButton } from 'noteloom';
function Editor() {
const editor = useEditor();
return (
<NoteloomEditor editor={editor}>
<DocumentExportButton label="View source" />
</NoteloomEditor>
);
}Clicking it opens a modal with JSON/Simple JSON/HTML/Text tabs and a copy button. The export is recomputed fresh every time the modal opens (not on every keystroke while it's closed) — cheap for real documents, and guarantees what's shown always matches the live store exactly, with no separate cache to keep in sync.
You can see this in action on this site itself — both Playground and My Notes mount it in their editor toolbar, labeled "View source".
The three export functions
| Function | Signature | Notes |
|---|---|---|
exportDocumentJSON | (store, { pretty = true } = {}) | Returns { version, rootId, blocks, runs } as a JSON string — the same shape new EditorStore({...}) itself accepts, so it round-trips: export, then hand the parsed result straight back into a fresh store to reconstruct the document exactly. |
exportDocumentHTML | (store, registry, inlineRegistry) | Every top-level block's own toHTML, joined — the same serialization path clipboard copy uses. |
exportDocumentText | (store, registry, inlineRegistry) | Every top-level block's own toPlainText, joined. |
Call these directly if you want the raw strings without any UI — e.g. to save a file, send to an API, or build your own export flow:
import { exportDocumentJSON, exportDocumentHTML, exportDocumentText } from 'noteloom';
const json = exportDocumentJSON(store); // string, pretty-printed by default
const html = exportDocumentHTML(store, registry, inlineRegistry); // string
const text = exportDocumentText(store, registry, inlineRegistry); // string
// exportDocumentJSON's shape round-trips straight back into a fresh store:
const restored = new EditorStore(JSON.parse(json));A simpler JSON shape for storage/API/CRUD use
exportDocumentJSON above returns the internal engine format — the same normalized, id-referenced graph EditorStore operates on (blocks reference other blocks by id; text lives in a separate runs collection, not embedded inline). That shape is what makes per-run reactivity, O(1) structural edits, and real nesting (toggle lists, tables, inline atomic chips) work — on purpose, it's not going to look like a simple flat document.
If you just want something simpler to store, send over an API, or hand-edit — self-contained blocks in an array, children for nesting, no id-references to resolve — use this second, optional export/import pair instead:
import { exportDocumentSimpleJSON, importDocumentSimpleJSON } from 'noteloom';
const json = exportDocumentSimpleJSON(store, registry, inlineRegistry);
// {
// "version": 1,
// "blocks": [
// { "id": "p1", "type": "paragraph", "data": { "text": "Hello <strong>world</strong>" } },
// { "id": "h1", "type": "heading", "data": { "text": "Key features", "level": 3 } },
// {
// "id": "li1", "type": "listItem",
// "data": { "text": "Nested item", "ordered": false, "checked": null },
// "children": [ /* nested listItem blocks, same shape */ ]
// },
// {
// "id": "t1", "type": "table",
// "data": { "columns": [{ "id": "c1", "label": "Name" }], "rows": [["Cell text"]] }
// }
// ]
// }
// ...later, or on a different machine/process:
const doc = importDocumentSimpleJSON(json, registry, inlineRegistry); // -> { rootId, blocks, runs }
const store2 = new EditorStore(doc);Rich text (data.text) is an HTML string — the exact same per-run serialization every block type's own clipboard-copy toHTML already produces, so marks (bold/italic/underline/strike/code/sub/superscript/color/highlight/link) and atomic inline chips (checkbox/date/select/mention) round-trip through it the same way copy/paste already does. Table is flattened specially (data.columns + data.rows, a 2D array) rather than exposing the internal table/row/cell block chain — the single biggest simplification versus the internal shape. Block/run ids are preserved on both export and import, useful for referencing or updating a specific block from an external system.
options list does not — only the currently-selected option survives, the same as pasting one of these chips into another instance of the editor today.This is a purely additive, alternate interchange format — the internal engine format above is unaffected either way, and this is not a replacement for it.