DocMosaicdocs
Recipes

Read-only viewer

Render a fully-composed editor as a viewer. Users can select, zoom, preview, and download - but nothing about the document is mutable.

Editor.Root readOnly flips the editor into viewer mode. Every mutating interaction is suppressed; export, preview, and selection keep working.

When you'd use this

  • Embedding a finished contract / template for review.
  • Sharing a built document with a stakeholder who shouldn't accidentally drag a section.
  • Pairing an editable canvas with a frozen reference canvas in a compare view.
  • A "preview" before commit step inside a multi-step flow.

Basic - root-level readOnly

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

export function DocumentViewer({ document }: { document: Document }) {
    return (
        <Editor.Root defaultDocument={document} readOnly>
            <Editor.Properties />
            <Editor.Toolbar />
            <Editor.Pages />
            <Editor.Canvas>
                <Editor.Section />
            </Editor.Canvas>
            <Editor.Preview />
        </Editor.Root>
    );
}

What changes in readOnly

SurfaceBehavior in readOnly
Canvas drag / dropNo-op. Section positions are frozen.
Section resizeHandles never render. Resize hook never arms.
Section file uploadDrop, drag-over, click-to-upload are no-ops. Crop overlay is suppressed.
Section floating toolbarHidden - duplicate / delete / layer buttons aren't rendered.
Drawing modePointer captures ignored. The Draw button isn't rendered.
Page listAdd Image / Add Page buttons hidden. Per-page delete hidden. Reorder ignored.
Properties barDocument name input, page size, orientation select are disabled.
PropertiesPanelLayout / Text / Shape inputs are disabled. Layer sub-section hides.
Page backgroundEditor.PageBackground returns null.
ToolbarUndoButton, RedoButton, Add*Button, DrawButton return null.
PreviewButton, PrintButton, DownloadButton stay live.
KeybindingsEvery mutating action (undo, redo, delete, nudge) is suppressed.
Escape (deselect) still works.

Selection (click, shift-click, marquee) keeps working - read-only is about mutation, not navigation.

Canvas-level - Editor.StaticCanvas

When the root is editable but a single canvas surface should refuse mutations, use Editor.StaticCanvas. The folding rule is effectiveReadOnly = root.readOnly || canvas.readOnly.

<Editor.Root defaultDocument={doc}>
    <Editor.Properties />
    <Editor.Toolbar />
    <div className="flex flex-1">
        <Editor.Pages />
        <Editor.StaticCanvas>
            <Editor.Section />
        </Editor.StaticCanvas>
    </div>
</Editor.Root>

This is the right primitive when:

  • You want a side-by-side "current vs. reference" view.
  • The root needs the toolbar / undo / page list to stay live for adjacent editable surfaces.
  • You're rendering a snapshot inside an otherwise-editable flow.

Detecting read-only in custom primitives

import { useEditor } from '@docmosaic/react';

function CustomAddButton() {
    const { actions, readOnly } = useEditor();
    if (readOnly) return null;
    return <button onClick={() => actions.addSection()}>Add</button>;
}

See also