Table Header Cell
- Stable
- WCAG 2.2 evidence
- RTL
A column header of a data table. A column with allowsSorting is sorted by click, Enter or Space; the header shows the
sort icon (ArrowUpDown while unsorted, ArrowUp ascending, ArrowDown descending) and exposes aria-sort; the tables
keeps arrow-key grid navigation between headers and cells (WCAG 1.3.1, 2.1.1, 4.1.2). The state is an icon and a darker label,
not colour alone (1.4.1); keyboard focus draws the inset ring (2.4.7). Use it through TableHeader. Do not use it as a layout heading. Consumer duties: the column title wording, and announcing a
sort result when the data changes (announceStatus, WCAG 4.1.3).
import { TableHeaderCell } from "@nexera-ui/react";- 8
- examples
- 16
- props
- 3
- live controls
- 1
- platform
- 9
- WCAG criteria
- 4
- blocks use it
Try every prop. Copy the code.
Change the props and the code updates. Check light and dark, LTR and RTL, and phone width.
import { TableHeaderCell } from "@nexera-ui/react";
<TableHeaderCell />Examples 7
The same examples as Storybook, rendered live. Open Code to copy one.
Sortable
Figma Sort=Sortable: allowsSorting. Click, or Tab to it and press Enter or Space; the direction comes from the table.
import { TableHeaderCell } from "@nexera-ui/react";
<TableHeaderCell id="column" isRowHeader label="Column" allowsSorting />;
Sorted
Figma Sort=Ascending and Sort=Descending: set by the table's sortDescriptor. The label turns darker and the icon changes.
import { type SortDescriptor } from "react-aria-components";
import {
Table,
TableBody,
TableCell,
TableHeader,
TableHeaderCell,
TableRow,
type TableHeaderCellProps,
} from "@nexera-ui/react";
function Host({
header,
sortDescriptor,
selectionMode,
label,
}: {
header: TableHeaderCellProps;
sortDescriptor?: SortDescriptor;
selectionMode?: "none" | "single" | "multiple";
label: string;
}) {
const cells = selectionMode === undefined || selectionMode === "none" ? 1 : 1;
return (
<Table
aria-label={label}
{...(sortDescriptor === undefined ? {} : { sortDescriptor })}
{...(selectionMode === undefined ? {} : { selectionMode })}
>
<TableHeader>
<TableHeaderCell {...header} />
</TableHeader>
<TableBody>
<TableRow id="row">
{Array.from({ length: cells }, (_, index) => (
<TableCell key={index}>Product designer</TableCell>
))}
</TableRow>
</TableBody>
</Table>
);
}
export function Sorted() {
return (
<div className="flex flex-wrap gap-4">
<div className="w-45">
<Host
label="Ascending"
header={{ ...args, id: "a", allowsSorting: true }}
sortDescriptor={{ column: "a", direction: "ascending" }}
/>
</div>
<div className="w-45">
<Host
label="Descending"
header={{ ...args, id: "d", allowsSorting: true }}
sortDescriptor={{ column: "d", direction: "descending" }}
/>
</div>
</div>
);
}
Aligned right
Figma Align=Right: for number columns. It means the end edge, so it flips in right-to-left layouts.
import { TableHeaderCell } from "@nexera-ui/react";
<TableHeaderCell id="column" isRowHeader label="Salary" align="right" allowsSorting />;
Select column
Figma Type=Select: the 48 px select-all column. The Table adds it for selectionMode="multiple".
import { type SortDescriptor } from "react-aria-components";
import {
Table,
TableBody,
TableCell,
TableHeader,
TableHeaderCell,
TableRow,
type TableHeaderCellProps,
} from "@nexera-ui/react";
function Host({
header,
sortDescriptor,
selectionMode,
label,
}: {
header: TableHeaderCellProps;
sortDescriptor?: SortDescriptor;
selectionMode?: "none" | "single" | "multiple";
label: string;
}) {
const cells = selectionMode === undefined || selectionMode === "none" ? 1 : 1;
return (
<Table
aria-label={label}
{...(sortDescriptor === undefined ? {} : { sortDescriptor })}
{...(selectionMode === undefined ? {} : { selectionMode })}
>
<TableHeader>
<TableHeaderCell {...header} />
</TableHeader>
<TableBody>
<TableRow id="row">
{Array.from({ length: cells }, (_, index) => (
<TableCell key={index}>Product designer</TableCell>
))}
</TableRow>
</TableBody>
</Table>
);
}
export function SelectColumn() {
return (
<div className="w-60">
<Host
label="Select column"
header={{ ...args, isRowHeader: false }}
selectionMode="multiple"
/>
</div>
);
}
Controlled sorting
Controlled sorting with sortDescriptor and onSortChange on the Table.
import { useState } from "react";
import { type SortDescriptor } from "react-aria-components";
import {
Table,
TableBody,
TableCell,
TableHeader,
TableHeaderCell,
TableRow,
} from "@nexera-ui/react";
function SortableHeadersDemo() {
const [sort, setSort] = useState<SortDescriptor>({ column: "name", direction: "ascending" });
return (
<Table aria-label="Sortable columns" sortDescriptor={sort} onSortChange={setSort}>
<TableHeader>
<TableHeaderCell id="name" isRowHeader allowsSorting label="Name" />
<TableHeaderCell id="role" allowsSorting label="Role" />
<TableHeaderCell id="salary" allowsSorting align="right" label="Salary" />
</TableHeader>
<TableBody>
<TableRow id="1">
<TableCell>Ayesha Khan</TableCell>
<TableCell>Product designer</TableCell>
<TableCell type="number" value="Rs 245,000" />
</TableRow>
</TableBody>
</Table>
);
}
export function ControlledSorting() {
return <SortableHeadersDemo />;
}
Long content
Long and translated titles wrap in the header; nothing is truncated.
import { TableHeaderCell } from "@nexera-ui/react";
<TableHeaderCell
id="column"
isRowHeader
label="Durchschnittliches Bruttogehalt im laufenden Geschaeftsjahr"
allowsSorting
/>;
Right to left
Right-to-left: the title sits at the start (right), and align="right" moves to the left edge.
import {
Table,
TableBody,
TableCell,
TableHeader,
TableHeaderCell,
TableRow,
} from "@nexera-ui/react";
export function RightToLeft() {
return (
<Table aria-label="الموظفون">
<TableHeader>
<TableHeaderCell id="name" isRowHeader allowsSorting label="الاسم" />
<TableHeaderCell id="salary" align="right" allowsSorting label="الراتب" />
</TableHeader>
<TableBody>
<TableRow id="1">
<TableCell>عائشة خان</TableCell>
<TableCell type="number" value="245,000" />
</TableRow>
</TableBody>
</Table>
);
}
Props 16
Press "Try it" on a card to load that prop into the playground.
16 props shown
labelNexeraReactNodeColumn title. It is the accessible name of the column and the text read for
each cell ("Department: Engineering"). With type="select" it is only the visually hidden name of the column in a
single-selection table (which has no select-all checkbox). Wraps instead of truncating.
typeNexera"label" | "select"label is a column title; select is the select-all column that holds the table's header checkbox. The
Table wrapper adds the select column itself when the table is selectable.
alignNexera"left" | "right"Alignment of the title. right is for number columns and mirrors to the left in right-to-left layouts
(it means the end edge). Set the same on the column's cells.
allowsSortingNexerabooleanWhether clicking the header, or pressing Enter or Space on it, sorts by this column. The direction comes from the Table's sortDescriptor, and React Aria sets aria-sort on the
header. Replaces the plan's sort enumeration, which would duplicate the table's own sort state.
textValueNexerastringPlain text of the title, for the announcement of sorting and cell navigation. Defaults to label when it is a string.
selectAllLabelNexerastringName of the select-all checkbox (type="select"). Translate it.
classNameNexerastringExtra classes for the header cell, merged last so they win over the defaults.
styleNexeraCSSPropertiesInline style for the header cell.
idReact AriaKeyThe unique id of the column.
isRowHeaderReact AriabooleanWhether a column is a [row header](https://www.w3.org/TR/wai-aria-1.1/#rowheader) and should be announced by assistive technology during row navigation.
widthReact AriaColumnSize | nullThe width of the column. This prop only applies when the <Table> is wrapped in a
<ResizableTableContainer>.
defaultWidthReact AriaColumnSize | nullThe default width of the column. This prop only applies when the <Table> is wrapped in a
<ResizableTableContainer>.
minWidthReact AriaColumnStaticSize | nullThe minimum width of the column. This prop only applies when the <Table> is wrapped in a
<ResizableTableContainer>.
maxWidthReact AriaColumnStaticSize | nullThe maximum width of the column. This prop only applies when the <Table> is wrapped in a
<ResizableTableContainer>.
focusModeReact Aria"child" | "cell"Whether the column header or its first focusable child element should be focused when the column header is focused. Defaults to 'child' in arrow keyboard navigation mode and 'cell' in tab keyboard navigation mode.
allowsArrowNavigationReact AriabooleanWhether the column should support arrow key navigation even when the containing table uses tab keyboard navigation. Allows users to navigate between columns and rows with arrow keys while focus is on an interactive child element within the column header.
* Required. React Aria props shown are the ones most apps use; the component accepts the rest of its React Aria props too.
Accessible by default.
Built on React Aria, and covered by the WCAG 2.2 evidence generated on every build.
WCAG 2.2 evidence
5 direct · 4 supporting- 1.1.1Non-text ContentLevel A · tested directly
- 1.3.1Info and RelationshipsLevel A · tested directly
- 1.4.1Use of ColorLevel A · tested directly
- 1.4.4Resize TextLevel AA · supporting test
- 1.4.11Non-text ContrastLevel AA · supporting test
- 1.4.12Text SpacingLevel AA · supporting test
- 2.1.1KeyboardLevel A · tested directly
- 2.4.7Focus VisibleLevel AA · supporting test
- 4.1.2Name, Role, ValueLevel A · tested directly
Fits any width.
Nexera components respond to the space they are given. Drag the corner of the frame, or pick a width.
Styling hooks
Pass className to add Tailwind classes (merged last). State is exposed as data attributes, so you can style it with variants like data-pressed:.
data-allows-sortingdata-hovered
<TableHeaderCell className="data-allows-sorting:opacity-90 shadow-sm" />Used in blocks
- Data tableLists, tables and records
- Record details drawerLists, tables and records
- Keyboard shortcutsLists, tables and records
- Billing and planSettings and preferences
Related components
- AccordionItemOne collapsible section of an FAQ or settings page.
- AreaChartA stacked area chart: layers piled up over time with the title, range switch, actions and legend of the card.
- AvailabilityRowOne person's availability across a window of the day: an avatar with initials and the name, and a timeline with the busy periods drawn as bars.
- AvatarA person or organisation shown as a photo, initials or an icon.
- AvatarGroupA row of overlapping avatars with a "+N" chip for the rest.
- AvatarLabelAn avatar with a name and an optional subtitle beside it, for a person in a list, a header or a mention.
- BadgeShort status or category label: `Tone` x `Variant` x `Size`, with an optional dot, leading icon and remove button.
- BadgeGroupA pill that pairs a small badge with a short message, for an announcement or a status line.