Apply a Word table style

Give a table one of Word's 105 built-in table styles, such as Grid Table 4 or List Table 3, from your code or from an agent.

SuperDoc includes Word's 105 built-in table styles, the ones in Word's Table Design gallery. tables.applyPreset gives a table one of them. In Word, the table uses that named style, and its colors follow the document's theme.

Two operations do the work:

  • tables.listPresets finds a style and returns its preset ID.
  • tables.applyPreset applies it to a table.

Try it

This Node.js script styles the first table in pricing.docx as Grid Table 4 - Accent 1. Install the Node.js SDK, save the script as table-style.ts, and put a DOCX with a table next to it:

import { SuperDocClient } from '@superdoc/sdk';

const client = new SuperDocClient();

try {
  await client.connect();
  const doc = await client.open({ doc: './pricing.docx' });

  try {
    const { blocks } = await doc.blocks.list({ nodeTypes: ['table'] });
    const table = blocks[0];
    if (!table) throw new Error('pricing.docx has no table.');

    const page = await doc.tables.listPresets({ source: 'word', query: 'Grid Table 4' });
    console.log(page.items.map((preset) => `${preset.id}  ${preset.name}`));

    await doc.tables.applyPreset({
      nodeId: table.nodeId,
      preset: 'word.grid-table-4-accent-1',
      styleOptions: { headerRow: true, bandedRows: true, firstColumn: false },
    });

    const { styleId } = await doc.tables.getProperties({ nodeId: table.nodeId });
    console.log('Table style:', styleId);

    await doc.save({ out: './pricing.styled.docx', force: true });
  } finally {
    await doc.close({ discard: true });
  }
} finally {
  await client.dispose();
}
npx tsx table-style.ts

It prints the seven Grid Table 4 presets, applies one, and prints the table's style ID, GridTable4-Accent1. Open pricing.styled.docx in Word to see the result.

The same calls work in the browser editor, through superdoc.activeEditor.doc, and in the other SDKs:

// Browser
const doc = superdoc.activeEditor?.doc;
await doc?.tables.applyPreset({ nodeId, preset: 'word.list-table-3', styleOptions: { headerRow: true } });
# Python
page = document.tables.list_presets({"source": "word", "query": "List Table 3"})
document.tables.apply_preset({"nodeId": node_id, "preset": "word.list-table-3", "styleOptions": {"headerRow": True}})
# CLI
superdoc tables list-presets --query "Grid Table 4"
superdoc tables apply-preset --node-id TABLE_ID --preset word.grid-table-4-accent-1 --style-options-json '{"headerRow":true,"bandedRows":true}'

In Rust, the calls are tables().list_presets(&params) and tables().apply_preset(&params) on doc.api().

nodeId identifies the table. Get it from blocks.list({ nodeTypes: ['table'] }), or in the browser from superdoc.ui.tables.getContext().tableNodeId for the table under the cursor. Read the context right before the call, not from a cached copy.

Find a style

Preset IDs follow Word's style names:

Word stylePreset ID
Table Gridword.table-grid
Table Grid Lightword.grid-table-light
Plain Table 1 to 5word.plain-table-1 to word.plain-table-5
Grid Table 1 Light to Grid Table 7 Colorfulword.grid-table-1-light, -2, -3, -4, -5-dark, -6-colorful, -7-colorful
List Table 1 Light to List Table 7 Colorfulword.list-table-1-light, -2, -3, -4, -5-dark, -6-colorful, -7-colorful
Accent 1 to 6 of a Grid or List TableAdd -accent-1 to -accent-6, for example word.grid-table-4-accent-1

tables.listPresets searches the catalog. It reads no document, so its result is the same for every file:

InputMeaning
sourceword for Word's styles, superdoc for SuperDoc's four presets.
groupplain, grid, list or convenience.
queryMatches IDs, names and appearance terms, such as Grid Table 4, shading banded rows or horizontal borders.
limit1 to 25. The default is 25.
offsetWhere the page starts.

The result has items, the filtered total, offset and nextOffset. A page can hold fewer than limit items, so call again with offset: nextOffset until nextOffset is null. Each item has id, name, source, group, accent (1 to 6, or null) and appearance.

To list the table styles a document already contains, call tables.getStyles instead.

Choose a style by its look

Each Word preset describes how it looks in appearance. This is word.grid-table-4-accent-1:

{
  "header": {
    "treatment": ["shading", "bold", "borders"],
    "fill": { "themeColor": "accent1" },
    "textColor": { "themeColor": "background1" }
  },
  "bandedRows": { "effect": "shading", "fill": { "themeColor": "accent1", "tint": "33" } },
  "borders": { "layout": "grid", "color": { "themeColor": "accent1", "tint": "99" } }
}
FieldValues
header.treatmentAny of shading, bold and borders. fill and textColor give the header's colors.
bandedRows.effectnone, shading, borders or shadingAndBorders. borders adds lines between rows but no fill.
borders.layoutnone, horizontal, outer, outerAndHorizontal or grid.
baseFillPresent when the whole table has a fill.

Colors name a theme slot, not a fixed color. accent1 is blue in Word's default theme, and can be green or any other color in a branded document. To get the real color, read styles.getTheme():

  1. For text1, background1, text2 and background2, look up t1, bg1, t2 or bg2 in theme.colorSchemeMapping to get a slot name. Without a mapping, Word uses dk1, lt1, dk2 and lt2.
  2. For accent1 to accent6, use the mapping's entry when there is one, otherwise the slot with the same name.
  3. Read the slot's hex value from theme.colorScheme.
  4. Apply tint (lighter, toward white) or shade (darker, toward black). Each is a hex byte from 00 to FF, as in Word's w:themeTint and w:themeShade.

appearance covers the header, the first banded row and the main borders. A property it leaves out is inherited, not white or borderless. Direct formatting on cells and text overrides the style.

Turn table regions on and off

styleOptions matches the checkboxes under Table Style Options in Word:

FlagWord checkbox
headerRowHeader Row
lastRowTotal Row
firstColumnFirst Column
lastColumnLast Column
bandedRowsBanded Rows
bandedColumnsBanded Columns

Send every flag you care about. A flag you leave out keeps its current value. If the table has no flags yet, the ones you leave out get Word's defaults, and Word's default turns First Column on.

To change the flags later without applying the style again, call tables.applyStyle({ nodeId, styleOptions }).

What applying a style changes

  • SuperDoc copies Word's definition of the style into the document's styles.xml and points the table at it, in one edit.
  • When the document already has the same definition, SuperDoc reuses it.
  • When the document has its own, customized version of that style, SuperDoc keeps it and installs Word's version under a new ID, such as GridTable4-Accent1-Word. Read the ID the table uses with tables.getProperties.
  • Borders and shading set on the whole table are removed, so the style controls them. Formatting set on individual cells and text stays and overrides the style.
  • Applying the same preset again succeeds and changes nothing. Pass dryRun: true to check a call without changing the document.

Unknown preset IDs fail with INVALID_INPUT and change nothing.

Collaboration and tracked changes

In a collaboration room, every connected editor receives the style definition and the table change together. Undo and redo work on every peer.

Word presets are direct edits. With changeMode: 'tracked', the call fails with CAPABILITY_UNAVAILABLE and changes nothing.

SuperDoc presets

grid, minimal, striped and accent are SuperDoc's own presets. They write borders, banding and header shading directly onto the table instead of applying a named Word style, so their appearance is null. accentColor sets the color of the accent preset.

Word presets reject accentColor with INVALID_INPUT. For a Word style in a theme color, pick one of its -accent-1 to -accent-6 presets.

Let an agent apply a style

Give the model a custom action on the core preset. The model names a Word style, and the action finds it and applies it:

import { createAgentToolkit, defineAction, type BoundDocApi } from '@superdoc/sdk';

type PresetId = Parameters<BoundDocApi['tables']['applyPreset']>[0]['preset'];

const isListedPreset = (id: string, listed: ReadonlyArray<{ id: string }>): id is PresetId =>
  listed.some((item) => item.id === id);

const normalize = (value: string) =>
  value
    .toLowerCase()
    .replace(/[^a-z0-9]+/g, ' ')
    .trim();

export const applyTableStyle = defineAction({
  name: 'superdoc.apply_table_style',
  description:
    'Apply a built-in Word table style to a table. Name the style as Word does, for example "Table Grid", ' +
    '"Plain Table 2", "List Table 3", or "Grid Table 4 - Accent 1". Accent 1-6 follow the document theme colors.',
  input: {
    type: 'object',
    properties: {
      style: { type: 'string', description: 'Word table style name or preset id.' },
      tableOrdinal: { type: 'number', description: '1-based table number. Defaults to 1.' },
      headerRow: { type: 'boolean', description: 'Format the first row as a header. Defaults to true.' },
      bandedRows: { type: 'boolean', description: 'Alternate row formatting. Defaults to true.' },
      firstColumn: { type: 'boolean', description: 'Emphasize the first column. Defaults to false.' },
    },
    required: ['style'],
  },
  run: async (doc, args) => {
    const ordinal = typeof args.tableOrdinal === 'number' ? args.tableOrdinal : 1;
    const { blocks } = await doc.blocks.list({ nodeTypes: ['table'] });
    const table = blocks[ordinal - 1];
    if (!table) throw new Error(`Table ${ordinal} does not exist. The document has ${blocks.length}.`);

    const wanted = normalize(String(args.style));
    const { items } = await doc.tables.listPresets({ source: 'word', query: String(args.style) });
    const preset = items.find((item) => normalize(item.id) === wanted || normalize(item.name) === wanted);
    if (!preset || !isListedPreset(preset.id, items)) {
      const names = items.map((item) => item.name).join(', ');
      throw new Error(`No Word table style is named "${args.style}". Close matches: ${names || 'none'}.`);
    }

    await doc.tables.applyPreset({
      nodeId: table.nodeId,
      preset: preset.id,
      styleOptions: {
        headerRow: args.headerRow !== false,
        bandedRows: args.bandedRows !== false,
        firstColumn: args.firstColumn === true,
      },
    });
    return { tableOrdinal: ordinal, style: preset.name };
  },
});

export const toolkit = await createAgentToolkit({
  provider: 'openai',
  preset: 'core',
  customActions: [applyTableStyle],
});

The model passes a name such as Grid Table 4 - Accent 1. If no style has that name, the action throws an error that lists similar names, when there are any, so the model can try again.

When the request describes a look instead of naming a style, such as "a green header with banded rows", use the full preset. It can read the theme, compare appearance, and apply the best match.

The core preset's built-in style_table action applies SuperDoc's accent look, not a Word style. The MCP server has no action for Word table styles yet.

Limits

  • Tracked changes. A Word preset cannot be recorded as a tracked change.
  • Built-in styles only. The catalog holds Word's built-in styles. To apply a table style the document already defines, pass its ID from tables.getStyles to tables.applyStyle. No operation creates a new table style.
  • Appearance. appearance describes the header, the first banded row and the main borders. It does not describe the total row, the first and last columns, or banded columns.

See the reference for tables.listPresets, tables.applyPreset, tables.getProperties, tables.applyStyle, tables.getStyles and styles.getTheme.

On this page