termino
~/docs/custom/grid

Gridcustom

Flexbox rows of equal-width cells

~/custom/grid
live
$ termino grid — ← → change columns
columns: 3
cell 1
1
────────
cell 2
2
────────
cell 3
3
────────
cell 4
4
────────
cell 5
5
────────
cell 6
6
────────
cells flow in rows of 3, each flexGrow: 1

##API

Slices `cells` into rows of `columns`; every cell is a box with flexGrow 1, so a row always fills its container width. Pure composition — no layout engine of its own.

properties
proptypedefaultdescription
cellsReactNode[]-Cell contents, row-major order
columnsnumbercells.lengthCells per row; last row may be short
gapnumber1Gap between cells and rows
cellPropsbox props-Extra props applied to every cell box

##Keymap

~/keymap
No keyboard input (layout only)
columns propChange column count; cells re-flow

##Notes

  • Sizing is deferred to the parent: give the Grid a width (or let the app root size it) and cells split it evenly.
  • Short last rows stretch — perfect for dashboards and menu grids.

##Source

Real implementation — this is the code that runs in your terminal.

tsxgrid.tsx
1/* eslint-disable react/no-array-index-key -- These renderers draw a fixed
2 terminal grid: a child's identity *is* its row and column, and the grid
3 never reorders, so the index is the stable key rather than a stand-in
4 for one. */
5
6import { createElement as h } from "react";
7import { Children, isValidElement, type ReactNode } from "react";
8
9export interface GridProps {
10 children: ReactNode;
11 columns?: number;
12 gap?: number;
13 cellProps?: Record<string, unknown>;
14}
15
16export function Grid({ children, columns, gap = 1, cellProps }: Readonly<GridProps>) {
17 const cells = Children.toArray(children).filter(isValidElement);
18 const cols = columns ?? (cells.length || 1);
19
20 const rows: ReactNode[][] = [];
21 for (let i = 0; i < cells.length; i += cols) {
22 rows.push(cells.slice(i, i + cols));
23 }
24
25 return h(
26 "box",
27 { flexDirection: "column", gap },
28 rows.map((row, ri) =>
29 h(
30 "box",
31 { key: ri, flexDirection: "row", gap },
32 row.map((cell, ci) =>
33 h(
34 "box",
35 {
36 key: ci,
37 style: { flexGrow: 1 },
38 ...cellProps,
39 },
40 cell,
41 ),
42 ),
43 ),
44 ),
45 );
46}
47