# Next.js

> 在 Fabric.js Next.js 项目里避免 SSR 报错：用 'use client' 和跳过服务端的动态导入，在客户端渲染画布编辑器。

Source: https://fabricjs-document-engine.jscrate.dev/zh/docs/frameworks/next-js
Last updated: 2026-09-28

Fabric.js 需要浏览器：它会创建 `<canvas>` 元素，还会读取 `window`。在 Fabric.js Next.js 项目里，只在客户端渲染编辑器，页面其余部分照常在服务端渲染。

## 在客户端渲染编辑器

把编辑器放在单独的文件里并标记 `'use client'`，再用跳过 SSR 的动态导入加载它：

```tsx
// 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>,
});
```

```tsx
// app/editor/page.tsx
import { Editor } from "./editor-loader";

export default function EditorPage() {
  return (
    <main>
      <h1>Floor plan</h1>
      <Editor />
    </main>
  );
}
```

`ssr: false` 只能在客户端组件里使用，所以加载文件也要写 `'use client'`。

## 客户端组件

在 `editor-client.tsx` 里，用 effect 创建画布并使用 hooks，做法和 [React 指南](https://fabricjs-document-engine.jscrate.dev/zh/docs/frameworks/react)完全一样：

```tsx
// 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" />;
}
```

`fabricjs-document-engine/react` 入口已经标记了 `'use client'`，所以导入这些 hooks 不会影响服务端组件。

## 在服务端

文档相关的辅助函数在 Node 里无需画布也能运行。在路由处理函数里用它们，先检查上传的文档再存储：

```ts
// 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 });
}
```

## Fabric.js Next.js 常见错误

- **"window is not defined"**：Fabric.js 在 SSR 时运行了。用 `ssr: false` 加载编辑器。
- **"useState only works in client components"**：在标记了 `'use client'` 的文件里，从 `fabricjs-document-engine/react` 导入 hooks。

更多解决办法见[问题排查](https://fabricjs-document-engine.jscrate.dev/zh/docs/production/troubleshooting)。
