This guide puts the free @runstamp/editor DOCX editor into a Next.js app (App Router). It takes three files and no account or key. It needs Node.js 20 or later.
1. Create the app and install the editor
npx create-next-app@latest your-app --ts --app --no-src-dir --eslint --no-tailwind --use-npm --yes
cd your-app
npm install https://runstamp.com/dl/runstamp-editor-0.1.0.tgz @runstamp/sdk@runstamp/editor is the browser editor. @runstamp/sdk is the server half: it replays each edit against the stored file.
2. Add your .docx
Copy any Word file into the app folder, next to package.json, as contract.docx.
3. Replace app/page.tsx
'use client';
import '@runstamp/editor/styles.css';
import { useMemo } from 'react';
import { DocxEditor } from '@runstamp/editor/docx/react';
import { httpHost } from '@runstamp/editor/headless';
export default function Page() {
// The host loads the .docx from your route handler and posts every edit back to it.
const host = useMemo(() => httpHost({ baseUrl: '/api/editor', documentId: 'contract' }), []);
return (
<main style={{ display: 'flex', flexDirection: 'column', height: '100vh' }}>
<header style={{ padding: '12px 16px', font: '600 16px system-ui' }}>YourApp</header>
<DocxEditor
host={host}
style={{ flex: 1, minHeight: 0 }}
engine={() => new Worker(new URL('@runstamp/editor/docx/layout-worker', import.meta.url), { type: 'module' })}
/>
</main>
);
}4. Add the route that stores saves
Create app/api/editor/[...path]/route.ts:
import { readFile } from 'node:fs/promises';
import { join } from 'node:path';
import { allowAnyone, createEditorHostHandler } from '@runstamp/sdk/editor-host';
import { fileSystemStorage } from '@runstamp/sdk/editor-host/node';
// Saved revisions go to ./data. The first request stores ./contract.docx (your file) as revision 0.
const storage = fileSystemStorage(join(process.cwd(), 'data'));
const handler = createEditorHostHandler({ storage, basePath: '/api/editor', authorize: allowAnyone });
let seeded: Promise<unknown> | undefined;
async function handle(request: Request): Promise<Response> {
seeded ??= readFile(join(process.cwd(), 'contract.docx')).then((bytes) =>
storage.ensure({ documentId: 'contract', format: 'docx', name: 'contract.docx', bytes: new Uint8Array(bytes) }));
await seeded;
return handler(request);
}
export { handle as GET, handle as HEAD, handle as POST, handle as OPTIONS };allowAnyone is for the demo: replace it with your own session check before you ship.
5. Replace next.config.ts
Replace the next.config.ts that create-next-app made. It lets the page load at http://127.0.0.1:3000 as well as http://localhost:3000; without allowedDevOrigins the Next.js dev server blocks its own scripts on 127.0.0.1 and the page stays blank. Step 1 makes the app without Tailwind (--no-tailwind): with Tailwind, create-next-app writes Tailwind's CSS loader into next.config.ts, and replacing the file would make npm run build print Unknown at rule: @theme. The editor brings its own styles.
import type { NextConfig } from 'next';
const nextConfig: NextConfig = {
allowedDevOrigins: ['127.0.0.1'],
};
export default nextConfig;6. Build and run
npm run build && npm startOpen http://localhost:3000 or http://127.0.0.1:3000. npm run dev works the same way. The contract opens paginated, with "Page 1 of N" in the status bar. Edit it and click Save in the toolbar, or press Ctrl+S or Cmd+S (either works on any system). The status next to the button reads "Unsaved changes" until you save, then "All changes saved". Each save writes a revision under data/contract/: ls -l data/contract shows r1.bin, the saved .docx.