DocMosaicdocs
Primitives

Editor.Root

The orchestrator. Owns the document state, the history timeline, and the DnD provider. Every other primitive reads from its context.

Editor.Root is the entry point. It owns the document state (controlled or uncontrolled), mounts a single react-dnd MultiBackend (HTML5 on desktop, auto-transitioning to touch) for the whole tree, and provides the editor + config contexts every other Editor.* primitive consumes.

Installation

bun add @docmosaic/react @docmosaic/core

Usage

import { Editor } from '@docmosaic/react';
import '@docmosaic/react/styles.css';

export function MyEditor() {
    return (
        <Editor.Root>
            <Editor.Properties />
            <Editor.Toolbar />
            <Editor.Pages />
            <Editor.Canvas>
                <Editor.Section />
            </Editor.Canvas>
            <Editor.Preview />
        </Editor.Root>
    );
}

Composition

Editor.Root arranges its children into the default shell automatically - Pages is forced to the left of Canvas regardless of source order. Drop any primitive in the child tree; it'll find its place.

Don't nest another <DndProvider> underneath - Editor.Root already mounts one.

Examples

Controlled

Lift the document state out so you can persist it or sync it across collaborators.

const [doc, setDoc] = useState<Document>(() => createDocument());

<Editor.Root document={doc} onDocumentChange={setDoc}>
    {/* ... */}
</Editor.Root>;

Read-only

Flip the editor into viewer mode. Mutating interactions are suppressed; preview / print / download stay live.

<Editor.Root defaultDocument={signedContract} readOnly>
    <Editor.Properties />
    <Editor.Toolbar />
    <Editor.Canvas />
    <Editor.Preview />
</Editor.Root>

Custom PDF backend

Swap the bundled jspdf pipeline for your own renderer (or run it in a Worker).

<Editor.Root
    pdf={{
        generate: myCustomRenderer,
        estimate: mySizeHeuristic,
    }}
>
    {/* ... */}
</Editor.Root>

See the custom PDF backend recipe for a full example.

Rulers + minimap

<Editor.Root showRuler showMinimap rulerUnit="mm">
    {/* ... */}
</Editor.Root>

API Reference

PropTypeDefaultDescription
childrenReactNode-

Extra nodes mounted **after** the default app-shell

  • custom dialogs, an extra toaster, analytics bridges, etc. The shell renders the full editor on its own, so children are purely additive overlays. Omit for the canonical <Editor.Root /> usage.
themeToggleReactNode-

Theme-toggle slot rendered in the top-bar right group. The package never imports next-themes; the host app injects a ready-made toggle node here. Renders nothing when omitted.

showLeftRailboolean | undefined-

Render the left rail (tool palette + Pages + Layers). Defaults to true.

showToolPaletteboolean | undefined-

Render the tool palette inside the left rail. Defaults to true.

showPagesboolean | undefined-

Render the "Pages" section inside the left rail. Defaults to true.

showLayersboolean | undefined-

Render the "Layers" section inside the left rail. Defaults to true.

showInspectorboolean | undefined-

Render the right inspector panel. Defaults to true.

pdfPartial<EditorPdfBackend> | undefined-

Pluggable PDF backend. Either or both functions can be overridden. Anything omitted falls back to the bundled @docmosaic/core implementation (generatePDF / estimatePDFSize). Use this to swap the jsPDF path for a different renderer, run generation in a Worker, or mock it out in tests.

keybindingsfalse | Partial<EditorKeymap> | undefined-

Keyboard shortcuts.

  • Omit (default): the built-in keymap is active
  • mod+z undo, mod+shift+z / mod+y redo, Delete / Backspace removes the selected section, Escape deselects, and Arrow / Shift+Arrow nudge the selected section by 1pt / 10pt.
  • Pass a partial EditorKeymap to override individual bindings or register alternates (e.g. { redo: 'mod+r' }).
  • Pass false to disable the layer entirely
  • no window listener is attached. Bindings are skipped while focus is inside an <input>, <textarea>, <select>, or anything contenteditable, so text fields like the document-name input remain typeable.
readOnlyboolean | undefined-

Render the editor in read-only / viewer mode. Defaults to false. When true:

  • Section drag, resize, drop, and file upload are suppressed.
  • Section floating toolbars (delete, duplicate, layer buttons) hide.
  • The properties bar (document name, page size, orientation) becomes read-only.
  • The page list hides Add Image / Add Page / per-page delete and ignores reorder gestures.
  • The PropertiesPanel renders its number/text inputs disabled and hides the Layer buttons.
  • The toolbar's AddImageButton, AddTextButton, AddShapeButton, DrawButton, UndoButton, and RedoButton hide themselves.
  • Keybindings skip every mutating action (undo, redo, deleteSection, nudge*); deselect (Esc) still works.
  • Drawing-mode pointer captures are ignored. What still works:
  • Selection (click, shift-click, marquee).
  • Zoom, pan, preview dialog, print, PDF/PNG download.
showRulerboolean | undefined-

Render Editor.Ruler along the top and left edges of the canvas viewport. Defaults to false. When true, the canvas reserves a 24px gutter on each axis so the rulers sit flush with the page edge without occluding section geometry.

showMinimapboolean | undefined-

Render Editor.Minimap anchored to the bottom-right of the canvas viewport

  • a thumbnail of the current page plus a viewport rectangle that pans the main canvas. Defaults to false.
rulerUnitenum-

Unit used by Editor.Ruler tick labels. Defaults to 'pt' so the tick values match the document's storage unit one-to-one. Set to 'mm' or 'in' to surface a human-facing measurement instead.

documentDocument | undefined-

Controlled: caller owns the document.

onDocumentChange((next: Document) => void) | undefined--
defaultDocumentDocument | undefined-

Uncontrolled: optional seed for the internally-owned document.