Skip to content

Overview — @helixui/document

@helixui/document is a sibling of @helixui/slides. It lets you build Word documents the same way you build the rest of your UI — as React components.

The mental model is simple:

A <Document> is a flow of blocks (<Heading>, <Paragraph>, <DocList>, <DocTable>, …). Page geometry is in inches, fonts are in points — the same units Word uses internally — so the DOM preview maps 1:1 to what lands in the .docx.

Live demo: /showcase/document — flip between preset documents and click Download .docx. Open the file in Word, Google Docs, or Pages and it’ll be a real, editable document.

Install

Terminal window
pnpm add @helixui/document
# Optional — only needed when you actually call the exporter.
pnpm add docx

The docx library is a lazy peer dependency. The base @helixui/document bundle does not import it until you call exportToDocx(). If you only want the web preview, you don’t need to install it at all.

A 30-second document

import {
Document, Heading, Paragraph, DocText, DocList, DocListItem,
DocTable, exportToDocx,
} from '@helixui/document';
import '@helixui/document/styles.css';
function Brief() {
return (
<Document
exportFileName="brief.docx"
meta={{ title: 'Q3 product brief', author: 'helixui' }}
>
<Heading level={1}>Q3 product brief</Heading>
<Paragraph>
We focus on three bets:{' '}
<DocText weight="bold">tokens</DocText>,{' '}
<DocText weight="bold">docs</DocText>, and{' '}
<DocText weight="bold">interop</DocText>.
</Paragraph>
<Heading level={2}>Bets</Heading>
<DocList type="check">
<DocListItem>Ship a token manifest LLMs can consume.</DocListItem>
<DocListItem>Render the same source to web, PPTX, DOCX.</DocListItem>
<DocListItem>Stay framework-light on the runtime.</DocListItem>
</DocList>
<DocTable
header={['Bet', 'Owner', 'Status']}
rows={[
['Tokens', 'design', 'shipped'],
['Docs', 'docs guild', 'in flight'],
['Interop', 'core', 'planning'],
]}
/>
</Document>
);
}
// in a button handler
await exportToDocx(<Brief />); // → brief.docx

How it maps to DOCX

Every primitive corresponds 1:1 to a docx construct so the export is predictable and lossless within the supported feature set.

ComponentDOCX equivalentNotes
<Document>Document + first SectionPage setup, theme, file metadata
<DocSection>New SectionMid-document page-setup change
<Heading level={1..6}>Paragraph + HeadingLevel.HEADING_nMapped to Word heading styles
<Paragraph>Paragraph with TextRunsBlock of text, inline formatting via children
<DocText>TextRunInline run with bold / italic / color / font / etc.
<DocLink>ExternalHyperlink + nested TextRunHonors styling on the hyperlink runs
<DocList> + <DocListItem>Paragraph per item with bullet prefixManual bullet glyph for max compatibility
<DocTable>Table + TableRow + TableCellHeader row, optional zebra, optional borders
<DocImage>Paragraph with ImageRunFetched + base64-inlined; SVG → SVG with PNG fallback
<DocCodeBlock>Paragraph per line, mono font, shadedPreserves whitespace and newlines
<DocBlockquote>Paragraph with left border + indentOptional <cite> paragraph below
<DocHorizontalRule>Paragraph with bottom borderStandard thematic break
<DocPageBreak>Paragraph containing PageBreakForces a new page mid-flow

Why points + inches?

Word measures fonts in points and page geometry in inches (or cm — they’re equivalent). @helixui/document mirrors these units verbatim so there’s no abstraction layer between props and the file:

  • No coordinate translation. A Paragraph with spaceAfter: 6 writes 6 points after the paragraph in the file. A column with colWidths: [3.6] occupies 3.6 inches.
  • Consistent rendering. The DOM preview applies inches directly (CSS’s in unit) and uses transform: scale() to fit the page canvas in any container. A 1-inch margin is always 1 inch.
  • Print-ready math. Page sizes match the standards: Letter → 8.5 × 11 in, A4 → 8.27 × 11.69 in, Legal → 8.5 × 14 in.

Sections vs the top-level Document

The <Document> component is itself a section — its pageSize, orientation, and margins apply to the first chunk of content. To switch page setup mid-document (e.g., a landscape spread inside a portrait report), drop a <DocSection>:

<Document pageSize="letter">
<Heading level={1}>Cover</Heading>
<Paragraph></Paragraph>
<DocSection orientation="landscape" margins={{ top: 0.5, right: 0.5, bottom: 0.5, left: 0.5 }}>
<Heading level={2}>Wide diagram</Heading>
<DocImage src="/architecture.svg" w={10} h={6} />
</DocSection>
<DocSection>
<Paragraph>Back to portrait.</Paragraph>
</DocSection>
</Document>

Each <DocSection> becomes a new Section in the exported file and starts on its own page by default (pageBreakBefore defaults to true).

Theming

<Document> accepts a theme prop with colors, fonts, and default font sizes. The defaults pull values from helixui CSS variables, so any DNA theme flows through. During DOCX export, any token reference ('brand', '--helixui-color-...', oklch(...), etc.) is resolved to a concrete hex via a hidden canvas — documents export faithfully under any theme.

<Document
theme={{
colors: { brand: 'oklch(0.62 0.18 264)', ink: '#0b0d12', paper: '#fafafa' },
fonts: { heading: '"Geist", system-ui, sans-serif' },
bodyFontSize: 12,
headingFontSize: 32,
}}
>
</Document>

What’s next

  • Components — every primitive at a glance
  • Authoring guide — patterns for real documents
  • Exporting — async export, file metadata, blob output, fonts, image inlining