opentui

Build terminal UIs with OpenTUI. Covers Core, frameworks, components, application APIs, testing, extensions, integrations, deployment, and public API lookup.

Install
npx skills add 'https://github.com/anomalyco/opentui/tree/main/packages/web/src/content'
Download bundle ↓
main · ac753b4Scanned 2026-09-15

Contributors

GitHub-linked commit authors for this SKILL.md at the saved revision. Co-authors and history before file renames are not included.

File history ↗

docs/components/text-table.mdx

docs/components/text-table.mdxBrowse 89 files
View on GitHub
← Back to SKILL.md

title: TextTable description: Render styled, wrapping, and selectable tabular text skill: intents: [text-table, table, tabular-data, styled-cells, table-selection]

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

FieldAvailability
Package@opentui/core
Core renderableTextTableRenderable
ReactUnavailable
SolidUnavailable
StatusBuilt-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:

╭───────┬────────┬────────────────╮
│Service│Status  │Notes           │
├───────┼────────┼────────────────┤
│api    │OK      │latency 28ms    │
├───────┼────────┼────────────────┤
│worker │DEGRADED│queue depth: 124│
╰───────┴────────┴────────────────╯

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:

MethodResult
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.

OptionTypeDefaultDescription
contentTextTableContent[]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
cellPaddingnumber0Horizontal and vertical padding on each side of a cell
cellPaddingXnumbercellPaddingHorizontal padding on each side
cellPaddingYnumbercellPaddingVertical padding on each side
columnGapnumber0Gap between columns when inner vertical borders are off
showBordersbooleantruePaint enabled border glyphs
borderbooleantrueEnable inner row and column separators
outerBorderbooleanborderEnable the table boundary
borderStyle"single" | "double" | "rounded" | "heavy""single"Border glyph set
borderColorColorInput"#FFFFFF"Border foreground color
borderBackgroundColorColorInput"transparent"Border background color
backgroundColorColorInput"transparent"Buffered table surface background
fgColorInput"#FFFFFF"Default cell text foreground
bgColorInput"transparent"Default cell text background
attributesnumber0Default text attribute bitmask
selectablebooleantrueAllow cell text selection
selectionBgColorInput-Selection background override
selectionFgColorInput-Selection foreground override
flexShrinknumber0Inherited 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.