better docx
Usage

Tables

Paragraphs requires an understanding of Sections.

Intro

  • Tables contain a list of Rows
  • Rows contain a list of TableCells
  • TableCells contain a list of Paragraphs and/or Tables. You can add Tables as tables can be nested inside each other

Create a simple table like so:

const table = new Table({
    rows: [Array of `TableRow`s]
});

Then add the table in the section

const doc = new Document({
    sections: [
        {
            children: [table],
        },
    ],
});

Table

Options

Here is a list of options you can add to the table:

PropertyTypeNotes
rowsArray<TableRow>Required
widthITableWidthPropertiesOptional. Defaults to { size: 100 } (auto)
columnWidthsArray<number>Optional. Width of each grid column in twips
marginsITableCellMarginOptionsOptional. Default cell margins for the whole table
indentITableWidthPropertiesOptional
floatITableFloatOptionsOptional. See Floating tables
layoutTableLayoutTypeOptional. AUTOFIT or FIXED
stylestringOptional. Id of a table style defined in the document
bordersITableBordersOptions | falseOptional. See Table borders
alignmentAlignmentTypeOptional. Aligns the whole table on the page
visuallyRightToLeftbooleanOptional
tableStyleOptionsITableStyleOptionsOptional. See Table Style Options
cellSpacingITableCellSpacingPropertiesOptional. Spacing between cells
shadingIShadingAttributesPropertiesOptional. Background shading for the whole table

Set Width

const table = new Table({
    ...,
    width: {
        size: [TABLE_WIDTH],
        type: WidthType,
    }
});

For example:


const table = new Table({
    ...,
    width: {
        size: 4535,
        type: WidthType.DXA,
    }
});

Depending on the WidthType, size accepts different values:

  • a plain number (twips for WidthType.DXA, converted to "<number>%" for WidthType.PERCENTAGE)
  • a percent string such as "50%"
  • a universal measure string such as "1.5in", "25mm" or "12pt"
const table = new Table({
    ...,
    width: {
        size: "50%",
        type: WidthType.PERCENTAGE,
    }
});

Column Widths

columnWidths sets the width of each grid column in twips (twentieths of a point). Provide one entry per column. This is especially useful together with TableLayoutType.FIXED:

const table = new Table({
    ...,
    columnWidths: [3000, 3000, 3000], // three columns, 3000 twips each
    layout: TableLayoutType.FIXED,
});

If omitted, a default grid is generated with one column per grid column found in the rows.

Layout

layout selects the algorithm Word uses to lay out the table:

  • TableLayoutType.AUTOFIT — columns expand to fit their content
  • TableLayoutType.FIXED — columns keep the widths from the table grid
const table = new Table({
    ...,
    layout: TableLayoutType.FIXED,
});

Alignment

Aligns the whole table on the page using the same AlignmentType used for paragraphs:

const table = new Table({
    ...,
    alignment: AlignmentType.CENTER,
});

Table-wide Cell Margins

margins sets the default cell margins for every cell in the table. Values are in the unit given by marginUnitType (defaults to WidthType.DXA, i.e. twips):

const table = new Table({
    ...,
    margins: {
        marginUnitType: WidthType.DXA,
        top: 100,
        bottom: 100,
        left: 150,
        right: 150,
    },
});

Individual cells can override these with their own margins option.

Cell Spacing

cellSpacing adds spacing between the cells of the table:

const table = new Table({
    ...,
    cellSpacing: {
        value: 100,
        type: CellSpacingType.DXA, // the default; CellSpacingType.NIL removes spacing
    },
});

Cell spacing can also be set per row — see the table row options.

Style

style references a table style by its id. This is useful when working with an existing template (e.g. via externalStyles) that defines table styles such as Word's built-in "TableGrid":

const table = new Table({
    ...,
    style: "TableGrid",
});

Table Borders

Table-level borders are set with the borders option. In addition to the four outer sides, tables support insideHorizontal and insideVertical for the lines between cells:

const table = new Table({
    ...,
    borders: {
        top: { style: BorderStyle.DOUBLE, size: 6, color: "ff0000" },
        bottom: { style: BorderStyle.DOUBLE, size: 6, color: "ff0000" },
        left: { style: BorderStyle.SINGLE, size: 4, color: "auto" },
        right: { style: BorderStyle.SINGLE, size: 4, color: "auto" },
        insideHorizontal: { style: BorderStyle.DASHED, size: 2, color: "999999" },
        insideVertical: { style: BorderStyle.DASHED, size: 2, color: "999999" },
    },
});

Note: Any side you leave out (including when borders is omitted entirely) falls back to a default single border (BorderStyle.SINGLE, size 4, color "auto"). To create a borderless table, use the TableBorders.NONE preset, which sets all six sides to BorderStyle.NONE:

const table = new Table({
    ...,
    borders: TableBorders.NONE,
});

Inheriting borders from a table style

Direct borders take precedence over the table style referenced by style — and that includes BorderStyle.NONE, which is still a direct border, just an invisible one. So neither omitting sides nor TableBorders.NONE will let a style's own borders show through.

To emit no tblBorders at all and let the style own the borders, pass borders: false:

const table = new Table({
    ...,
    style: "TableGrid",
    borders: false,
});

borders: false and TableBorders.NONE are not the same thing. TableBorders.NONE writes six explicit none borders, which override the style and give you a borderless table. borders: false writes nothing, letting whatever the style defines apply.

Floating Tables

By default a table is placed inline with the surrounding text. The float option turns it into a floating table that is positioned freely on the page, with text flowing around it.

const table = new Table({
    rows: [...],
    float: {
        horizontalAnchor: TableAnchorType.MARGIN,
        verticalAnchor: TableAnchorType.MARGIN,
        relativeHorizontalPosition: RelativeHorizontalPosition.RIGHT,
        relativeVerticalPosition: RelativeVerticalPosition.BOTTOM,
        overlap: OverlapType.NEVER,
        leftFromText: 1000,
        rightFromText: 2000,
        topFromText: 1500,
        bottomFromText: 30,
    },
    width: {
        size: 4535,
        type: WidthType.DXA,
    },
    layout: TableLayoutType.FIXED,
});

Float Options

PropertyTypeNotes
horizontalAnchorTableAnchorTypeOptional. Base object for horizontal positioning: MARGIN, PAGE or TEXT. Assumed page if omitted
verticalAnchorTableAnchorTypeOptional. Base object for vertical positioning: MARGIN, PAGE or TEXT. Assumed page if omitted
absoluteHorizontalPositionnumber | UniversalMeasureOptional. Absolute horizontal offset from the anchor, in twips (may be negative) or a measure like "1in"
relativeHorizontalPositionRelativeHorizontalPositionOptional. CENTER, INSIDE, LEFT, OUTSIDE or RIGHT. Supersedes absoluteHorizontalPosition
absoluteVerticalPositionnumber | UniversalMeasureOptional. Absolute vertical offset from the anchor, in twips (may be negative) or a measure like "1in"
relativeVerticalPositionRelativeVerticalPositionOptional. CENTER, INSIDE, BOTTOM, OUTSIDE, INLINE or TOP. Supersedes absoluteVerticalPosition
topFromTextnumber | PositiveUniversalMeasureOptional. Minimum distance between the table and the text above it, in twips
bottomFromTextnumber | PositiveUniversalMeasureOptional. Minimum distance between the table and the text below it, in twips
leftFromTextnumber | PositiveUniversalMeasureOptional. Minimum distance between the table and the text to its left, in twips
rightFromTextnumber | PositiveUniversalMeasureOptional. Minimum distance between the table and the text to its right, in twips
overlapOverlapTypeOptional. NEVER or OVERLAP — whether other floating tables may overlap this one

Set Indent

const table = new Table({
    ...,
    indent: {
        size: 600,
        type: WidthType.DXA,
    }
});

Table Row

A table consists of multiple table rows. Table rows have a list of children which accepts a list of table cells explained below. You can create a simple table row like so:

const tableRow = new TableRow({
    children: [
        new TableCell({
            children: [new Paragraph("hello")],
        }),
    ],
});

Or preferably, add the tableRow directly into the table without declaring a variable:

const table = new Table({
    rows: [
        new TableRow({
            children: [
                new TableCell({
                    children: [new Paragraph("hello")],
                }),
            ],
        }),
    ],
});

Options

Here is a list of options you can add to the table row:

PropertyTypeNotes
childrenArray<TableCell>Required
cantSplitbooleanOptional
tableHeaderbooleanOptional
height{ value: number | PositiveUniversalMeasure, rule: HeightRule }Optional
cellSpacingITableCellSpacingPropertiesOptional. Cell spacing for this row only

height.value accepts a number in twips or a universal measure string:

const row = new TableRow({
    ...,
    height: {
        value: "0.5in",
        rule: HeightRule.EXACT,
    },
});

A row can also override the table-wide cell spacing:

const row = new TableRow({
    ...,
    cellSpacing: {
        value: 50,
        type: CellSpacingType.DXA,
    },
});

Repeat row

If a table is paginated on multiple pages, it is possible to repeat a row at the top of each new page by setting tableHeader to true:

const row = new TableRow({
    ...,
    tableHeader: true,
});

Pagination

Prevent row pagination

To prevent breaking contents of a row across multiple pages, call cantSplit:

const row = new Row({
    ...,
    cantSplit: true,
});

Table Cells

Cells need to be added in the table row, you can create a table cell like:

const tableCell = new TableCell({
    children: [new Paragraph("hello")],
});

Or preferably, add the tableRow directly into the table row without declaring a variable:

const tableRow = new TableRow({
    children: [
        new TableCell({
            children: [new Paragraph("hello")],
        }),
    ],
});

Note: A cell must end with a paragraph. If the last child of a cell is not a Paragraph (for example a nested Table, or an empty children array), an empty paragraph is appended to the cell automatically when the document is generated.

Options

PropertyTypeNotes
childrenArray<Paragraph or Table>Required. You can nest tables by adding a table into a cell
shadingIShadingAttributesPropertiesOptional
marginsITableCellMarginOptionsOptional
verticalAlignVerticalAlignTableOptional
textDirectionTextDirectionOptional. Rotates the text inside the cell
verticalMergeVerticalMergeTypeOptional. Low-level vertical merge; prefer rowSpan
columnSpannumberOptional
rowSpannumberOptional
bordersITableCellBordersOptional
widthITableWidthPropertiesOptional

Border Options

PropertyTypeNotes
top{ style: BorderStyle, size: number, color: string }Optional
bottom{ style: BorderStyle, size: number, color: string }Optional
left{ style: BorderStyle, size: number, color: string }Optional
right{ style: BorderStyle, size: number, color: string }Optional
start{ style: BorderStyle, size: number, color: string }Optional. Logical start edge (text-direction aware)
end{ style: BorderStyle, size: number, color: string }Optional. Logical end edge (text-direction aware)
Example
const cell = new TableCell({
    ...,
    borders: {
        top: {
            style: BorderStyle.DASH_DOT_STROKED,
            size: 1,
            color: "ff0000",
        },
        bottom: {
            style: BorderStyle.THICK_THIN_MEDIUM_GAP,
            size: 5,
            color: "889900",
        },
    },
});
Google DOCS

Google DOCS does not support start and end borders, instead they use left and right borders. So to set left and right borders for Google DOCS you should use:

const cell = new TableCell({
    ...,
    borders: {
        left: {
            style: BorderStyle.DOT_DOT_DASH,
            size: 3,
            color: "00FF00",
        },
        right: {
            style: BorderStyle.DOT_DOT_DASH,
            size: 3,
            color: "ff8000",
        },
    },
});

Add paragraph to a cell

Once you have got the cell, you can add data to it:

const cell = new TableCell({
    children: [new Paragraph("Hello")],
});

Set width of a cell

You can specify the width of a cell using:

const cell = new TableCell({
    ...,
    width: {
        size: number,
        type: WidthType,
    },
});

WidthType values can be:

PropertyNotes
AUTO
DXAValue is in twentieths of a point
NILIs considered as zero
PERCENTAGEPercent of table width

As with the table width, size accepts a plain number, a percent string ("25%") or a universal measure string ("1.5in"), depending on the WidthType.

Cell Margins

A cell can override the table-wide cell margins. Values are in the unit given by marginUnitType (defaults to WidthType.DXA, i.e. twips):

const cell = new TableCell({
    ...,
    margins: {
        marginUnitType: WidthType.DXA,
        top: 200,
        bottom: 200,
        left: 200,
        right: 200,
    },
});

Text Direction

textDirection rotates the text inside a cell, e.g. for vertical header cells:

const cell = new TableCell({
    ...,
    textDirection: TextDirection.BOTTOM_TO_TOP_LEFT_TO_RIGHT,
});

TextDirection values can be:

PropertyNotes
LEFT_TO_RIGHT_TOP_TO_BOTTOMNormal horizontal text. The default
BOTTOM_TO_TOP_LEFT_TO_RIGHTText rotated 90° counter-clockwise
TOP_TO_BOTTOM_RIGHT_TO_LEFTText rotated 90° clockwise

Nested Tables

To have a table within a table, simply add it in the children block of a table cell:

const cell = new TableCell({
    children: [new Table(...)],
});

Vertical Align

Sets the vertical alignment of the contents of the cell

const cell = new TableCell({
    ...,
    verticalAlign: VerticalAlignTable,
});

VerticalAlignTable values can be:

PropertyNotes
BOTTOMAlign the contents on the bottom
CENTERAlign the contents on the center
TOPAlign the contents on the top. The default

For example, to center align a cell:

const cell = new TableCell({
    verticalAlign: VerticalAlignTable.CENTER,
});

Note: The old VerticalAlign export is deprecated. Use VerticalAlignTable for table cells (VerticalAlign aliases VerticalAlignSection, which additionally contains BOTH — a value that is only valid for section properties, not table cells).

Merging cells together

Row Merge

When cell rows are merged, it counts as multiple rows, so be sure to remove excess cells. It is similar to how HTML's rowspan works. https://www.w3schools.com/tags/att_td_rowspan.asp

const cell = new TableCell({
    ...,
    rowSpan: [NUMBER_OF_CELLS_TO_MERGE],
});

Example

The example will merge three rows together.

const cell = new TableCell({
    ...,
    rowSpan: 3,
});

Manual vertical merge

rowSpan is a convenience: internally, the first cell is marked with VerticalMergeType.RESTART and continuation cells are inserted into the following rows with VerticalMergeType.CONTINUE for you. If you need full control (for example when constructing every row yourself), you can set verticalMerge directly instead:

// First row: the cell that starts the merge
new TableCell({
    children: [new Paragraph("merged")],
    verticalMerge: VerticalMergeType.RESTART,
});

// Following rows: cells that continue the merge
new TableCell({
    children: [],
    verticalMerge: VerticalMergeType.CONTINUE,
});

If both are set, verticalMerge takes precedence over rowSpan for the cell's own merge marker, so avoid mixing the two on the same cell.

Column Merge

When cell columns are merged, it counts as multiple columns, so be sure to remove excess cells. It is similar to how HTML's colspan works. https://www.w3schools.com/tags/att_td_colspan.asp

const cell = new TableCell({
    ...,
    columnSpan: [NUMBER_OF_CELLS_TO_MERGE],
});

Example

The example will merge three columns together.

const cell = new TableCell({
    ...,
    columnSpan: 3,
});

Visual Right to Left Table

It is possible to reverse how the cells of the table are displayed. The table direction. More info here: https://superuser.com/questions/996912/how-to-change-a-table-direction-in-microsoft-word

const table = new Table({
    visuallyRightToLeft: true,
});

Table Style Options (Conditional Formatting)

Table styles can define special formatting for the header row, the first column, banded rows, and so on. tableStyleOptions decides which of those the table actually uses. These are the same six checkboxes Word shows in the Table Style Options group of the Table Design tab, and each one is true when the box is checked:

const table = new Table({
    style: "GridTable5Dark-Accent3",
    tableStyleOptions: {
        headerRow: true, // Header Row: checked
        totalRow: true, // Total Row: checked
        bandedRows: false, // Banded Rows: unchecked
    },
});

Because tableStyleOptions only selects parts of a table style, it has no visible effect on a table without a style.

Mapping to Word's checkboxes

Word checkboxPropertyDefault
Header RowheaderRowtrue
Total RowtotalRowfalse
Banded RowsbandedRowstrue
First ColumnfirstColumntrue
Last ColumnlastColumnfalse
Banded ColumnsbandedColumnsfalse

The underlying w:tblLook element stores banding as the negative attributes noHBand and noVBand. bandedRows and bandedColumns follow Word's checkboxes instead and are inverted on the way out, so you never have to write a double negative.

Header Row is not "Repeat Header Rows"

headerRow decides whether the style paints its header band. It is not the same as Word's Repeat Header Rows (on the Layout tab), which repeats a row across page breaks — that is tableHeader on TableRow. Word keeps the two apart and so does this library: setting one never implies the other.

Defaults

The defaults above match Word's own: Header Row, First Column and Banded Rows checked; Total Row, Last Column and Banded Columns unchecked. In OOXML terms that is the bitmask 0x04A0.

They are applied whenever you omit tableStyleOptions, and any options you do pass are merged on top rather than replacing the whole set — so tableStyleOptions: { totalRow: true } turns on the Total Row and leaves the other five at their defaults.

The spec and Word disagree here, which is why the defaults are always written out explicitly. ECMA-376 says a table with no tblLook element gets 0x0000 (everything off), but Word instead assumes 0x04A0 (MS-OI29500 §17.4.55). Emitting the values keeps every renderer agreeing with what Word displays.

To opt out of a default, set it explicitly — for example tableStyleOptions: { headerRow: false } to drop the header row formatting.

Examples

Example

Source: https://github.com/dolanmiu/docx/blob/master/demo/4-basic-table.ts

Custom borders

Example showing how to add colorful borders to tables

Example

Source: https://github.com/dolanmiu/docx/blob/master/demo/20-table-cell-borders.ts

Adding images

Example showing how to add images to tables

Example

Source: https://github.com/dolanmiu/docx/blob/master/demo/24-images-to-table-cell.ts

Example

Source: https://github.com/dolanmiu/docx/blob/master/demo/36-image-to-table-cell.ts

Alignment of text in a cell

Example showing how align text in a table cell

Example

Source: https://github.com/dolanmiu/docx/blob/master/demo/31-tables.ts

Shading

Example showing merging of columns and rows and shading

Example

Source: https://github.com/dolanmiu/docx/blob/master/demo/32-merge-and-shade-table-cells.ts

Example

Source: https://github.com/dolanmiu/docx/blob/master/demo/41-merge-table-cells-2.ts

Merging columns

Example showing merging of columns and rows

Example

Source: https://github.com/dolanmiu/docx/blob/master/demo/43-images-to-table-cell-2.ts

On this page