TextTable

TextTableRenderable lays out a two-dimensional array of styled text cells. Use Markdown when the table starts as Markdown source.

It supports intrinsic or full-width columns, constrained wrapping, independent borders, cell padding, and text selection.

Availability#

Field Availability
Package @opentui/core
Core renderable TextTableRenderable
React Unavailable
Solid Unavailable
Status Built-in Core renderable

Basic usage#

import { TextTableRenderable, bold, fg, type TextChunk, type TextTableContent } from "@opentui/core"

const cell = (text: string): TextChunk[] => [{ __isChunk: true, text }]

const content: TextTableContent = [
  [[bold("Service")], [bold("Status")], [bold("Notes")]],
  [cell("api"), [fg("#00d4aa")("OK")], cell("latency 28ms")],
  [cell("worker"), [fg("#b8a0ff")("DEGRADED")], cell("queue depth: 124")],
]

const table = new TextTableRenderable(renderer, {
  width: "100%",
  wrapMode: "word",
  columnWidthMode: "content",
  borderStyle: "rounded",
  content,
})

renderer.root.add(table)

Monochrome preview of the same table:

Content#

The public content types are:

type TextTableCellContent = TextChunk[] | null | undefined
type TextTableContent = TextTableCellContent[][]

Use styled-text helpers such as bold(), fg(), and bg() to build each cell’s TextChunk[]. null, undefined, and missing cells render as empty text. Rows can have different lengths. The table uses the longest row’s column count and fills missing cells with empty content.

The first row has no special behavior. Styling it as a header is a convention. TextTable has no header option or separate header model.

Replace the table data through the content setter:

table.content = [
  [[bold("Name")], [bold("State")]],
  [cell("worker-1"), [fg("#22c55e")("ready")]],
]

Column sizing and wrapping#

columnWidthMode controls what happens when the available width exceeds the content’s intrinsic width:

  • "full" expands columns evenly to fill the width constraint. This is the default.
  • "content" keeps the table at its intrinsic width when additional space is available.

When content is wider than a finite width constraint, wrapMode: "word" or "char" allows the table to shrink columns and grow rows. wrapMode: "none" preserves intrinsic column widths instead of shrinking over-wide content.

columnFitter chooses how constrained width is distributed:

  • "proportional" preserves more width for intrinsically wider columns. This is the default.
  • "balanced" keeps constrained columns closer to an even visual width while respecting their intrinsic sizes.
const table = new TextTableRenderable(renderer, {
  width: 50,
  wrapMode: "word",
  columnWidthMode: "full",
  columnFitter: "balanced",
  content,
})

Borders and spacing#

border controls separators between cells. outerBorder controls the boundary around the table. When omitted, outerBorder follows the initial border value and later assignments. Assigning a different outerBorder value makes it independent. Assigning its current value is a no-op and keeps that relationship unchanged.

// Outer border without inner cell separators
const table = new TextTableRenderable(renderer, {
  border: false,
  outerBorder: true,
  content,
})

showBorders: false suppresses border glyph painting without removing the space reserved by enabled inner or outer borders. columnGap adds space between columns only when inner vertical borders are disabled.

cellPadding sets both axes. cellPaddingX and cellPaddingY override that value per axis. OpenTUI floors padding and gap values and clamps them to zero. Non-finite values use the default 0.

Selection#

Selection starts only within cell content, not on border glyphs. Selection within one cell can be partial. A vertical drag in the anchor column selects that column. Moving into another column changes to grid selection.

getSelectedText() joins nonempty selected cells in a row with tabs. It joins selected rows with newlines and excludes table borders. A selected blank cell does not add an empty tab field.

renderer.on("selection", () => {
  console.log(table.getSelectedText())
})

Selection-related methods are:

Method Result
shouldStartSelection(x, y) Whether the global position is selectable cell content
onSelectionChanged(selection) Applies a renderer selection and reports whether text is selected
hasSelection() Whether any cell has a selection
getSelection() The first selected cell’s { start, end } range, or null
getSelectedText() Tab/newline-delimited nonempty selected cell text

Options#

TextTable also accepts the standard renderable layout options.

Option Type Default Description
content TextTableContent [] Rows of styled cell chunks
wrapMode "none" | "char" | "word" "word" Cell text wrapping behavior
columnWidthMode "content" | "full" "full" Preserve intrinsic width or fill the width constraint
columnFitter "proportional" | "balanced" "proportional" Width allocation when columns must shrink
cellPadding number 0 Horizontal and vertical padding on each side of a cell
cellPaddingX number cellPadding Horizontal padding on each side
cellPaddingY number cellPadding Vertical padding on each side
columnGap number 0 Gap between columns when inner vertical borders are off
showBorders boolean true Paint enabled border glyphs
border boolean true Enable inner row and column separators
outerBorder boolean border Enable the table boundary
borderStyle "single" | "double" | "rounded" | "heavy" "single" Border glyph set
borderColor ColorInput "#FFFFFF" Border foreground color
borderBackgroundColor ColorInput "transparent" Border background color
backgroundColor ColorInput "transparent" Buffered table surface background
fg ColorInput "#FFFFFF" Default cell text foreground
bg ColorInput "transparent" Default cell text background
attributes number 0 Default text attribute bitmask
selectable boolean true Allow cell text selection
selectionBg ColorInput - Selection background override
selectionFg ColorInput - Selection foreground override
flexShrink number 0 Inherited layout shrink factor

The renderable always uses a buffered surface. The mutable table-specific properties are content, wrapMode, columnWidthMode, columnFitter, cellPadding, cellPaddingX, cellPaddingY, columnGap, showBorders, border, outerBorder, borderStyle, and borderColor.

Use Markdown for document tables. Use Code for syntax-highlighted source and Diff for patches. Line number gutter applies to line-aware content instead of table rows.