Scrivr
Integrations

React Integration

Patterns for Next.js, server components, SSR, and context providers.

@scrivr/react is a client-only library — it creates a canvas, listens to DOM events, and manages browser APIs. This page covers the patterns you need when using Scrivr inside a React framework like Next.js.


Client Components

The editor must live in a client component. Add 'use client' to any file that imports from @scrivr/react:

components/MyEditor.tsx
'use client';

import { useScrivrEditor, Scrivr, StarterKit } from '@scrivr/react';

export function MyEditor() {
  const editor = useScrivrEditor({ extensions: [StarterKit] });
  return <Scrivr editor={editor} style={{ height: '100vh' }} />;
}

Server components can then import and render <MyEditor /> normally — they just can't use the editor instance themselves.


Next.js — Disabling SSR

If you need the editor component to be lazily loaded (e.g. to keep it out of the initial bundle), use next/dynamic with ssr: false:

app/page.tsx
import dynamic from 'next/dynamic';

const MyEditor = dynamic(() => import('@/components/MyEditor'), { ssr: false });

export default function Page() {
  return <MyEditor />;
}

'use client' on the component itself is still required — ssr: false only controls whether Next.js attempts to server-render the component during the build.


Sharing the Editor Instance

For small apps, pass editor as a prop. For larger trees, create a context to avoid threading it through every component.

context/EditorContext.tsx
'use client';

import { createContext, useContext } from 'react';
import type { Editor } from '@scrivr/core';

const EditorContext = createContext<Editor | null>(null);

export const EditorProvider = EditorContext.Provider;

export function useEditor(): Editor | null {
  return useContext(EditorContext);
}
components/EditorApp.tsx
'use client';

import { useScrivrEditor, Scrivr, StarterKit } from '@scrivr/react';
import { EditorProvider } from '@/context/EditorContext';
import { Toolbar } from './Toolbar';

export function EditorApp() {
  const editor = useScrivrEditor({ extensions: [StarterKit] });

  return (
    <EditorProvider value={editor}>
      <Toolbar />
      <Scrivr editor={editor} style={{ height: '100vh' }} />
    </EditorProvider>
  );
}
components/Toolbar.tsx
'use client';

import { useEditor } from '@/context/EditorContext';

export function Toolbar() {
  const editor = useEditor();
  return (
    <button onClick={() => editor?.commands.toggleBold()}>Bold</button>
  );
}

A built-in ScrivrContext and useScrivr hook are coming to @scrivr/react — see the React package page. Once available, the manual context above can be replaced with those.


Server-Side Document Processing

If you need to process documents on the server — applying AI suggestions, validating content, or exporting to Markdown — use ServerEditor from @scrivr/core. It shares the same schema and extension system as the browser Editor but has no DOM dependency.

app/api/export/route.ts
import { ServerEditor, StarterKit } from '@scrivr/core';

export async function POST(req: Request) {
  const { doc } = await req.json();

  const editor = new ServerEditor({
    extensions: [StarterKit],
    content: doc,
  });

  return Response.json({ markdown: editor.getMarkdown() });
}

See the ServerEditor API reference for the full interface.

On this page