Fabric.js Document Engine

Search documentation

Find a page or section

中文

Keep Fabric.js in the browser, and the rest of the page on the server.

Fabric.js needs a browser: it creates <canvas> elements and reads window. In Fabric.js Next.js apps, render the editor only on the client, and keep the rest of the page server-rendered.

Render the editor on the client

Put the editor in its own file marked 'use client', and load it with a dynamic import that skips SSR:

// app/editor/editor-loader.tsx
"use client";
 
import dynamic from "next/dynamic";
 
export const Editor = dynamic(() => import("./editor-client"), {
  ssr: false,
  loading: () => <p>Loading the editor…</p>,
});
// app/editor/page.tsx
import { Editor } from "./editor-loader";
 
export default function EditorPage() {
  return (
    <main>
      <h1>Floor plan</h1>
      <Editor />
    </main>
  );
}

ssr: false is only allowed inside a client component, which is why the loader file has 'use client' too.

The client component

Inside editor-client.tsx, create the canvas in an effect and use the hooks, exactly as in the React guide:

// app/editor/editor-client.tsx
"use client";
 
import { useDocumentEngine } from "fabricjs-document-engine/react";
import { createLocalStorage } from "fabricjs-document-engine/storage";
 
import { useFabricCanvas } from "./use-fabric-canvas";
 
const storage = createLocalStorage({ prefix: "plans:" });
 
export default function EditorClient() {
  const { elementRef, canvas } = useFabricCanvas(1200, 800);
  const engine = useDocumentEngine(canvas, { storage, autosave: true });
 
  return <canvas ref={elementRef} aria-label="Floor plan editor" />;
}

The fabricjs-document-engine/react entry is already marked 'use client', so importing the hooks never breaks a server component.

On the server

The document helpers work in Node without a canvas. Use them in a route handler to check uploads before you store them:

// app/api/documents/[id]/route.ts
import { secureDocument, validateDocument } from "fabricjs-document-engine";
 
export async function PUT(request: Request) {
  const document = secureDocument(await request.json());
  if (validateDocument(document).length > 0) {
    return new Response("Invalid document", { status: 422 });
  }
  // Store the document, checking the revision in the same query.
  return Response.json({ revision: 1 });
}

Common Fabric.js Next.js errors

  • "window is not defined": Fabric.js ran during SSR. Load the editor with ssr: false.
  • "useState only works in client components": import hooks from fabricjs-document-engine/react inside a file marked 'use client'.

More fixes are in troubleshooting.