How to Create New DOCX Documents Using docx-js: A Complete Guide

The docx-js skill in the ComposioHQ/awesome-claude-skills repository provides a production-ready wrapper around the docx JavaScript library, enabling server-side generation of Word-compatible .docx files through a three-layer architecture of setup, document construction, and export.

Creating new DOCX documents using docx-js within the ComposioHQ/awesome-claude-skills ecosystem requires understanding its structured API patterns. This skill leverages the open-source docx library to generate complex Word documents entirely in Node.js or the browser, supporting professional formatting, tables, images, and Table of Contents generation according to the specifications in document-skills/docx/docx-js.md.

Three-Layer Architecture

The docx-js skill implements a structured approach to document generation consisting of three distinct layers.

Setup Layer

Install the dependency globally to ensure availability across the project. The skill expects the docx package to be installed via npm and all required classes to be imported in a single destructured require statement.

npm install -g docx
const {
    Document, Packer, Paragraph, TextRun,
    HeadingLevel, AlignmentType, PageBreak,
    Table, TableRow, TableCell, WidthType, BorderStyle,
    Numbering, LevelFormat, ImageRun, Header, Footer,
    PageNumber, PageOrientation, TableOfContents,
    ExternalHyperlink, InternalHyperlink
} = require('docx');

Document Construction Layer

Documents are built from a Document object containing one or more sections. Each section houses an array of children—Paragraphs, Tables, Images, and other elements—that form the document content.

const doc = new Document({
    sections: [{
        properties: {
            page: { margin: { top: 1440, right: 1440, bottom: 1440, left: 1440 } }
        },
        children: [
            // Content elements go here
        ]
    }]
});

Export Layer

The Packer class serializes the in-memory Document into a Buffer (Node.js) or Blob (browser). This binary data is then written to disk or offered for download.

Packer.toBuffer(doc).then(buffer => {
    fs.writeFileSync("MyDocument.docx", buffer);
});

Document Structure and Content

Creating rich DOCX documents requires precise construction of content elements. The docx-js skill enforces specific patterns for text, headings, and structural elements.

Paragraphs and TextRuns

TextRun objects contain the actual text content, while Paragraph elements wrap them and control alignment, spacing, and list properties. According to the source code in document-skills/docx/docx-js.md, you must never embed newline characters (\n) inside a TextRun. Each line break requires a separate Paragraph instance.

// Correct: Multiple paragraphs for line breaks
new Paragraph({ children: [new TextRun("First line")] }),
new Paragraph({ children: [new TextRun("Second line")] })

// Incorrect: Newline inside TextRun (creates malformed XML)
new TextRun({ text: "Line 1\nLine 2" })

Headings and Styles

To ensure Table of Contents functionality works correctly, override built-in heading styles using exact IDs (Heading1, Heading2, etc.) and set the outlineLevel property. The document-level styles configuration defines default fonts and custom paragraph styles.

const doc = new Document({
    styles: {
        default: { document: { run: { font: "Arial", size: 24 } } },
        paragraphStyles: [
            { 
                id: "Heading1", 
                name: "Heading 1", 
                basedOn: "Normal",
                run: { size: 32, bold: true },
                paragraph: { outlineLevel: 0, spacing: { before: 240, after: 240 } }
            }
        ]
    },
    sections: [{
        children: [
            new Paragraph({
                heading: HeadingLevel.HEADING_1,
                children: [new TextRun("Introduction")]
            })
        ]
    }]
});

Lists, Tables, and Images

Complex document elements require specific configuration objects to render correctly in Microsoft Word.

Configuring Lists

Numbered and bulleted lists must be defined via the numbering configuration in the Document constructor. Unicode bullet characters are ignored by Word; instead, reference a defined numbering style.

const doc = new Document({
    numbering: {
        config: [
            {
                reference: "bullet-list",
                levels: [{
                    level: 0,
                    format: LevelFormat.BULLET,
                    text: "•",
                    style: { paragraph: { indent: { left: 720, hanging: 360 } } }
                }]
            }
        ]
    },
    sections: [{
        children: [
            new Paragraph({
                numbering: { reference: "bullet-list", level: 0 },
                children: [new TextRun("First bullet")]
            })
        ]
    }]
});

Building Tables

Tables require both a columnWidths array at the table level and a width object on each TableCell. Cell borders are applied per-cell, not per-table, requiring explicit border definitions for each side.

new Table({
    columnWidths: [4680, 4680],
    rows: [
        new TableRow({
            children: [
                new TableCell({
                    width: { size: 4680, type: WidthType.DXA },
                    borders: {
                        top: { style: BorderStyle.SINGLE, size: 1, color: "CCCCCC" },
                        bottom: { style: BorderStyle.SINGLE, size: 1, color: "CCCCCC" },
                        left: { style: BorderStyle.SINGLE, size: 1, color: "CCCCCC" },
                        right: { style: BorderStyle.SINGLE, size: 1, color: "CCCCCC" }
                    },
                    shading: { fill: "D5E8F0", type: "clear" },
                    children: [new Paragraph({ children: [new TextRun({ text: "Header", bold: true })] })]
                }),
                // Additional cells...
            ]
        })
    ]
})

Inserting Images

Images must include a mandatory type field specifying the format (png, jpg, etc.) and a fully populated altText object containing title, description, and name properties.

new Paragraph({
    alignment: AlignmentType.CENTER,
    children: [
        new ImageRun({
            type: "png",
            data: fs.readFileSync("logo.png"),
            transformation: { width: 150, height: 100 },
            altText: { 
                title: "Company Logo", 
                description: "Logo of the company", 
                name: "logo" 
            }
        })
    ]
})

Page Breaks and Table of Contents

Page breaks must be wrapped in a Paragraph element. A bare PageBreak() produces malformed XML that Word cannot open. Similarly, a TableOfContents relies on properly configured heading styles with outlineLevel settings to generate correctly.

// Correct page break implementation
new Paragraph({ children: [new PageBreak()] }),

// Table of Contents (requires HeadingLevel usage in document)
new TableOfContents("Table of Contents", {
    hyperlink: true,
    headingStyleRange: "1-3"
})

Complete Implementation Example

The following example from document-skills/docx/docx-js.md demonstrates the complete workflow combining styles, headings, formatted text, lists, tables, images, and a Table of Contents:

const fs = require('fs');
const {
    Document, Packer, Paragraph, TextRun,
    HeadingLevel, AlignmentType, PageBreak,
    Table, TableRow, TableCell, WidthType, BorderStyle,
    LevelFormat, ImageRun, TableOfContents
} = require('docx');

const doc = new Document({
    styles: {
        default: { document: { run: { font: "Arial", size: 24 } } },
        paragraphStyles: [
            { id: "Title", name: "Title", basedOn: "Normal",
              run: { size: 56, bold: true },
              paragraph: { spacing: { before: 240, after: 120 }, alignment: AlignmentType.CENTER } },
            { id: "Heading1", name: "Heading 1", basedOn: "Normal",
              run: { size: 32, bold: true },
              paragraph: { outlineLevel: 0, spacing: { before: 240, after: 240 } } }
        ]
    },
    sections: [{
        properties: {
            page: { margin: { top: 1440, right: 1440, bottom: 1440, left: 1440 } }
        },
        children: [
            new Paragraph({
                heading: HeadingLevel.TITLE,
                children: [new TextRun("My First DOCX Document")]
            }),
            new Paragraph({
                heading: HeadingLevel.HEADING_1,
                children: [new TextRun("Introduction")]
            }),
            new Paragraph({
                alignment: AlignmentType.JUSTIFY,
                children: [
                    new TextRun({ text: "Bold text", bold: true }),
                    new TextRun({ text: " – normal text – " }),
                    new TextRun({ text: "Red underline", underline: { type: "single", color: "FF0000" }, color: "FF0000" })
                ]
            }),
            new Paragraph({ children: [new PageBreak()] }),
            new Paragraph({
                numbering: { reference: "bullet-list", level: 0 },
                children: [new TextRun("First bullet")]
            }),
            new Table({
                columnWidths: [4680, 4680],
                rows: [
                    new TableRow({
                        tableHeader: true,
                        children: [
                            new TableCell({
                                width: { size: 4680, type: WidthType.DXA },
                                borders: { top: { style: BorderStyle.SINGLE, size: 1, color: "CCCCCC" },
                                           bottom: { style: BorderStyle.SINGLE, size: 1, color: "CCCCCC" },
                                           left: { style: BorderStyle.SINGLE, size: 1, color: "CCCCCC" },
                                           right: { style: BorderStyle.SINGLE, size: 1, color: "CCCCCC" } },
                                shading: { fill: "D5E8F0", type: "clear" },
                                children: [new Paragraph({ children: [new TextRun({ text: "Header", bold: true })] })]
                            }),
                            new TableCell({
                                width: { size: 4680, type: WidthType.DXA },
                                borders: { top: { style: BorderStyle.SINGLE, size: 1, color: "CCCCCC" },
                                           bottom: { style: BorderStyle.SINGLE, size: 1, color: "CCCCCC" },
                                           left: { style: BorderStyle.SINGLE, size: 1, color: "CCCCCC" },
                                           right: { style: BorderStyle.SINGLE, size: 1, color: "CCCCCC" } },
                                shading: { fill: "D5E8F0", type: "clear" },
                                children: [new Paragraph({ children: [new TextRun({ text: "Data" })] })]
                            })
                        ]
                    })
                ]
            }),
            new Paragraph({
                alignment: AlignmentType.CENTER,
                children: [
                    new ImageRun({
                        type: "png",
                        data: fs.readFileSync("logo.png"),
                        transformation: { width: 150, height: 100 },
                        altText: { title: "Company Logo", description: "Logo of the company", name: "logo" }
                    })
                ]
            }),
            new TableOfContents("Table of Contents", {
                hyperlink: true,
                headingStyleRange: "1-3"
            })
        ]
    }]
});

Packer.toBuffer(doc).then(buffer => {
    fs.writeFileSync("MyDocument.docx", buffer);
    console.log("Document created → MyDocument.docx");
});

Key Source Files

The docx-js implementation references several critical files in the repository:

Summary

  • The docx-js skill uses a three-layer architecture: setup (npm install), construction (Document/Section/Children), and export (Packer.toBuffer)
  • Never use \n inside TextRun objects; create separate Paragraph instances for line breaks
  • Tables require columnWidths at the table level and width objects with per-cell BorderStyle definitions
  • Images must include type and complete altText objects to pass Word validation
  • Page breaks must be wrapped in Paragraph elements, not used standalone
  • Tables of Contents require outlineLevel configuration in paragraph styles to populate correctly

Frequently Asked Questions

How do I install docx-js for use with Claude skills?

Install the docx package globally using npm install -g docx, then import all required classes (Document, Packer, Paragraph, TextRun, etc.) via require('docx') at the top of your skill file. The awesome-claude-skills repository expects this global installation pattern for the document generation skills.

Why are my Unicode bullets not rendering in the generated DOCX file?

Microsoft Word ignores Unicode bullet characters in favor of structured numbering definitions. You must define bulleted lists using the numbering configuration in the Document constructor, referencing a LevelFormat.BULLET style, then apply that reference to Paragraph elements via the numbering property.

What causes malformed XML errors when adding page breaks?

Passing a bare PageBreak() object without wrapping it in a Paragraph produces invalid XML that Word cannot parse. Always wrap page breaks in a Paragraph constructor: new Paragraph({ children: [new PageBreak()] }).

How do I ensure the Table of Contents populates correctly?

The TableOfContents element requires properly configured heading styles with explicit outlineLevel values (0 for Heading 1, 1 for Heading 2, etc.). Use the exact style IDs Heading1, Heading2, etc., and ensure your sections use HeadingLevel.HEADING_1 and similar constants rather than plain text styling.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →