# DOCX Library Tutorial

Generate .docx files with JavaScript/TypeScript.

**Important: Read this entire document before starting.** Critical formatting rules and common pitfalls are covered throughout - skipping sections may result in corrupted files or rendering issues.

## Setup

Assumes docx is already installed globally
If not installed: `npm install -g docx`

```javascript
const {
  Document,
  Packer,
  Paragraph,
  TextRun,
  Table,
  TableRow,
  TableCell,
  ImageRun,
  Media,
  Header,
  Footer,
  AlignmentType,
  PageOrientation,
  LevelFormat,
  ExternalHyperlink,
  InternalHyperlink,
  TableOfContents,
  HeadingLevel,
  BorderStyle,
  WidthType,
  TabStopType,
  TabStopPosition,
  UnderlineType,
  ShadingType,
  VerticalAlign,
  SymbolRun,
  PageNumber,
  FootnoteReferenceRun,
  Footnote,
  PageBreak,
} = require('docx');

// Create & Save
const doc = new Document({
  sections: [
    {
      children: [
        /* content */
      ],
    },
  ],
});
Packer.toBuffer(doc).then((buffer) => fs.writeFileSync('doc.docx', buffer)); // Node.js
Packer.toBlob(doc).then((blob) => {
  /* download logic */
}); // Browser
```

## Text & Formatting

```javascript
// IMPORTANT: Never use \n for line breaks - always use separate Paragraph elements
// ❌ WRONG: new TextRun("Line 1\nLine 2")
// ✅ CORRECT: new Paragraph({ children: [new TextRun("Line 1")] }), new Paragraph({ children: [new TextRun("Line 2")] })

// Basic text with all formatting options
new Paragraph({
  alignment: AlignmentType.CENTER,
  spacing: { before: 200, after: 200 },
  indent: { left: 720, right: 720 },
  children: [
    new TextRun({ text: 'Bold', bold: true }),
    new TextRun({ text: 'Italic', italics: true }),
    new TextRun({
      text: 'Underlined',
      underline: { type: UnderlineType.DOUBLE, color: 'FF0000' },
    }),
    new TextRun({ text: 'Colored', color: 'FF0000', size: 28, font: 'Arial' }), // Arial default
    new TextRun({ text: 'Highlighted', highlight: 'yellow' }),
    new TextRun({ text: 'Strikethrough', strike: true }),
    new TextRun({ text: 'x2', superScript: true }),
    new TextRun({ text: 'H2O', subScript: true }),
    new TextRun({ text: 'SMALL CAPS', smallCaps: true }),
    new SymbolRun({ char: '2022', font: 'Symbol' }), // Bullet •
    new SymbolRun({ char: '00A9', font: 'Arial' }), // Copyright © - Arial for symbols
  ],
});
```

## Styles & Professional Formatting

```javascript
const doc = new Document({
  styles: {
    default: { document: { run: { font: 'Arial', size: 24 } } }, // 12pt default
    paragraphStyles: [
      // Document title style - override built-in Title style
      {
        id: 'Title',
        name: 'Title',
        basedOn: 'Normal',
        run: { size: 56, bold: true, color: '000000', font: 'Arial' },
        paragraph: {
          spacing: { before: 240, after: 120 },
          alignment: AlignmentType.CENTER,
        },
      },
      // IMPORTANT: Override built-in heading styles by using their exact IDs
      {
        id: 'Heading1',
        name: 'Heading 1',
        basedOn: 'Normal',
        next: 'Normal',
        quickFormat: true,
        run: { size: 32, bold: true, color: '000000', font: 'Arial' }, // 16pt
        paragraph: { spacing: { before: 240, after: 240 }, outlineLevel: 0 },
      }, // Required for TOC
      {
        id: 'Heading2',
        name: 'Heading 2',
        basedOn: 'Normal',
        next: 'Normal',
        quickFormat: true,
        run: { size: 28, bold: true, color: '000000', font: 'Arial' }, // 14pt
        paragraph: { spacing: { before: 180, after: 180 }, outlineLevel: 1 },
      },
      // Custom styles use your own IDs
      {
        id: 'myStyle',
        name: 'My Style',
        basedOn: 'Normal',
        run: { size: 28, bold: true, color: '000000' },
        paragraph: { spacing: { after: 120 }, alignment: AlignmentType.CENTER },
      },
    ],
    characterStyles: [
      {
        id: 'myCharStyle',
        name: 'My Char Style',
        run: {
          color: 'FF0000',
          bold: true,
          underline: { type: UnderlineType.SINGLE },
        },
      },
    ],
  },
  sections: [
    {
      properties: {
        page: { margin: { top: 1440, right: 1440, bottom: 1440, left: 1440 } },
      },
      children: [
        new Paragraph({
          heading: HeadingLevel.TITLE,
          children: [new TextRun('Document Title')],
        }), // Uses overridden Title style
        new Paragraph({
          heading: HeadingLevel.HEADING_1,
          children: [new TextRun('Heading 1')],
        }), // Uses overridden Heading1 style
        new Paragraph({
          style: 'myStyle',
          children: [new TextRun('Custom paragraph style')],
        }),
        new Paragraph({
          children: [
            new TextRun('Normal with '),
            new TextRun({ text: 'custom char style', style: 'myCharStyle' }),
          ],
        }),
      ],
    },
  ],
});
```

**Professional Font Combinations:**

- **Arial (Headers) + Arial (Body)** - Most universally supported, clean and professional
- **Times New Roman (Headers) + Arial (Body)** - Classic serif headers with modern sans-serif body
- **Georgia (Headers) + Verdana (Body)** - Optimized for screen reading, elegant contrast

**Key Styling Principles:**

- **Override built-in styles**: Use exact IDs like "Heading1", "Heading2", "Heading3" to override Word's built-in heading styles
- **HeadingLevel constants**: `HeadingLevel.HEADING_1` uses "Heading1" style, `HeadingLevel.HEADING_2` uses "Heading2" style, etc.
- **Include outlineLevel**: Set `outlineLevel: 0` for H1, `outlineLevel: 1` for H2, etc. to ensure TOC works correctly
- **Use custom styles** instead of inline formatting for consistency
- **Set a default font** using `styles.default.document.run.font` - Arial is universally supported
- **Establish visual hierarchy** with different font sizes (titles > headers > body)
- **Add proper spacing** with `before` and `after` paragraph spacing
- **Use colors sparingly**: Default to black (000000) and shades of gray for titles and headings (heading 1, heading 2, etc.)
- **Set consistent margins** (1440 = 1 inch is standard)

## Lists (ALWAYS USE PROPER LISTS - NEVER USE UNICODE BULLETS)

```javascript
// Bullets - ALWAYS use the numbering config, NOT unicode symbols
// CRITICAL: Use LevelFormat.BULLET constant, NOT the string "bullet"
const doc = new Document({
  numbering: {
    config: [
      {
        reference: 'bullet-list',
        levels: [
          {
            level: 0,
            format: LevelFormat.BULLET,
            text: '•',
            alignment: AlignmentType.LEFT,
            style: { paragraph: { indent: { left: 720, hanging: 360 } } },
          },
        ],
      },
      {
        reference: 'first-numbered-list',
        levels: [
          {
            level: 0,
            format: LevelFormat.DECIMAL,
            text: '%1.',
            alignment: AlignmentType.LEFT,
            style: { paragraph: { indent: { left: 720, hanging: 360 } } },
          },
        ],
      },
      {
        reference: 'second-numbered-list', // Different reference = restarts at 1
        levels: [
          {
            level: 0,
            format: LevelFormat.DECIMAL,
            text: '%1.',
            alignment: AlignmentType.LEFT,
            style: { paragraph: { indent: { left: 720, hanging: 360 } } },
          },
        ],
      },
    ],
  },
  sections: [
    {
      children: [
        // Bullet list items
        new Paragraph({
          numbering: { reference: 'bullet-list', level: 0 },
          children: [new TextRun('First bullet point')],
        }),
        new Paragraph({
          numbering: { reference: 'bullet-list', level: 0 },
          children: [new TextRun('Second bullet point')],
        }),
        // Numbered list items
        new Paragraph({
          numbering: { reference: 'first-numbered-list', level: 0 },
          children: [new TextRun('First numbered item')],
        }),
        new Paragraph({
          numbering: { reference: 'first-numbered-list', level: 0 },
          children: [new TextRun('Second numbered item')],
        }),
        // ⚠️ CRITICAL: Different reference = INDEPENDENT list that restarts at 1
        // Same reference = CONTINUES previous numbering
        new Paragraph({
          numbering: { reference: 'second-numbered-list', level: 0 },
          children: [
            new TextRun('Starts at 1 again (because different reference)'),
          ],
        }),
      ],
    },
  ],
});

// ⚠️ CRITICAL NUMBERING RULE: Each reference creates an INDEPENDENT numbered list
// - Same reference = continues numbering (1, 2, 3... then 4, 5, 6...)
// - Different reference = restarts at 1 (1, 2, 3... then 1, 2, 3...)
// Use unique reference names for each separate numbered section!

// ⚠️ CRITICAL: NEVER use unicode bullets - they create fake lists that don't work properly
// new TextRun("• Item")           // WRONG
// new SymbolRun({ char: "2022" }) // WRONG
// ✅ ALWAYS use numbering config with LevelFormat.BULLET for real Word lists
```

## Tables

```javascript
// Complete table with margins, borders, headers, and bullet points
const tableBorder = { style: BorderStyle.SINGLE, size: 1, color: 'CCCCCC' };
const cellBorders = {
  top: tableBorder,
  bottom: tableBorder,
  left: tableBorder,
  right: tableBorder,
};

new Table({
  columnWidths: [4680, 4680], // ⚠️ CRITICAL: Set column widths at table level - values in DXA (twentieths of a point)
  margins: { top: 100, bottom: 100, left: 180, right: 180 }, // Set once for all cells
  rows: [
    new TableRow({
      tableHeader: true,
      children: [
        new TableCell({
          borders: cellBorders,
          width: { size: 4680, type: WidthType.DXA }, // ALSO set width on each cell
          // ⚠️ CRITICAL: Always use ShadingType.CLEAR to prevent black backgrounds in Word.
          shading: { fill: 'D5E8F0', type: ShadingType.CLEAR },
          verticalAlign: VerticalAlign.CENTER,
          children: [
            new Paragraph({
              alignment: AlignmentType.CENTER,
              children: [new TextRun({ text: 'Header', bold: true, size: 22 })],
            }),
          ],
        }),
        new TableCell({
          borders: cellBorders,
          width: { size: 4680, type: WidthType.DXA }, // ALSO set width on each cell
          shading: { fill: 'D5E8F0', type: ShadingType.CLEAR },
          children: [
            new Paragraph({
              alignment: AlignmentType.CENTER,
              children: [
                new TextRun({ text: 'Bullet Points', bold: true, size: 22 }),
              ],
            }),
          ],
        }),
      ],
    }),
    new TableRow({
      children: [
        new TableCell({
          borders: cellBorders,
          width: { size: 4680, type: WidthType.DXA }, // ALSO set width on each cell
          children: [
            new Paragraph({ children: [new TextRun('Regular data')] }),
          ],
        }),
        new TableCell({
          borders: cellBorders,
          width: { size: 4680, type: WidthType.DXA }, // ALSO set width on each cell
          children: [
            new Paragraph({
              numbering: { reference: 'bullet-list', level: 0 },
              children: [new TextRun('First bullet point')],
            }),
            new Paragraph({
              numbering: { reference: 'bullet-list', level: 0 },
              children: [new TextRun('Second bullet point')],
            }),
          ],
        }),
      ],
    }),
  ],
});
```

**IMPORTANT: Table Width & Borders**

- Use BOTH `columnWidths: [width1, width2, ...]` array AND `width: { size: X, type: WidthType.DXA }` on each cell
- Values in DXA (twentieths of a point): 1440 = 1 inch, Letter usable width = 9360 DXA (with 1" margins)
- Apply borders to individual `TableCell` elements, NOT the `Table` itself

**Precomputed Column Widths (Letter size with 1" margins = 9360 DXA total):**

- **2 columns:** `columnWidths: [4680, 4680]` (equal width)
- **3 columns:** `columnWidths: [3120, 3120, 3120]` (equal width)

## Links & Navigation

```javascript
// TOC (requires headings) - CRITICAL: Use HeadingLevel only, NOT custom styles
// ❌ WRONG: new Paragraph({ heading: HeadingLevel.HEADING_1, style: "customHeader", children: [new TextRun("Title")] })
// ✅ CORRECT: new Paragraph({ heading: HeadingLevel.HEADING_1, children: [new TextRun("Title")] })
new TableOfContents("Table of Contents", { hyperlink: true, headingStyleRange: "1-3" }),

// External link
new Paragraph({
  children: [new ExternalHyperlink({
    children: [new TextRun({ text: "Google", style: "Hyperlink" })],
    link: "https://www.google.com"
  })]
}),

// Internal link & bookmark
new Paragraph({
  children: [new InternalHyperlink({
    children: [new TextRun({ text: "Go to Section", style: "Hyperlink" })],
    anchor: "section1"
  })]
}),
new Paragraph({
  children: [new TextRun("Section Content")],
  bookmark: { id: "section1", name: "section1" }
}),
```

## Images & Media

```javascript
// Basic image with sizing & positioning
// CRITICAL: Always specify 'type' parameter - it's REQUIRED for ImageRun
new Paragraph({
  alignment: AlignmentType.CENTER,
  children: [
    new ImageRun({
      type: 'png', // NEW REQUIREMENT: Must specify image type (png, jpg, jpeg, gif, bmp, svg)
      data: fs.readFileSync('image.png'),
      transformation: { width: 200, height: 150, rotation: 0 }, // rotation in degrees
      altText: { title: 'Logo', description: 'Company logo', name: 'Name' }, // IMPORTANT: All three fields are required
    }),
  ],
});
```

## Page Breaks

```javascript
// Manual page break
(new Paragraph({ children: [new PageBreak()] }),
  // Page break before paragraph
  new Paragraph({
    pageBreakBefore: true,
    children: [new TextRun('This starts on a new page')],
  }));

// ⚠️ CRITICAL: NEVER use PageBreak standalone - it will create invalid XML that Word cannot open
// ❌ WRONG: new PageBreak()
// ✅ CORRECT: new Paragraph({ children: [new PageBreak()] })
```

## Headers/Footers & Page Setup

```javascript
const doc = new Document({
  sections: [
    {
      properties: {
        page: {
          margin: { top: 1440, right: 1440, bottom: 1440, left: 1440 }, // 1440 = 1 inch
          size: { orientation: PageOrientation.LANDSCAPE },
          pageNumbers: { start: 1, formatType: 'decimal' }, // "upperRoman", "lowerRoman", "upperLetter", "lowerLetter"
        },
      },
      headers: {
        default: new Header({
          children: [
            new Paragraph({
              alignment: AlignmentType.RIGHT,
              children: [new TextRun('Header Text')],
            }),
          ],
        }),
      },
      footers: {
        default: new Footer({
          children: [
            new Paragraph({
              alignment: AlignmentType.CENTER,
              children: [
                new TextRun('Page '),
                new TextRun({ children: [PageNumber.CURRENT] }),
                new TextRun(' of '),
                new TextRun({ children: [PageNumber.TOTAL_PAGES] }),
              ],
            }),
          ],
        }),
      },
      children: [
        /* content */
      ],
    },
  ],
});
```

## Tabs

```javascript
new Paragraph({
  tabStops: [
    { type: TabStopType.LEFT, position: TabStopPosition.MAX / 4 },
    { type: TabStopType.CENTER, position: TabStopPosition.MAX / 2 },
    { type: TabStopType.RIGHT, position: (TabStopPosition.MAX * 3) / 4 },
  ],
  children: [new TextRun('Left\tCenter\tRight')],
});
```

## Constants & Quick Reference

- **Underlines:** `SINGLE`, `DOUBLE`, `WAVY`, `DASH`
- **Borders:** `SINGLE`, `DOUBLE`, `DASHED`, `DOTTED`
- **Numbering:** `DECIMAL` (1,2,3), `UPPER_ROMAN` (I,II,III), `LOWER_LETTER` (a,b,c)
- **Tabs:** `LEFT`, `CENTER`, `RIGHT`, `DECIMAL`
- **Symbols:** `"2022"` (•), `"00A9"` (©), `"00AE"` (®), `"2122"` (™), `"00B0"` (°), `"F070"` (✓), `"F0FC"` (✗)

## Critical Issues & Common Mistakes

- **CRITICAL: PageBreak must ALWAYS be inside a Paragraph** - standalone PageBreak creates invalid XML that Word cannot open
- **ALWAYS use ShadingType.CLEAR for table cell shading** - Never use ShadingType.SOLID (causes black background).
- Measurements in DXA (1440 = 1 inch) | Each table cell needs ≥1 Paragraph | TOC requires HeadingLevel styles only
- **ALWAYS use custom styles** with Arial font for professional appearance and proper visual hierarchy
- **ALWAYS set a default font** using `styles.default.document.run.font` - Arial recommended
- **ALWAYS use columnWidths array for tables** + individual cell widths for compatibility
- **NEVER use unicode symbols for bullets** - always use proper numbering configuration with `LevelFormat.BULLET` constant (NOT the string "bullet")
- **NEVER use \n for line breaks anywhere** - always use separate Paragraph elements for each line
- **ALWAYS use TextRun objects within Paragraph children** - never use text property directly on Paragraph
- **CRITICAL for images**: ImageRun REQUIRES `type` parameter - always specify "png", "jpg", "jpeg", "gif", "bmp", or "svg"
- **CRITICAL for bullets**: Must use `LevelFormat.BULLET` constant, not string "bullet", and include `text: "•"` for the bullet character
- **CRITICAL for numbering**: Each numbering reference creates an INDEPENDENT list. Same reference = continues numbering (1,2,3 then 4,5,6). Different reference = restarts at 1 (1,2,3 then 1,2,3). Use unique reference names for each separate numbered section!
- **CRITICAL for TOC**: When using TableOfContents, headings must use HeadingLevel ONLY - do NOT add custom styles to heading paragraphs or TOC will break
- **Tables**: Set `columnWidths` array + individual cell widths, apply borders to cells not table
- **Set table margins at TABLE level** for consistent cell padding (avoids repetition per cell)
