Tables and cells

Create rectangular tables, edit values, add or delete rows and columns, and format cell bodies.

Use Body.tables or Range.tables to discover tables in the addressed scope. Table, TableRow, and TableCell are proxies with separate collections. You can edit rectangular tables. Structural edits can fail on merged or otherwise unsupported tables.

Create a runtime with Runtime and setup. Use context and document proxies inside runtime.run().

Insert a table

Range.insertTable(rowCount, columnCount, location, values?) accepts Before or After. Counts must be positive integers. Supplied values must form a matching rectangular string matrix. Resolve the range, create the table, then configure it after sync:

await runtime.run(async (context) => {
  const paragraph = context.document.paragraphs.getFirst();
  paragraph.load('text');
  await context.sync();
  const anchor = paragraph.getRange('Content');
  await context.sync();
  const table = anchor.insertTable(2, 2, 'After', [
    ['Item', 'Amount'],
    ['License', '$100'],
  ]);
  await context.sync();
  table.headerRowCount = 1;
  await context.sync();
});

The returned Table requires a completed sync before dependent operations. Table insertion is not a Body.insertTable() operation in this subset. Use a body paragraph's range as the anchor.

Read table values and cells

Load scalar table properties explicitly:

await runtime.run(async (context) => {
  const table = context.document.body.tables.getFirstOrNullObject();
  table.load(['values', 'rowCount', 'columnCount']);
  await context.sync();
  if (table.isNullObject) return;
  console.log(table.values);

  const cell = table.getCell(0, 0);
  cell.load('value');
  await context.sync();
  console.log(cell.value);
});

Table.getCell(rowIndex, cellIndex) uses zero-based indexes. Table.rows returns TableRowCollection; TableRow.cells returns TableCellCollection. All three table collections expose items, getFirst(), and getFirstOrNullObject(). Load collection membership before reading item properties.

TableCell.body exposes the cell's Body for text, paragraphs, search, and formatting. Resolve the cell and its body before dependent navigation. To preserve rich cell content, edit an exact range within that body. Whole-cell replacement supports ordinary single-paragraph text and preserves its existing font formatting. The runtime rejects multiline values, nested blocks, and complex cell content. Cell-body font reads remain scoped to that cell. You can navigate existing nested rectangular tables, but cannot create them.

Edit values and structure

MemberBehavior
Table.valuesRead or replace the table's string matrix
TableCell.valueRead or replace one cell's plain text
Table.rowCount, Table.columnCountLoaded, read-only dimensions
Table.addRows(location, rowCount, values?)Add rows at Start or End; return TableRowCollection
TableRow.insertRows(location, rowCount, values?)Add rows Before or After this row; return TableRowCollection
Table.addColumns(location, columnCount, values?)Add columns at Start or End; return void
Table.deleteRows(rowIndex, rowCount?)Delete rows from a zero-based index; default count is one
Table.deleteColumns(columnIndex, columnCount?)Delete columns from a zero-based index; default count is one
Table.delete()Delete the table and clean up its references

Value matrices must match the affected row and column dimensions. Do not assume ragged matrices or implicit resizing work. Deleting all rows or all columns removes the table. Table.deleteRows() works on merged tables. If a vertical merge starts in a deleted row, the next row starts the merge. If no later row continues the merge, the next row is no longer merged. If the row above the deleted row has a merged cell over the same grid columns, the next row joins that merge instead. Row deletion also works on tables whose rows have different cell counts. The deleted row's content is removed. In TrackMineOnly mode, merges change only when the deletion is accepted. If a cell or row cannot be placed in the table grid, the deletion refuses with InvalidArgument. Table.delete() can remove an existing merged table. Deletion can still fail if the table is protected or its surrounding structure is unsupported. Sync structural changes before addressing newly added cells or relying on changed indexes.

Row insertion preserves unrelated merged headers when the source row has ordinary cells covering the table grid. addRows() uses the first or last row as its source. insertRows() uses the addressed row. Merged source rows, row offsets, hidden source cells, and boundaries crossing vertical merges refuse with InvalidArgument. Tables with merges require an explicit grid. Other table mutations retain their documented restrictions.

For example, insert before the first ordinary row after a merged header:

const table = context.document.body.tables.getFirst();
table.rows.load('items');
await context.sync();
const source = table.rows.items[1];
if (!source) throw new Error('Expected an ordinary row after the header');
source.insertRows('Before', 1, [['Item', 'Price']]);
await context.sync();

Format tables and cells

Table.style uses an existing table style name. Table.headerRowCount sets the number of leading header rows, from zero through the row count. Zero removes the header flags. Cell formatting exposes these properties:

TableCell propertyDomain
columnWidthSupported finite width in points
shadingColorSix-digit RGB hex, optional #; auto removes direct shading
verticalAlignmentTop, Center, or Bottom; the runtime rejects Mixed on writes

VerticalAlignment exports the corresponding enum values. For character or paragraph formatting, use cell.body.font or cell.body.paragraphs. A width write changes the entire grid column, matching cell widths, and total table width. It also sets fixed table layout. The runtime rejects width changes for tables with missing grids or merged cells.

For example, format a resolved header cell:

cell.shadingColor = '#EEEEEE';
cell.verticalAlignment = 'Center';
await context.sync();

Handle unsupported structures

Structural edits can fail on merged grids, protected content, nested preserved structures, and pending revisions. Adding columns or deleting some columns requires an existing table grid. Deleting all columns can still remove a table without a grid. A matching TypeScript signature does not guarantee an edit works on every table. Table value replacement does not silently discard unsupported cell content. Inspect and edit a smaller cell-body range when that preserves the intended structure.

Complete table insertion, cell value replacement, row additions, and partial row deletions support native revisions. An author can configure a complete proposed table while it has no foreign revisions. Existing table properties and columns require permanent edits. Tracked table and cell value replacement refuse in collaboration. Collaborative tracked range edits must remain within one paragraph. Tracked text and font edits can operate inside eligible cell paragraphs. A pending tracked row insertion or deletion blocks tracked text edits throughout that row. See Tracked changes.

Each row or column addition or deletion must run alone in its sync, even across separate tables. Do not combine it with cell values or formatting. Separate other dependent structural operations into distinct syncs. If a batch returns ConflictingChanges, read the table again before retrying a revised operation.

Row suggestions

Set document.changeTrackingMode = 'TrackMineOnly' before Table.addRows(), TableRow.insertRows(), or Table.deleteRows(). Added rows can include initial cell values. The runtime saves native w:trPr/w:ins and w:trPr/w:del revisions.

Save and reopen preserve the suggestions. Use body.revisions.acceptAll() or body.revisions.rejectAll() to resolve complete tracked rows. The revision collection omits row revisions from items.

Delete fewer than all rows when you suggest a deletion. Deleting every row refuses in tracking mode. Changing columns and table properties requires an eligible complete proposed table. Row insertion retains the source-row and vertical-merge limits described earlier. The runtime never changes tracking mode to complete an unsupported edit.

Each deletion must leave a row without a pending deletion. Existing deletion suggestions do not count as surviving rows. Independent concurrent deletions can combine to mark every row. Bulk accept and reject resolve that combined result after save and reopen.

Tracked deletion refuses rows with pending row or cell structure revisions with NotImplemented. Resolve those revisions before suggesting another deletion.

Next steps

See Batching, loading, and errors and API member directory.