DocsBuilding

Layouts

Layout components divide the terminal into panels — side-by-side, stacked, or in grids. Each panel is an independent area with its own border, title, and content clipping.

import { columns, rows, grid, panel, row, col, container } from "terminaltui";

Navigation uses spatial positioning — arrow keys move to the nearest focusable item in that direction based on screen position. This works automatically with all layout functions.

KeyAction
Left / hMove to nearest focusable item to the left. If nothing to the left, go back.
Right / lMove to nearest focusable item to the right.
Up / kMove to nearest focusable item above.
Down / jMove to nearest focusable item below.
EnterActivate focused item (open URL, submit, etc.)
EscapeGo back to menu
Tab / Shift+TabCycle through all focusable items sequentially

In a 2-column grid with 6 items:

[A] [B]    Right from A -> B, Down from A -> C
[C] [D]    Left from D -> C, Up from D -> B
[E] [F]

On single-column pages, Up/Down moves sequentially and Left goes back — identical to classic TUI navigation.

No configuration needed. Spatial navigation is automatic for all layouts.


columns(panels)

Side-by-side panels. Each panel gets a width.

columns([
  { width: "60%", content: [
    table(["Container", "Status", "CPU"], [
      ["nginx", "running", "2.3%"],
      ["postgres", "running", "5.1%"],
    ]),
  ]},
  { width: "40%", content: [
    markdown("## System Stats"),
    progressBar("CPU", 45),
    progressBar("Memory", 72),
  ]},
])

Width options: "50%" (percentage), 30 (fixed chars), omit for equal split.


rows(panels)

Vertically stacked panels with fixed/flex heights.

rows([
  { height: "30%", content: [
    markdown("## Active Containers"),
    table(["Name", "Status"], [["nginx", "up"], ["postgres", "up"]]),
  ]},
  { height: "70%", content: [
    markdown("## Logs"),
    markdown("12:00:01 [nginx] GET /health 200"),
  ]},
])

Height options: "50%" (percentage), 10 (fixed rows), omit for equal split.


grid(config)

N*M grid of panels.

grid({
  cols: 2,     // number of columns
  gap: 1,      // gap between cells in characters (default: 1)
  items: [
    { title: "CPU", content: [progressBar("Usage", 45)] },
    { title: "Memory", content: [progressBar("RAM", 72)] },
    { title: "Network", content: [markdown("125 Mbps")] },
    { title: "Disk I/O", content: [progressBar("Read", 23)] },
  ],
})

Rows are auto-calculated from Math.ceil(items.length / cols).


12-Column Grid System

row(), col(), and container() provide a Bootstrap-style 12-column grid for precise layouts with responsive breakpoints.

// Basic: sidebar + main content
row([
  col([sidebar], { span: 3 }),         // 25%
  col([mainContent], { span: 9 }),     // 75%
], { gap: 1 })

// Responsive: cards reflow based on terminal width
row([
  col([card1], { span: 4, sm: 6, xs: 12 }),  // 33% wide, 50% medium, full narrow
  col([card2], { span: 4, sm: 6, xs: 12 }),
  col([card3], { span: 4, sm: 12, xs: 12 }),
])

// Centered container with max width
container([
  row([col([hero(...)], { span: 12 })]),
  row([
    col([sidebar], { span: 3 }),
    col([content], { span: 9 }),
  ]),
], { maxWidth: 100, padding: 2 })

ColConfig: span (1-12), offset (0-11), xs/sm/md/lg (responsive spans), padding. Responsive breakpoints: xs (<60 cols), sm (60-89), md (90-119), lg (>=120).

When total spans in a row exceed 12, columns wrap to the next line.


panel(config)

A content area with optional border. Used inside layout components or standalone.

{
  content: ContentBlock[];      // required
  width?: string | number;      // for columns
  height?: string | number;     // for rows
  title?: string;               // rendered above content
  border?: boolean | BorderStyle; // default: no border
  padding?: number;             // interior padding (default: 0)
}

Nested Layouts

Layouts compose freely. A column panel can contain rows, which can contain grids:

columns([
  { width: "25%", title: "Nav", content: [
    link("Dashboard", "#"),
    link("Logs", "#"),
    link("Settings", "#"),
  ]},
  { width: "75%", content: [
    rows([
      { height: "60%", content: [
        markdown("## Main Content"),
        table(["Name", "Status"], [["nginx", "running"]]),
      ]},
      { height: "40%", title: "Logs", content: [
        markdown("Log output here..."),
      ]},
    ]),
  ]},
])

Responsive Behavior

When the terminal is too narrow for side-by-side panels (less than 20 chars per panel), columns automatically collapse to vertical stacking. When the terminal is resized wider, columns restore.

Grid columns also auto-reduce when the terminal is narrow.

The 12-column grid system uses responsive breakpoints (xs, sm, md, lg) for fine-grained control.


When to Use Which Layout

Use CaseLayout
Dashboard with sidebarcolumns() or row() + col()
Log viewer with controlsrows()
Editor with previewcolumns([panel({ width: "30%" }), panel({ width: "70%" })])
Monitoring metricsgrid()
Responsive card gridrow() with xs:12, sm:6, lg:4
Centered narrow contentcontainer({ maxWidth: 80 })
Complex dashboardNested columns() + rows() or row() + col()

For simple content pages, stick with the default vertical scroll. Layouts are best for dashboards, monitoring, and tool UIs where spatial organization matters.


Examples in Demos

Every demo uses layouts where side-by-side content improves the experience:

DemoLayouts UsedPages
server-dashboardcolumns, rows, gridAll 4 pages — the layout showcase
developer-portfoliocolumns, grid, row/colAbout (text + skills), Projects, Experience, Writing, Links
restaurantcolumnsMenu (food / cocktails), Wine, Hours (table / location)
startupgrid, row/colHome (features grid), Features (2-col grid), Pricing (3 tiers), Links
conferencecolumns, gridHome, Schedule (Day 1 / Day 2), Speakers, Venue, Sponsors
coffee-shopcolumns, gridMenu (espresso / filter), Beans, Hours, Connect
bandgrid, columnsDiscography, Shows, About (members grid)
freelancergrid, columnsServices, Work, Testimonials, Contact
dashboardcolumnsDashboard home (status / posts), Post detail, New Post

Run npx terminaltui demo server-dashboard for the most comprehensive layout example.

Edit this page on GitHub