cli-table
cli-table turns arrays and small key-value objects into bordered text tables for Node command-line output. You construct a table, push rows because the object inherits from Array, and call `toString()` to render Unicode borders, padded columns, optional ANSI colors, alignment, truncation, multiline cells, horizontal tables, vertical key-value layouts, or cross tables. It is the original 2010 package, not the newer `cli-table3` fork.
Do not install the original for a new CLI; use `cli-table3` unless a different data model fits better. Keep `cli-table` only when an existing tool relies on its exact legacy rendering behavior.
Use it if
- You maintain an existing CommonJS CLI that already depends on cli-table and its exact output is covered by snapshots
- You need a dependency-light table with custom borders, fixed widths, alignment, truncation, and multiline cells
- Your table data is short ASCII or simple ANSI-colored text where JavaScript string length is a sufficient width estimate
- You want an array-like object that can be built incrementally with `push`, `unshift`, and `splice`
- You are choosing a table renderer for a new project: `cli-table3` is the maintained fork and keeps a closely related API
- You print emoji, CJK, combining marks, hyperlinks, or newer ANSI sequences: the width function strips only a narrow SGR color pattern and otherwise counts UTF-16 code units, so borders can drift
- You need TypeScript support or ESM exports: 0.3.11 ships no declarations, uses CommonJS, and predates package export maps
- You expect automatic terminal-aware wrapping: fixed columns truncate with one marker, automatic widths can grow past the terminal, and the implementation has no terminal width or reflow logic
- You need active releases and modern dependency hygiene: the latest npm version was published in 2021 and pins `colors` 1.0.3 rather than using an unstyled core
Setup reality
`npm install cli-table` is the whole installation. There are no native builds, peer dependencies, credentials, or config files; the only runtime dependency is the pinned `colors` 1.0.3 package. The API is CommonJS (`require('cli-table')`) and has no bundled TypeScript declarations, so typed projects need their own declaration or a community types package. Widths are the first practical surprise. Each `colWidths` number includes left and right padding plus the cell content, and values that exceed it are truncated with `…` by default. Without explicit widths, the renderer measures all rows and can produce a table wider than the terminal. Its measurement removes a limited ANSI SGR pattern but counts ordinary JavaScript string length, which is not display width for emoji, combining characters, and many CJK glyphs. Alignment uses `left`, `right`, or the implementation's `middle` value even though the README describes center alignment. Row cells are converted through `toString()`, so `null` and `undefined` are not safe cell values unless normalized first. Vertical rows are one-key objects; extra keys are ignored because rendering takes only `Object.keys(row)[0]`. Cross-table rendering calls `unshift` on the array stored as the object value, mutating that source array. Styling names are looked up on `colors/safe`, and invalid style names fail at render time. The table does not print by itself, wrap to terminal width, sanitize control characters, or stream rows; call `toString()` only after the complete in-memory table is ready.
Patterns
Render a basic horizontal tablerender-horizontal-table
const Table = require('cli-table');
const table = new Table({
head: ['Name', 'Status'],
});
table.push(
['api', 'ready'],
['worker', 'stopped'],
);
console.log(table.toString());Automatic widths inspect all rows and do not respect terminal width, so long values can create an extremely wide table.
Fix widths and truncate long cellsset-column-widths
const table = new Table({
head: ['Package', 'Description'],
colWidths: [18, 42],
truncate: '...',
});
table.push(['cli-table', 'Render tables in terminal output']);Each width includes both padding columns. The truncation marker also consumes part of the available content width.
Align numeric and centered columnsalign-columns
const table = new Table({
head: ['Item', 'Count', 'State'],
colWidths: [18, 10, 12],
colAligns: ['left', 'right', 'middle'],
});
table.push(['jobs', 42, 'ok']);The implementation recognizes `middle` for centered text, not the more common `center` spelling.
Render key-value rowsrender-vertical-table
const table = new Table();
table.push(
{ Runtime: process.version },
{ Platform: process.platform },
);
console.log(table.toString());Use exactly one key per row object. If an object has several keys, only the first enumerated key is rendered.
Render row and column headersrender-cross-table
const table = new Table({
head: ['', 'Passed', 'Failed'],
});
table.push(
{ Unit: [120, 0] },
{ Integration: [34, 2] },
);
console.log(table.toString());Rendering prepends the row header with `unshift`, mutating each array supplied as an object value. Do not reuse those arrays afterward.
Provide rows in the constructorinitialize-with-rows
const table = new Table({
head: ['Command', 'Duration'],
rows: [
['lint', '1.2s'],
['test', '8.4s'],
],
});
process.stdout.write(table.toString() + '\n');Constructor rows are pushed into the array-like table; later `push`, `splice`, and `unshift` calls still work.
Replace the border character setcustomize-borders
const table = new Table({
chars: {
top: '=', 'top-mid': '+', 'top-left': '+', 'top-right': '+',
bottom: '=', 'bottom-mid': '+', 'bottom-left': '+', 'bottom-right': '+',
left: '|', 'left-mid': '+', mid: '-', 'mid-mid': '+',
right: '|', 'right-mid': '+', middle: '|',
},
});
table.push(['plain', 'ascii']);Override every key for a consistent ASCII border; omitted keys retain their Unicode defaults.
Hide horizontal lines between rowsremove-row-separators
const table = new Table({
chars: {
mid: '',
'left-mid': '',
'mid-mid': '',
'right-mid': '',
},
});
table.push(['one', 'first'], ['two', 'second']);Empty decoration lines are skipped, but the top, bottom, left, right, and vertical middle borders remain.
Create a compact borderless layoutrender-borderless-columns
const table = new Table({
chars: {
top: '', 'top-mid': '', 'top-left': '', 'top-right': '',
bottom: '', 'bottom-mid': '', 'bottom-left': '', 'bottom-right': '',
left: '', 'left-mid': '', mid: '', 'mid-mid': '',
right: '', 'right-mid': '', middle: ' ',
},
style: { 'padding-left': 0, 'padding-right': 0 },
});The spaces needed for alignment still come from computed column widths; only borders and explicit padding disappear.
Render multiline cell contentrender-multiline-cell
const table = new Table({
head: ['Service', 'Endpoints'],
colWidths: [16, 32],
});
table.push(['api', 'GET /health\nPOST /jobs']);The row height grows to the tallest cell and shorter cells are padded with blank lines; there is no automatic word wrapping.
Normalize missing values before renderingnormalize-null-cells
const rows = records.map((record) => [
record.name ?? '(unnamed)',
record.owner ?? '-',
]);
const table = new Table({ head: ['Name', 'Owner'], rows });Cells are rendered through `item.toString()`, so passing `null` or `undefined` directly throws instead of displaying an empty value.
Alternatives
| Package | Registry | Pick it when |
|---|---|---|
| cli-table3 | npm | You want the maintained continuation with a familiar row model and better handling for modern Node projects |
| table | npm | You need configurable word wrapping, spanning cells, border presets, and a functional rendering API |
| easy-table | npm | You prefer building records column by column with automatic widths and simple formatters |