GridTable
CSS Grid-based superset of Table for column grouping and content-based sizing.
When to use#
With flexbox-based Table, each row calculates its layout independently. GridTable uses CSS Grid for a single unified column track system across all rows.
Use GridTable when you need:
- Grouped headers spanning multiple columns
- Columns sized based on their widest content
- Precise, declarative column layout
Import#
import {GridTable,createGridTemplate,getGridTemplateColumn} from '@volue/wave-react';
GridTable is a compound component that consists of multiple parts which can be composed together to achieve the desired table interface:
-
GridTable.Root: The wrapper that contains all the parts of a grid table. RequiresgridTemplateColumnsprop. -
GridTable.Head: The container that defines the head of the table's columns (equivalent of<thead>HTML element). -
GridTable.Header: A convenience container wrapping content intoGridTable.Headwith singleGridTable.Rowinside. -
GridTable.ColumnHeader: Renders the header cell of a column (equivalent of native<th>element). SupportscolSpanfor spanning multiple columns. -
GridTable.ColumnHeaderContent: Wrapper for column's header text contents. -
GridTable.ColumnHeaderTitle: Column's header title. -
GridTable.ColumnHeaderSubtitle: Column's header subtitle. -
GridTable.ColumnResizeHandle: The handle for resizing the column. Should be passed toGridTable.ColumnHeadervia theresizeHandleprop. -
GridTable.Body: The container grouping a set ofGridTable.Rows that are main content of the table (equivalent of native<tbody>element). -
GridTable.Row: Renders table row (equivalent of native<tr>element). -
GridTable.Cell: Cell to be placed in theGridTable.Rowthat displays single piece of data in the column (equivalent of native<td>element). Wraps text content intoGridTable.CellContentautomatically. -
GridTable.CellContent: Wrapper for cell's text contents. -
GridTable.SelectionCell: ComposesGridTable.CellandGridTable.SelectionCellCheckbox, to render a cell with checkbox for implementing selection of rows. -
GridTable.SelectionCellCheckbox: Customized Checkbox component to be placed in the table cell. -
GridTable.Foot: The container grouping a set ofGridTable.Rows which is placed at the bottom of the table (equivalent of native<tfoot>element). -
GridTable.Footer: A convenience container wrapping content intoGridTable.Footwith singleGridTable.Rowinside. -
GridTable.EmptyState: The wrapper for providing additional context when the table data is either unavailable or nonexistent.
Examples#
These examples focus on GridTable-specific features. For general table patterns, see Table which has the same component structure. Migration is straightforward: replace Table namespace with GridTable and move sizing props from cells to gridTemplateColumns.
Basic#
The gridTemplateColumns prop is required. You can pass any CSS grid-template-columns value directly, or use the createGridTemplate utility for semantic column widths.
Column widths#
GridTable supports several column width modes through the createGridTemplate utility:
-
grow(default): Fills available space. Min width is the width of the widest cell. -
growCollapse: Grows to fill space but can shrink below content width. -
auto: The column is the width of its widest cell. - Fixed values: Will be exactly that width (e.g.,
120,'200px','10rem').
Each mode can be combined with minWidth and maxWidth constraints.
Column groups#
Use colSpan on GridTable.ColumnHeader to create grouped headers that span multiple columns. This is useful for organizing related columns under a common heading.
Content-based sizing#
Use auto width to size a column to the width of its widest cell.
Keep in mind that column width will change as content changes. For columns with highly variable content, prefer grow with constraints.
When using overflowStrategy="wrap", values with units can break across lines. Use non-breaking spaces (\u00A0) to keep them glued together. This is not needed with the default truncate strategy since content never wraps.
// ❌ Regular space — can break across lines`${value} MW`// ✅ Non-breaking space`${value}\u00A0MW`;
For values without a visible space (e.g., a currency symbol and number like €100), use \u2060 (word joiner) to prevent breaks without adding space.
If you format values with Intl.NumberFormat, the spaces it inserts are already non-breaking — no extra work needed. This only applies when manually concatenating values with units.
Column layout utilities#
Wave provides utility functions to easily construct gridTemplateColumns values.
createGridTemplate#
Converts an array of column configurations to a CSS grid-template-columns value.
import { createGridTemplate } from '@volue/wave-react';createGridTemplate([{ width: 'grow', minWidth: 100 },{ width: 120 },{ width: 'auto' }]);// Returns: "minmax(100px, 1fr) 120px auto"
getGridTemplateColumn#
Converts a single column configuration to a CSS value. Useful when building gridTemplateColumns dynamically.
import { getGridTemplateColumn } from '@volue/wave-react';// Semantic modesgetGridTemplateColumn({ width: 'grow' }); // "minmax(max-content, 1fr)"getGridTemplateColumn({ width: 'growCollapse' }); // "minmax(0, 1fr)"getGridTemplateColumn({ width: 'auto' }); // "auto"// Fixed widthsgetGridTemplateColumn({ width: 120 }); // "120px"getGridTemplateColumn({ width: '200px' }); // "200px"// With constraintsgetGridTemplateColumn({ width: 'grow', minWidth: 100 }); // "minmax(100px, 1fr)"getGridTemplateColumn({ width: 'grow', maxWidth: 300 }); // "minmax(auto, 300px)"getGridTemplateColumn({ width: 'grow', minWidth: 100, maxWidth: 300 }); // "minmax(100px, 300px)"
Quick reference#
| Input | Output |
|---|---|
{ width: 'grow' } | minmax(max-content, 1fr) |
{ width: 'growCollapse' } | minmax(0, 1fr) |
{ width: 'auto' } | auto |
{ width: 'auto', maxWidth: 200 } | minmax(auto, 200px) |
{ width: 120 } | 120px |
{ width: '200px' } | 200px |
{ width: 'grow', minWidth: 100 } | minmax(100px, 1fr) |
{ width: 'grow', maxWidth: 300 } | minmax(auto, 300px) |
{ width: 'growCollapse', maxWidth: 200 } | minmax(0, 200px) |
TanStack Table integration#
GridTable works well with TanStack Table for features like sorting, filtering, and column resizing. The key is generating gridTemplateColumns from TanStack's column state.
GridTable shares the same patterns as Table. See Table's TanStack integration for more examples.
Extending column meta#
When using TypeScript, extend TanStack's ColumnMeta type to include semantic grid column width options:
import type { CSSProperties } from 'react';import type { RowData } from '@tanstack/react-table';import type { GridTableColumnWidth } from '@volue/wave-react';declare module '@tanstack/react-table' {interface ColumnMeta<TData extends RowData, TValue> {gridWidth?: GridTableColumnWidth;minWidth?: CSSProperties['minWidth'];maxWidth?: CSSProperties['maxWidth'];showDivider?: boolean;}}
Generating gridTemplateColumns#
Generate template columns from table.getVisibleLeafColumns(). You can extract a reusable hook (example below) or compute it directly in your component.
import { useMemo } from 'react';import { useReactTable } from '@tanstack/react-table';import { getGridTemplateColumn } from '@volue/wave-react';type ResizeMode = 'fixed' | 'proportional';function useGridTemplateColumns<TData>(table: ReturnType<typeof useReactTable<TData>>,resizeMode: ResizeMode = 'proportional') {return useMemo(() => {return table.getVisibleLeafColumns().map(column => {// Optional: handle resizable columnsif (column.getCanResize()) {const size = column.getSize();return resizeMode === 'fixed'? `${size}px`: `minmax(${size}px, ${size}fr)`;}return getGridTemplateColumn({width: column.columnDef.meta?.gridWidth,minWidth: column.columnDef.meta?.minWidth,maxWidth: column.columnDef.meta?.maxWidth});}).join(' ');}, [table,// eslint-disable-next-line react-hooks/exhaustive-depstable.getState().columnSizing,// eslint-disable-next-line react-hooks/exhaustive-depstable.getState().columnSizingInfo,// eslint-disable-next-line react-hooks/exhaustive-depstable.getState().columnVisibility,resizeMode]);}
Example with sorting and column groups#
import {flexRender,getCoreRowModel,getSortedRowModel,useReactTable,createColumnHelper} from '@tanstack/react-table';
Rows virtualization#
Rendering large amounts of rows can be inefficient. With virtualization (or windowing) of data, only currently visible rows are rendered to DOM to deliver best performance and user experience.
When only a subset of rows are visible, it's a good practice to let all users know which rows are being displayed.
Use the aria-rowcount attribute on the GridTable.Root to let assistive technologies know the total number of rows available. GridTable.Row should include aria-rowindex attribute to indicate where each row is in relation to the total available rows.
import {flexRender,getCoreRowModel,useReactTable,createColumnHelper} from '@tanstack/react-table';import { useVirtualizer } from '@tanstack/react-virtual';
API Reference#
GridTable.Root#
Prop | Type | Default |
|---|---|---|
css | StitchesCss | No default value |
gridTemplateColumns* | string | No default value |
alignY | enum | "center" |
cellSize | enum | "medium" |
overflowStrategy | enum | "truncate" |
highlightOnHover | boolean | true |
syncScroll | boolean | false |
aria-label* | string | No default value |
rowSeparation | enum | "dividers" |
outlined | boolean | true |
addHorizontalWhitespace | boolean | false |
showCellSeparation | boolean | false |
fitContent | boolean | false |
GridTable.Head#
Container for header rows. Supports multiple GridTable.Row elements for grouped headers.
Prop | Type | Default |
|---|---|---|
css | StitchesCss | No default value |
stickyOffset | number | No default value |
GridTable.Header#
Convenience wrapper that composes GridTable.Head with a single GridTable.Row.
Prop | Type | Default |
|---|---|---|
css | StitchesCss | No default value |
stickyOffset | number | No default value |
GridTable.ColumnHeader#
Header cell for a column. Supports colSpan for grouped headers.
Prop | Type | Default |
|---|---|---|
css | StitchesCss | No default value |
align | enum | "left" |
colSpan | number | No default value |
stickyOffset | number | No default value |
showDivider | boolean | No default value |
onClick | function | No default value |
sort | enum | No default value |
sortIndicator | React.ReactElement | No default value |
resizeHandle | React.ReactElement | No default value |
isGroupHeader | boolean | false |
GridTable.ColumnHeaderContent#
Prop | Type | Default |
|---|---|---|
as | enum | div |
css | StitchesCss | No default value |
overflowStrategy | enum | No default value |
GridTable.ColumnHeaderTitle#
Prop | Type | Default |
|---|---|---|
as | enum | span |
css | StitchesCss | No default value |
overflowStrategy | enum | No default value |
GridTable.ColumnHeaderSubtitle#
Prop | Type | Default |
|---|---|---|
as | enum | span |
css | StitchesCss | No default value |
overflowStrategy | enum | No default value |
GridTable.ColumnResizeHandle#
Prop | Type | Default |
|---|---|---|
css | StitchesCss | No default value |
isResizing | boolean | false |
GridTable.SortIndicator#
Prop | Type | Default |
|---|---|---|
css | StitchesCss | No default value |
GridTable.Body#
Container for the main content rows of the table.
Prop | Type | Default |
|---|---|---|
css | StitchesCss | No default value |
GridTable.Row#
Prop | Type | Default |
|---|---|---|
css | StitchesCss | No default value |
status | enum | No default value |
isHighlighted | boolean | false |
tone | enum | No default value |
GridTable.Cell#
Cell for displaying data. Wraps text content into GridTable.CellContent automatically.
Prop | Type | Default |
|---|---|---|
css | StitchesCss | No default value |
align | enum | "left" |
stickyOffset | number | No default value |
showDivider | boolean | No default value |
role | enum | "gridcell" |
tone | enum | No default value |
GridTable.CellContent#
In addition to the props below, you can pass Text props.
Prop | Type | Default |
|---|---|---|
as | enum | div |
css | StitchesCss | No default value |
overflowStrategy | enum | No default value |
GridTable.SelectionCell#
Composes GridTable.Cell and GridTable.SelectionCellCheckbox for row selection.
In addition to the props below, you can pass all GridTable.SelectionCellCheckbox props.
Prop | Type | Default |
|---|---|---|
css | StitchesCss | No default value |
align | enum | "left" |
stickyOffset | number | No default value |
showDivider | boolean | No default value |
role | enum | gridcell |
aria-label* | string | No default value |
GridTable.SelectionCellCheckbox#
In addition to the props below, you can pass all Checkbox props.
Prop | Type | Default |
|---|---|---|
css | StitchesCss | No default value |
aria-label* | string | No default value |
GridTable.Foot#
Container for footer rows.
Prop | Type | Default |
|---|---|---|
css | StitchesCss | No default value |
stickyOffset | number | No default value |
GridTable.Footer#
Convenience wrapper that composes GridTable.Foot with a single GridTable.Row.
Prop | Type | Default |
|---|---|---|
css | StitchesCss | No default value |
stickyOffset | number | No default value |
GridTable.EmptyState#
Prop | Type | Default |
|---|---|---|
css | StitchesCss | No default value |