[Bug] DOCX import to Quill loses original Word formatting (indentation, lists, spacing) — output does not match source
When a user uploads a .docx file into the policy editor, the resulting content rendered in the Quill editor does not visually match the original Word document. Indentation, multi-level list numbering, paragraph spacing, and some text styles are lost or rendered incorrectly.
Affected area
- Editor: components/editor/QuillEditorCore.tsx
- DOCX → HTML conversion: app/api/templates/[id]/route.ts (mammoth.js + applyListIndentToHtml)
- Import preview routes: /api/policy-templates/import-preview, /api/policies/import-preview
Steps to reproduce
- Run the frontend (npm run dev, port 3001).
- Go to Policies → Create / Import and upload one_smart_compliance_frontend/assets/Business.docx.
- Open the same Business.docx in Microsoft Word side-by-side with the editor preview.
- Compare formatting.
Expected behavior The Quill editor should render the imported content with formatting that matches the source .docx as closely as possible:
- Correct left indentation on paragraphs and nested list items
- Multi-level numbered/bulleted lists preserved (1, 1.1, 1.1.1, a, b, c, etc.)
- Paragraph spacing and line height consistent with Word
- Bold / italic / underline / strikethrough preserved on inline runs
- Tables preserved with original cell alignment and borders
- Heading levels (H1–H5) matching Word styles
Actual behavior
- Indentation is flattened — paragraphs that should be indented 1–3 levels render at a single indent level or no indent. Custom ql-indent-1/2/3 classes from
applyListIndentToHtml do not always cover Word's w:numPr/w:ind cases. - Numbered lists lose their numbering scheme — multi-level lists (e.g. 1. → 1.1 → 1.1.1) are converted to plain bullets or restart numbering at each level.
- Paragraph spacing is collapsed — empty paragraphs are stripped (html.replace(/
\s*</p>/g, '')), removing intentional vertical whitespace.
- Inline styles (font-family) are stripped globally to enforce app font, but font-size differences between headings and body sometimes flatten.
- Tables render but lose column widths and merged cells.
- Comments and tracked changes in the source .docx are dropped entirely (not currently supported by mammoth).
Test files
- one_smart_compliance_frontend/assets/Business.docx (primary repro)
- one_smart_compliance_frontend/docs/Maintaining Compliance_V2026.1.docx
- one_smart_compliance_frontend/docs/Regulatory Compliance and Internal Controls_V2026.1.docx
Root cause (initial analysis)
- mammoth.js is lossy by design. It targets a "semantic HTML" output and intentionally drops most direct formatting (margins, spacing, indents, list numbering schemes). Our custom styleMap and applyListIndentToHtml post-processor only patch a subset of cases.
- Quill's data model is limited. Quill stores content as Delta and has no native concept of multi-level numbered lists, page breaks, or comments. Even with a perfect HTML conversion, Quill would normalize a lot of it away on clipboard.dangerouslyPasteHTML.
- No round-trip fidelity guarantee. HTML → Quill Delta → HTML is destructive; what the user sees is not what is persisted.
Possible directions (for discussion)
- Replace mammoth with docx-preview for higher-fidelity HTML conversion.
- Extend applyListIndentToHtml to handle every w:numPr + w:ind combination present in our sample docs.
- Evaluate replacing Quill with an editor that has first-class .docx support (CKEditor 5 + Import from Word, SuperDoc, or ONLYOFFICE) — see R&D ticket.
- Add a regression suite: snapshot .docx → HTML output for Business.docx and the V2026.1 templates; fail CI if output drifts.
Environment
- Frontend: Next.js 16 (App Router), React 19, Quill (latest), mammoth (CDN/npm)
- Backend: NestJS 11
- Browser: Chrome / Edge (latest)
- OS: Windows 11
Attachments
- Source: assets/Business.docx
- Recommend attaching: screenshot of Word source vs. screenshot of Quill render side-by-side.
Business Continuity Plan_V2026.1.docx Maintaining Compliance_V2026.1.docx Regulatory Compliance and Internal Controls_V2026.1.docx
Source: zenoamaro/react-quill