DocMosaicdocs
Recipes

Next.js Server Component boundary

Where to put the 'use client' directive so the Editor.* namespace dereferences cleanly.

@docmosaic/react is client-only. In Next.js App Router, that's two minutes of wiring - but one detail trips people up.

The problem

A Server Component can't dereference the Editor.* namespace through a client-reference proxy. This breaks:

// app/editor/page.tsx - Server Component
import { Editor } from '@docmosaic/react';

export default function Page() {
    // ❌ Editor.Root is a client reference; Server Component can't read .Root
    return <Editor.Root>{/* ... */}</Editor.Root>;
}

You'll see a runtime error like "Cannot read properties of undefined (reading 'Root')".

The fix

Move the dereferencing into a client component. The simplest approach: one client file per editor route.

// app/editor/page.tsx - Server Component, fine
import { Suspense } from 'react';
import { EditorMount } from './editor-mount';

export default function Page() {
    return (
        <Suspense fallback={<div>Loading editor…</div>}>
            <EditorMount />
        </Suspense>
    );
}
// app/editor/editor-mount.tsx - Client component
'use client';

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

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

The 'use client' directive at the top of editor-mount.tsx means the dereferencing happens at the client-runtime, where the namespace object exists.

Why not import flat?

The flat exports (EditorRoot, EditorCanvas, etc.) work from a Server Component because each is a single client-reference, not a property access:

// Works from Server Component
import { EditorRoot, EditorCanvas, EditorSection } from '@docmosaic/react';

But you still need a 'use client' boundary somewhere - the components themselves are client components. The boundary is just slightly higher in the tree.

See also