TutorialMay 20, 20262 min read

Next.js DOCX editor: edit and embed Word documents in the browser

Edit DOCX files in a Next.js App Router project. Install the React adapter, render it client-side, load a file, and save the result.

To edit Word documents in a Next.js app, install @docx-editor.dev/react, pass the document bytes, and render <DocxEditor> inside a "use client" component. The editor parses Word OOXML in the browser.

This guide covers installation, the client component, file input handling, saving through a route handler, and the common server-side rendering (SSR) errors.

Live demo

This editor runs in this Next.js application. The demo sends no document data to a server.

Install

npm install @docx-editor.dev/react @docx-editor.dev/core

@docx-editor.dev/core is a peer dependency. The adapter bundles the string catalog.

Build the editor component

"use client";
 
import { useState, useEffect, useRef, useCallback } from "react";
import { DocxEditor } from "@docx-editor.dev/react";
import type { DocxEditorRef } from "@docx-editor.dev/react";
import "@docx-editor.dev/core/styles/editor.css";
 
const DOCX_MIME =
  "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
 
export function MyDocxEditor() {
  const editorRef = useRef<DocxEditorRef>(null);
  const [buffer, setBuffer] = useState<ArrayBuffer | null>(null);
 
  useEffect(() => {
    fetch("/sample.docx")
      .then((res) => res.arrayBuffer())
      .then(setBuffer);
  }, []);
 
  const handleSave = useCallback(async () => {
    const saved = await editorRef.current?.save();
    if (!saved) return;
    const blob = new Blob([saved], { type: DOCX_MIME });
    const url = URL.createObjectURL(blob);
    Object.assign(document.createElement("a"), {
      href: url,
      download: "edited.docx",
    }).click();
    URL.revokeObjectURL(url);
  }, []);
 
  if (!buffer) return <div>Loading...</div>;
 
  return (
    <div style={{ height: "80vh" }}>
      <DocxEditor ref={editorRef} document={buffer} mode="edit" />
      <button onClick={handleSave}>Download .docx</button>
    </div>
  );
}

Add "use client". The component uses refs, hooks, and browser globals, so it cannot render on the server.

Render the component in a page

import { MyDocxEditor } from "@/components/DocxEditor";
 
export default function EditorPage() {
  return <MyDocxEditor />;
}

Let users open a file

"use client";
 
import { useState } from "react";
import { DocxEditor } from "@docx-editor.dev/react";
 
export function UploadEditor() {
  const [buffer, setBuffer] = useState<ArrayBuffer | null>(null);
 
  function handleUpload(event: React.ChangeEvent<HTMLInputElement>) {
    const file = event.target.files?.[0];
    if (!file) return;
    const reader = new FileReader();
    reader.onload = () => setBuffer(reader.result as ArrayBuffer);
    reader.readAsArrayBuffer(file);
  }
 
  return (
    <div style={{ height: "80vh" }}>
      <input type="file" accept=".docx" onChange={handleUpload} />
      {buffer && <DocxEditor document={buffer} />}
    </div>
  );
}

Set a different buffer to load another document without a page reload.

Save to a route handler

// app/api/documents/route.ts
import { NextResponse, type NextRequest } from "next/server";
 
export async function POST(request: NextRequest) {
  const data = await request.arrayBuffer();
  // Store the bytes: upload to object storage, write to a database, and so on.
  console.log(`received ${data.byteLength} bytes`);
  return NextResponse.json({ ok: true });
}
"use client";
 
import { useRef } from "react";
import { DocxEditor, type DocxEditorRef } from "@docx-editor.dev/react";
 
export function SavingEditor({ buffer }: { buffer: ArrayBuffer }) {
  const editorRef = useRef<DocxEditorRef>(null);
 
  async function save() {
    const saved = await editorRef.current?.save();
    if (!saved) return;
    await fetch("/api/documents", { method: "POST", body: saved });
  }
 
  return (
    <>
      <button onClick={save}>Save</button>
      <DocxEditor ref={editorRef} document={buffer} />
    </>
  );
}

Common errors

ErrorFix
Styles do not renderImport @docx-editor.dev/core/styles/editor.css in the client component

Included features

The editor parses OOXML in the browser and renders the document as pages. It supports:

  • Bold, italic, underline, and strikethrough
  • Tables, including merged cells
  • Inline and floating images
  • Headers, footers, and page breaks
  • Zoom and a document outline

It writes the document back to .docx. The editor is Apache 2.0 and needs no document-processing server. For more information about tracked changes, comments, and custom nodes, see the Pro module.

Where to go next