TableSelection
Row checkboxes for Table's bulk actions. Native checkboxes rendered on the server submit the checked ids with their form before any JavaScript runs; one small client island adds select-all, Shift-click ranges and Escape.
Import
import { TableSelectAll, TableSelectRow, TableSelection } from '@nizivo/react/table-selection';
// Once per app (Getting started), after
// @nizivo/tokens/tokens.css and @nizivo/react/css/base.css and @nizivo/react/css/checkbox.css and @nizivo/react/css/table.css
import '@nizivo/react/css/table-selection.css';Static or interactive
Server, with one client part. TableSelectAll and TableSelectRow render native checkboxes with
Checkbox's look and no client JavaScript; TableSelection is one client island around the whole
table. Import them from @nizivo/react/table-selection, next to Table from
@nizivo/react/table: a page that does not use selection loads none of it, under Turbopack and
webpack alike. How to choose.
In a client app (a Vite or React Router single-page app, or a 'use client' file) it is an ordinary React component: event handlers, refs and state work as usual. In a client app.
Bulk actions
Put a TableSelectRow in each row and a TableSelectAll in the header, and wrap the table in
TableSelection. Each row's box is a native checkbox that submits its value under name
(ids by default) with its form, so a bulk action works before any JavaScript runs: pass form
with the id of a form outside the table, as the example's "Archive selected" does. Give every row's
box its own name (aria-label="Select order 1042"). A checked row paints bg/brand-subtle through
CSS, with no script.
TableSelection adds what a native checkbox cannot do: the header's box checks or clears every
row and shows the mixed state when only some are selected, Shift-click selects a range, and Escape
clears the selection. A client owner hears every change through onSelectionChange(ids); a Server
Component reads the selection from the submitted form. Rows with disabled boxes are left out.
The action bar
BulkActionBar is the bar over the table, with how many rows are
selected and the actions; TableSelection writes its count.
Examples
Basic
Bulk actions: each row's checkbox is a native input of the form in the BulkActionBar (form), so "Archive" submits the checked ids even before JavaScript runs. TableSelection adds the select-all box (checked, mixed or clear), Shift-click ranges, Escape to clear, and the bar's "1 selected".
| Product | Inventory | Price | |
|---|---|---|---|
| Stoneware mug | 124 in stock | $28.00 | |
| Linen apron | 8 in stock | $42.00 | |
| Ceramic coaster set | Sold out | $18.50 | |
| Speckled bowl | 36 in stock | $24.00 |
import type { JSX } from 'react';
import { Button } from '@nizivo/react/button';
import { Stack } from '@nizivo/react/stack';
import { Table } from '@nizivo/react/table';
import { BulkActionBar } from '@nizivo/react/bulk-action-bar';
import { TableSelectAll, TableSelection, TableSelectRow } from '@nizivo/react/table-selection';
const MONEY = new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' });
const PRODUCTS = [
['mug-014', 'Stoneware mug', '124 in stock', 28],
['apron-002', 'Linen apron', '8 in stock', 42],
['coaster-007', 'Ceramic coaster set', 'Sold out', 18.5],
['bowl-021', 'Speckled bowl', '36 in stock', 24],
] as const;
export function TableSelectionExample(): JSX.Element {
return (
<Stack gap="3">
<TableSelection>
<BulkActionBar defaultCount={1}>
<form id="products-bulk" action="#">
<Button type="submit" size="sm" variant="secondary">
Archive selected
</Button>
</form>
</BulkActionBar>
<Table.Container>
<Table.Root>
<Table.Caption visuallyHidden>Products</Table.Caption>
<Table.Header>
<Table.Row>
<Table.Head>
<TableSelectAll />
</Table.Head>
<Table.Head>Product</Table.Head>
<Table.Head>Inventory</Table.Head>
<Table.Head numeric>Price</Table.Head>
</Table.Row>
</Table.Header>
<Table.Body>
{PRODUCTS.map(([id, name, stock, price]) => (
<Table.Row key={id}>
<Table.Cell>
<TableSelectRow
value={id}
form="products-bulk"
aria-label={`Select ${name}`}
defaultChecked={id === 'apron-002'}
/>
</Table.Cell>
<Table.Head scope="row">{name}</Table.Head>
<Table.Cell>{stock}</Table.Cell>
<Table.Cell numeric>{MONEY.format(price)}</Table.Cell>
</Table.Row>
))}
</Table.Body>
</Table.Root>
</Table.Container>
</TableSelection>
</Stack>
);
}
Props
TableSelectAll
The header's checkbox: checks or clears every row of its TableSelection, and shows the mixed state when some are selected. It submits nothing. Without TableSelection it does nothing.
Server component Renders data-slot="table-select-all" with the class nz-table-select-all. Props: TableSelectAllProps. Source: src/table-selection/table-selection.tsx.
| Name | Type | Default | Description |
|---|---|---|---|
labels | Partial<TableSelectionLabels> | – | An |
- Also takes every attribute of
<input>exceptchecked,children,defaultChecked,type. (Omit<ComponentPropsWithRef<"input">, "type" | "children" | "checked" | "defaultChecked">)
TableSelectRow
A row's checkbox: a native input that submits value under name with its form (pass form to reach a form outside the table). The row shows bg/brand-subtle while it is checked, through CSS.
Server component Renders data-slot="table-select-row" with the class nz-table-select-row. Props: TableSelectRowProps. Source: src/table-selection/table-selection.tsx.
| Name | Type | Default | Description |
|---|---|---|---|
aria-labelrequired | string | – | The checkbox's name, unique per row, for example "Select order 1042". |
defaultChecked | boolean | false | Selected when the page loads. |
name | string | 'ids' | |
valuerequired | string | – | The row's id: what a form receives under |
- Also takes every attribute of
<input>exceptchecked,children,defaultChecked,type;aria-label,name,valueare redeclared above. (Omit<ComponentPropsWithRef<"input">, "type" | "children" | "checked" | "defaultChecked">)
TableSelection
Client component Renders data-slot="table-selection" with the class nz-table-selection. Props: TableSelectionProps. Source: src/table-selection/table-selection.client.tsx.
| Name | Type | Default | Description |
|---|---|---|---|
onSelectionChange | (ids: string[]) => void | – | The selected rows' values, after every change (and once on mount when a row is already checked, for example by the browser's form restoration). For a client owner; a Server Component reads the selection from the form. |
render | RenderProp<Record<string, never>> | – |
- Also takes every attribute of
<div>exceptref. (Omit<ComponentPropsWithRef<"div">, "ref">)
render takes an element, or, in client code, a function of the props and no state.
Other exports
TableSelectAllPropstypeimport type { TableSelectAllProps } from '@nizivo/react/table-selection'The props of
TableSelectAll, listed above.TableSelectRowPropstypeimport type { TableSelectRowProps } from '@nizivo/react/table-selection'The props of
TableSelectRow, listed above.TableSelectionLabelstypeimport type { TableSelectionLabels } from '@nizivo/react/table-selection'Row selection's own strings (§16.4): serialisable, English defaults.
Name Type Default Description selectAllrequiredstring'Select all rows'The select-all checkbox's name.
TableSelectionPropstypeimport type { TableSelectionProps } from '@nizivo/react/table-selection'The props of
TableSelection, listed above.
Data attributes and slots
Style parts by class (.nz-<slot>) and state by data attribute. Attribute meanings come from contracts/vocabulary.json.
| Slot | Rendered by | Data attributes |
|---|---|---|
table-select-all.nz-table-select-all | TableSelectAllroot (no render) | none |
table-select-all-indicator.nz-table-select-all-indicator | TableSelectAllinner element (CSS only) | none |
table-select-all-input.nz-table-select-all-input | TableSelectAllinner element (CSS only) | none |
table-select-row.nz-table-select-row | TableSelectRowroot (no render) | none |
table-select-row-indicator.nz-table-select-row-indicator | TableSelectRowinner element (CSS only) | none |
table-select-row-input.nz-table-select-row-input | TableSelectRowinner element (CSS only) | none |
table-selection.nz-table-selection | TableSelectionroot (takes render) | none |
CSS hooks
Set a hook on the component's class or any ancestor to restyle it without specificity games; leave it unset for the default shown.
This stylesheet reads no public hook.
Layers: nz.components. Classes: .nz-table-selection.
Accessibility
Nothing in this component takes focus or keyboard input.
Keyboard
| Keys | Action |
|---|---|
| Tab | Reaches the header's checkbox, then each row's, with the table's other controls in document order. |
| Space | On a row's checkbox, checks or clears it; on the header's, every row. |
| Shift + click | On a row's checkbox, gives every row between it and the last one clicked the same state. |
| Escape | Inside a TableSelection with rows checked, clears them; with none, does nothing, so an enclosing Dialog still closes. |
Source: Native checkboxes; TableSelection's own listener for select-all, Shift-click and Escape. Checked by the browser lane (test/browser/table-selection.spec.tsx) in Chromium, Firefox and WebKit.
Figma
Node ids from design/component-map.json, which nz gen writes from component.json. The links open the Nizivo Figma library, which may ask you to sign in.
- Canonical: none
- Ghost masters: none
Table
A native table in server-rendered parts, for orders, products and customers. Rows can link to a detail page as a whole, columns sort through the URL, and number columns line up. No client JS.
Tabs
In-page views of one subject, one panel at a time, as WAI-ARIA tabs. Only the selected panel is rendered, on the server too; keepMounted keeps a panel's state while it is hidden. When each view is a URL, use TabNav; to pick a range or a value, use SegmentedControl.