Skip to content

Authoring guide

This page covers the patterns we use when building real documents — the “why” behind the API, plus a few concrete templates you can lift.

Build the document as a React tree

Treat the document like any other component. Take props, derive content, return JSX. The shape is the same whether you’re generating from data or hand-authoring.

function Invoice({ client, lineItems }: Props) {
const total = lineItems.reduce((s, l) => s + l.qty * l.unit, 0);
return (
<Document
pageSize="letter"
meta={{ title: `Invoice ${invoiceId}`, author: 'helixui' }}
exportFileName={`invoice-${invoiceId}.docx`}
>
<Heading level={1} align="right">Invoice #{invoiceId}</Heading>
<Paragraph>Bill to: {client.name}</Paragraph>
<DocTable
header={['Item', 'Qty', 'Unit', 'Total']}
rows={lineItems.map((l) => [l.name, l.qty, fmt(l.unit), fmt(l.qty * l.unit)])}
colWidths={[3.6, 0.7, 1.2, 1.4]}
/>
<Paragraph align="right">
<DocText size={14} weight="bold" color="brand">Total · {fmt(total)}</DocText>
</Paragraph>
</Document>
);
}

Compose at the block level, style at the run level

Blocks (<Heading>, <Paragraph>, <DocList>, <DocTable>) own layout — alignment, indentation, spacing. Inline runs (<DocText>, <DocLink>) own visual style — bold, color, font.

This split keeps the API tight and matches how Word itself thinks about documents.

<Paragraph align="justify" indent={0.25}>
The platform team will own{' '}
<DocText weight="semibold" color="brand">tokens</DocText> and{' '}
<DocText weight="semibold" color="brand">docs</DocText> through Q3.
</Paragraph>

Common patterns

Section header + body

<Heading level={2}>Risks</Heading>
<DocList type="number">
<DocListItem>Browser-side DOCX export is heavier than PPTX.</DocListItem>
<DocListItem>Image embedding requires CORS-friendly sources.</DocListItem>
</DocList>

Two-column figures (with sections)

@helixui/document doesn’t ship a column primitive. For a wide figure or chart, switch the section to landscape:

<DocSection orientation="landscape" margins={{ top: 0.5, right: 0.5, bottom: 0.5, left: 0.5 }}>
<Heading level={2}>Architecture</Heading>
<DocImage src="/architecture.svg" w={10} h={6} align="center" />
</DocSection>

Right-aligned totals

For invoices and receipts, set align="right" on the totals paragraphs. Color the grand total with <DocText color="brand"> so it pops without needing a separate style.

<Paragraph align="right">
Subtotal: <DocText weight="semibold">$ 22,820</DocText>
</Paragraph>
<Paragraph align="right">
<DocText size={14} weight="bold" color="brand">Total · $ 25,102</DocText>
</Paragraph>

Highlighted runs

highlight maps to docx’s named highlight enum. Pass a theme key and the exporter will pick the closest match.

<DocText highlight="success">Added</DocText>
<DocText highlight="brand">Changed</DocText>
<DocText highlight="danger">Removed</DocText>

Code blocks with language hints

The language prop is preserved as a data-language attribute in the DOM preview and is currently informational on export — Word doesn’t render syntax highlighting natively. Use it anyway, future versions can pre-render highlighted spans.

<DocCodeBlock language="ts">
{`await exportToDocx(<Brief />);`}
</DocCodeBlock>

Page breaks vs sections

Use <DocPageBreak> when you only want a new page — same page setup. Use <DocSection> when you want a new page and a setup change (orientation, margins, page size).

Generating from data

Any list of records → a document. Map to React, hand to exportToDocx.

async function downloadChangelog(releases: Release[]) {
const doc = (
<Document meta={{ title: 'Changelog', author: 'helixui' }} exportFileName="changelog.docx">
<Heading level={1}>Changelog</Heading>
{releases.map((r) => (
<React.Fragment key={r.version}>
<Heading level={2}>{r.version}{r.date}</Heading>
<DocList>
{r.notes.map((n, i) => <DocListItem key={i}>{n}</DocListItem>)}
</DocList>
</React.Fragment>
))}
</Document>
);
await exportToDocx(doc);
}

Don’t over-style

Word handles defaults well. Body text doesn’t need an explicit size or color — the theme’s bodyFontSize and colors.ink already apply. Lean on the theme; reserve inline overrides for emphasis.

Preview before exporting

The same React tree feeds both the DOM preview and the exporter. Render the document in your app, let the user review it, then attach the export to a button:

const docEl = <Brief />;
return (
<>
{docEl}
<Button onClick={() => exportToDocx(docEl)}>Download .docx</Button>
</>
);

You can also attach the document to email or upload it without triggering a download by calling documentToDocxBlob() instead.