Skip to content

Columns & Layout

Column Options

Every column accepts these configuration options:

OptionTypeDefaultDescription
namestringDisplay name shown in the header
keystringsame as nameKey used to look up data in row objects
widthnumberautoFixed column width in characters
minWidthnumberMinimum width (used with auto-sizing)
maxWidthnumberMaximum width before truncation
align'left' | 'right' | 'center''left'Horizontal text alignment
colorColorNameANSI color for cell content
formatter(value: any, rowIndex: number) => stringTransform the cell value before rendering
hiddenbooleanfalseHide this column from output
prioritynumber0Responsive priority (higher = hidden first)
wrapWordbooleanfalseEnable word wrapping for long content
truncatestring'…'Truncation character when content overflows
paddingLeftnumber1Left padding (spaces)
paddingRightnumber1Right padding (spaces)

Examples

Basic Column Definition

ts
const table = new Table();

// String shorthand — name and key are both 'Name'
table.addColumn('Name');

// Full config object
table.addColumn({
    name: 'Salary',
    key: 'salary',
    align: 'right',
    color: 'green',
    width: 12
});

Constructor Columns

Define all columns at once:

ts
const table = new Table({
    columns: [
        { name: 'ID', width: 5, align: 'right' },
        { name: 'Name', color: 'cyan' },
        { name: 'Department', width: 15 },
        { name: 'Salary', align: 'right', color: 'green' }
    ]
});

Replacing Columns

Use setColumns() to replace all columns after construction:

ts
table.setColumns([
    { name: 'Product', key: 'product' },
    { name: 'Price', key: 'price', align: 'right' }
]);

Text Alignment

ts
const table = new Table();
table.addColumn({ name: 'Left', align: 'left', width: 15 });   // Default
table.addColumn({ name: 'Center', align: 'center', width: 15 });
table.addColumn({ name: 'Right', align: 'right', width: 15 });

table.addRow({ Left: 'Hello', Center: 'Hello', Right: 'Hello' });

Expected output:

╭─────────────────┬─────────────────┬─────────────────╮
│ Left            │     Center      │           Right │
├─────────────────┼─────────────────┼─────────────────┤
│ Hello           │      Hello      │           Hello │
╰─────────────────┴─────────────────┴─────────────────╯

Word Wrapping & Truncation

Truncation (Default)

When content exceeds the column width, it's truncated with :

ts
table.addColumn({ name: 'Description', width: 15, truncate: '…' });
table.addRow({ Description: 'This is a very long description that will be cut' });
// Output: "This is a ve…"

Word Wrap

Enable word wrapping to show full content across multiple lines:

ts
table.addColumn({ name: 'Description', width: 15, wrapWord: true });
table.addRow({ Description: 'This is a very long description that will wrap' });

Expected output:

│ This is a very │
│ long           │
│ description    │
│ that will wrap │

Responsive Layouts

Make tables adapt to narrow terminals by setting responsiveMode.

Hide Mode

Low-priority columns are hidden first when the table doesn't fit.

ts
const table = new Table({
    terminalWidth: 40,  // Or auto-detected from process.stdout.columns
    responsiveMode: 'hide'
});

table.addColumn({ name: 'ID', priority: 0 });          // Kept (low priority number)
table.addColumn({ name: 'Name', priority: 0 });         // Kept
table.addColumn({ name: 'Email', priority: 5 });         // Hidden first
table.addColumn({ name: 'Bio', priority: 10 });          // Hidden first (highest priority = hidden first)

Priority Logic

Lower number = more important. Columns with the highest priority value are hidden first when space is tight.

Stack Mode

Converts the table into a vertical key-value layout when it doesn't fit:

ts
const table = new Table({
    terminalWidth: 30,
    responsiveMode: 'stack'
});

Stacked output:

╭───────┬──────────╮
│ Row 1            │
├───────┼──────────┤
│ Name  │ Alice    │
│ Role  │ Engineer │
│ Email │ a@co.com │
╰───────┴──────────╯

Spanning

Header Groups (Column Grouping)

Group multiple columns under a super-header:

ts
const table = new Table({
    headerGroups: [
        { title: 'Personal', colSpan: 2 },
        { title: 'Work', colSpan: 2 }
    ]
});
table.addColumn('First');
table.addColumn('Last');
table.addColumn('Role');
table.addColumn('Team');

Cell ColSpan (Horizontal Merge)

Merge cells horizontally within a row:

ts
table.addRow([
    { content: 'Section Title', colSpan: 3, align: 'center' },
    'Status'
]);

Cell RowSpan (Vertical Merge)

Use mergeAdjacent() to automatically merge identical cells vertically:

ts
table.addRows([
    { Dept: 'Engineering', Name: 'Alice' },
    { Dept: 'Engineering', Name: 'Bob' },
    { Dept: 'HR', Name: 'Charlie' }
]);

table.mergeAdjacent(['Dept']); // 'Engineering' merges into a single tall cell

Hidden Columns

Manually hide specific columns:

ts
table.addColumn({ name: 'Internal ID', hidden: true });

The column data is still stored — it's just not rendered. Useful for export or programmatic access.

Column Formatters

The formatter callback transforms a cell's raw value before it is word-wrapped and rendered. Use it for number formatting, date display, boolean icons, conditional text, and more.

ts
(value: any, rowIndex: number) => string
  • value — the raw cell content as a string
  • rowIndex — zero-based index of the data row (header is never formatted)

Examples

Currency

ts
table.addColumn({
    name: 'Price',
    key: 'price',
    align: 'right',
    minWidth: 10,
    formatter: (v) => '$' + Number(v).toFixed(2),
});
// $9.50, $100.00, $0.99

Boolean Icons

ts
table.addColumn({
    name: 'Active',
    key: 'active',
    align: 'center',
    formatter: (v) => v === 'true' ? '✅' : '❌',
});

Conditional per-row styling

ts
table.addColumn({
    name: 'Score',
    key: 'score',
    align: 'right',
    formatter: (v, i) => {
        const n = Number(v);
        if (n >= 90) return `🟢 ${n}`;
        if (n >= 60) return `🟡 ${n}`;
        return `🔴 ${n}`;
    },
});

Inline Progress Bar (using ProgressBar)

ts
import { ProgressBar } from 'cmd-table';

table.addColumn({
    name: 'Coverage',
    key: 'coverage',
    minWidth: 15,
    formatter: (v) => ProgressBar.generate(Number(v), 100, { width: 10 }),
});
// ████████░░ 82%

TIP

Always set minWidth on columns using a formatter so the formatted value isn't word-wrapped onto multiple lines.

Header protection

The formatter is never applied to the column header row. Column names are always rendered as-is.