Table Cell
- Stable
- WCAG 2.2 evidence
- RTL
A body cell of a data table.
The Figma Type chooses the content and layout: text, text with subtext, person (avatar, name, email), badge, number, progress,
tags, actions, checkbox and expander. Cells are fluid: Figma widths (180, 260 and 48 px) are minimum widths, text wraps, and
the 52 px row height grows with wrapped content. Accessibility (WCAG 1.3.1, 2.1.1, 4.1.2): real table markup, so each cell is read with its column header; arrow keys move
between cells and, in a cell that holds a button, to the button. The avatar is decorative because the name is text, the tags
are a list and hidden tags are named for assistive technology, and the progress bar has a name and a percentage. Keyboard focus
draws the inset ring (2.4.7). Consumer duties: names for icon buttons and checkboxes in the cell, a textValue when the
content is not a string, and locale-aware formatting of numbers.
import { TableCell } from "@nexera-ui/react";- 7
- examples
- 21
- props
- 1
- live controls
- 1
- platform
- 10
- 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 { TableCell } from "@nexera-ui/react";
<TableCell />Examples 6
The same examples as Storybook, rendered live. Open Code to copy one.
Data table
A sortable table that uses every content type; wide tables scroll sideways inside their own labelled, focusable region.
import { useMemo, useState } from "react";
import { type Selection, type SortDescriptor } from "react-aria-components";
import {
Badge,
EmptyState,
IconButton,
Table,
TableBody,
TableCell,
TableHeader,
TableHeaderCell,
TableRow,
} from "@nexera-ui/react";
import { LuEllipsis, LuPencil, LuUsers } from "react-icons/lu";
interface Employee {
readonly id: string;
readonly name: string;
readonly email: string;
readonly role: string;
readonly status: "Active" | "On leave";
readonly tags: readonly string[];
readonly salary: number;
readonly budget: number;
}
const employees: readonly Employee[] = [
{
id: "ayesha",
name: "Ayesha Khan",
email: "ayesha.khan@company.com",
role: "Product designer",
status: "Active",
tags: ["React", "Design", "Figma", "Research"],
salary: 245000,
budget: 72,
},
{
id: "bilal",
name: "Bilal Ahmed",
email: "bilal.ahmed@company.com",
role: "Engineering manager",
status: "On leave",
tags: ["Go", "Rust"],
salary: 310000,
budget: 45,
},
{
id: "sana",
name: "Sana Iqbal",
email: "sana.iqbal@company.com",
role: "People operations",
status: "Active",
tags: ["Payroll"],
salary: 198000,
budget: 91,
},
{
id: "omar",
name: "Omar Farooq",
email: "omar.farooq@company.com",
role: "Data analyst",
status: "Active",
tags: ["SQL", "Python", "BI"],
salary: 220000,
budget: 18,
},
];
const money = new Intl.NumberFormat("en-PK", {
style: "currency",
currency: "PKR",
maximumFractionDigits: 0,
});
function Directory({
selectable = false,
rows = employees,
}: {
selectable?: boolean;
rows?: readonly Employee[];
}) {
const [sort, setSort] = useState<SortDescriptor>({ column: "name", direction: "ascending" });
const [selected, setSelected] = useState<Selection>(new Set(["bilal"]));
const sorted = useMemo(() => {
const list = [...rows];
list.sort((a, b) => {
const field = sort.column === "role" ? "role" : "name";
const key = sort.column === "salary" ? a.salary - b.salary : a[field].localeCompare(b[field]);
return sort.direction === "descending" ? -key : key;
});
return list;
}, [rows, sort]);
return (
<Table
aria-label="Employees"
sortDescriptor={sort}
onSortChange={setSort}
{...(selectable
? {
selectionMode: "multiple" as const,
selectedKeys: selected,
onSelectionChange: setSelected,
}
: {})}
>
<TableHeader>
<TableHeaderCell id="name" isRowHeader allowsSorting label="Employee" />
<TableHeaderCell id="role" allowsSorting label="Role" />
<TableHeaderCell id="status" label="Status" />
<TableHeaderCell id="tags" label="Skills" />
<TableHeaderCell id="budget" label="Budget used" />
<TableHeaderCell id="salary" allowsSorting align="right" label="Salary" />
<TableHeaderCell id="actions" align="right" label="Actions" />
</TableHeader>
<TableBody
items={sorted}
renderEmptyState={() => (
<EmptyState
icon={<LuUsers />}
title="No employees found"
description="Try another filter."
headingLevel={3}
/>
)}
>
{(row) => (
<TableRow id={row.id}>
<TableCell type="avatar" name={row.name} email={row.email} />
<TableCell type="text-and-subtext" primary={row.role} secondary="Lahore HQ" />
<TableCell type="badge">
<Badge tone={row.status === "Active" ? "success" : "leave"} label={row.status} />
</TableCell>
<TableCell type="tags" tags={row.tags} />
<TableCell
type="progress"
value={`${String(row.budget)}%`}
progress={row.budget}
progressLabel={`Budget used by ${row.name}`}
/>
<TableCell type="number" value={money.format(row.salary)} />
<TableCell type="actions">
<IconButton variant="ghost" aria-label={`Edit ${row.name}`} icon={<LuPencil />} />
<IconButton
variant="ghost"
aria-label={`More actions for ${row.name}`}
icon={<LuEllipsis />}
/>
</TableCell>
</TableRow>
)}
</TableBody>
</Table>
);
}
export function DataTable() {
return <Directory />;
}
With selection
selectionMode="multiple": the header gets a select-all checkbox and every row a checkbox.
import { useMemo, useState } from "react";
import { type Selection, type SortDescriptor } from "react-aria-components";
import {
Badge,
EmptyState,
IconButton,
Table,
TableBody,
TableCell,
TableHeader,
TableHeaderCell,
TableRow,
} from "@nexera-ui/react";
import { LuEllipsis, LuPencil, LuUsers } from "react-icons/lu";
interface Employee {
readonly id: string;
readonly name: string;
readonly email: string;
readonly role: string;
readonly status: "Active" | "On leave";
readonly tags: readonly string[];
readonly salary: number;
readonly budget: number;
}
const employees: readonly Employee[] = [
{
id: "ayesha",
name: "Ayesha Khan",
email: "ayesha.khan@company.com",
role: "Product designer",
status: "Active",
tags: ["React", "Design", "Figma", "Research"],
salary: 245000,
budget: 72,
},
{
id: "bilal",
name: "Bilal Ahmed",
email: "bilal.ahmed@company.com",
role: "Engineering manager",
status: "On leave",
tags: ["Go", "Rust"],
salary: 310000,
budget: 45,
},
{
id: "sana",
name: "Sana Iqbal",
email: "sana.iqbal@company.com",
role: "People operations",
status: "Active",
tags: ["Payroll"],
salary: 198000,
budget: 91,
},
{
id: "omar",
name: "Omar Farooq",
email: "omar.farooq@company.com",
role: "Data analyst",
status: "Active",
tags: ["SQL", "Python", "BI"],
salary: 220000,
budget: 18,
},
];
const money = new Intl.NumberFormat("en-PK", {
style: "currency",
currency: "PKR",
maximumFractionDigits: 0,
});
function Directory({
selectable = false,
rows = employees,
}: {
selectable?: boolean;
rows?: readonly Employee[];
}) {
const [sort, setSort] = useState<SortDescriptor>({ column: "name", direction: "ascending" });
const [selected, setSelected] = useState<Selection>(new Set(["bilal"]));
const sorted = useMemo(() => {
const list = [...rows];
list.sort((a, b) => {
const field = sort.column === "role" ? "role" : "name";
const key = sort.column === "salary" ? a.salary - b.salary : a[field].localeCompare(b[field]);
return sort.direction === "descending" ? -key : key;
});
return list;
}, [rows, sort]);
return (
<Table
aria-label="Employees"
sortDescriptor={sort}
onSortChange={setSort}
{...(selectable
? {
selectionMode: "multiple" as const,
selectedKeys: selected,
onSelectionChange: setSelected,
}
: {})}
>
<TableHeader>
<TableHeaderCell id="name" isRowHeader allowsSorting label="Employee" />
<TableHeaderCell id="role" allowsSorting label="Role" />
<TableHeaderCell id="status" label="Status" />
<TableHeaderCell id="tags" label="Skills" />
<TableHeaderCell id="budget" label="Budget used" />
<TableHeaderCell id="salary" allowsSorting align="right" label="Salary" />
<TableHeaderCell id="actions" align="right" label="Actions" />
</TableHeader>
<TableBody
items={sorted}
renderEmptyState={() => (
<EmptyState
icon={<LuUsers />}
title="No employees found"
description="Try another filter."
headingLevel={3}
/>
)}
>
{(row) => (
<TableRow id={row.id}>
<TableCell type="avatar" name={row.name} email={row.email} />
<TableCell type="text-and-subtext" primary={row.role} secondary="Lahore HQ" />
<TableCell type="badge">
<Badge tone={row.status === "Active" ? "success" : "leave"} label={row.status} />
</TableCell>
<TableCell type="tags" tags={row.tags} />
<TableCell
type="progress"
value={`${String(row.budget)}%`}
progress={row.budget}
progressLabel={`Budget used by ${row.name}`}
/>
<TableCell type="number" value={money.format(row.salary)} />
<TableCell type="actions">
<IconButton variant="ghost" aria-label={`Edit ${row.name}`} icon={<LuPencil />} />
<IconButton
variant="ghost"
aria-label={`More actions for ${row.name}`}
icon={<LuEllipsis />}
/>
</TableCell>
</TableRow>
)}
</TableBody>
</Table>
);
}
export function WithSelection() {
return <Directory selectable />;
}
Empty table
No rows: renderEmptyState shows an EmptyState in place of them.
import { useMemo, useState } from "react";
import { type Selection, type SortDescriptor } from "react-aria-components";
import {
Badge,
EmptyState,
IconButton,
Table,
TableBody,
TableCell,
TableHeader,
TableHeaderCell,
TableRow,
} from "@nexera-ui/react";
import { LuEllipsis, LuPencil, LuUsers } from "react-icons/lu";
interface Employee {
readonly id: string;
readonly name: string;
readonly email: string;
readonly role: string;
readonly status: "Active" | "On leave";
readonly tags: readonly string[];
readonly salary: number;
readonly budget: number;
}
const employees: readonly Employee[] = [
{
id: "ayesha",
name: "Ayesha Khan",
email: "ayesha.khan@company.com",
role: "Product designer",
status: "Active",
tags: ["React", "Design", "Figma", "Research"],
salary: 245000,
budget: 72,
},
{
id: "bilal",
name: "Bilal Ahmed",
email: "bilal.ahmed@company.com",
role: "Engineering manager",
status: "On leave",
tags: ["Go", "Rust"],
salary: 310000,
budget: 45,
},
{
id: "sana",
name: "Sana Iqbal",
email: "sana.iqbal@company.com",
role: "People operations",
status: "Active",
tags: ["Payroll"],
salary: 198000,
budget: 91,
},
{
id: "omar",
name: "Omar Farooq",
email: "omar.farooq@company.com",
role: "Data analyst",
status: "Active",
tags: ["SQL", "Python", "BI"],
salary: 220000,
budget: 18,
},
];
const money = new Intl.NumberFormat("en-PK", {
style: "currency",
currency: "PKR",
maximumFractionDigits: 0,
});
function Directory({
selectable = false,
rows = employees,
}: {
selectable?: boolean;
rows?: readonly Employee[];
}) {
const [sort, setSort] = useState<SortDescriptor>({ column: "name", direction: "ascending" });
const [selected, setSelected] = useState<Selection>(new Set(["bilal"]));
const sorted = useMemo(() => {
const list = [...rows];
list.sort((a, b) => {
const field = sort.column === "role" ? "role" : "name";
const key = sort.column === "salary" ? a.salary - b.salary : a[field].localeCompare(b[field]);
return sort.direction === "descending" ? -key : key;
});
return list;
}, [rows, sort]);
return (
<Table
aria-label="Employees"
sortDescriptor={sort}
onSortChange={setSort}
{...(selectable
? {
selectionMode: "multiple" as const,
selectedKeys: selected,
onSelectionChange: setSelected,
}
: {})}
>
<TableHeader>
<TableHeaderCell id="name" isRowHeader allowsSorting label="Employee" />
<TableHeaderCell id="role" allowsSorting label="Role" />
<TableHeaderCell id="status" label="Status" />
<TableHeaderCell id="tags" label="Skills" />
<TableHeaderCell id="budget" label="Budget used" />
<TableHeaderCell id="salary" allowsSorting align="right" label="Salary" />
<TableHeaderCell id="actions" align="right" label="Actions" />
</TableHeader>
<TableBody
items={sorted}
renderEmptyState={() => (
<EmptyState
icon={<LuUsers />}
title="No employees found"
description="Try another filter."
headingLevel={3}
/>
)}
>
{(row) => (
<TableRow id={row.id}>
<TableCell type="avatar" name={row.name} email={row.email} />
<TableCell type="text-and-subtext" primary={row.role} secondary="Lahore HQ" />
<TableCell type="badge">
<Badge tone={row.status === "Active" ? "success" : "leave"} label={row.status} />
</TableCell>
<TableCell type="tags" tags={row.tags} />
<TableCell
type="progress"
value={`${String(row.budget)}%`}
progress={row.budget}
progressLabel={`Budget used by ${row.name}`}
/>
<TableCell type="number" value={money.format(row.salary)} />
<TableCell type="actions">
<IconButton variant="ghost" aria-label={`Edit ${row.name}`} icon={<LuPencil />} />
<IconButton
variant="ghost"
aria-label={`More actions for ${row.name}`}
icon={<LuEllipsis />}
/>
</TableCell>
</TableRow>
)}
</TableBody>
</Table>
);
}
export function EmptyTable() {
return <Directory rows={[]} />;
}
Narrow container
At 20 rem the table keeps its columns and scrolls sideways in its region; the page does not scroll. Tab to the region and use the arrow keys.
import { useMemo, useState } from "react";
import { type Selection, type SortDescriptor } from "react-aria-components";
import {
Badge,
EmptyState,
IconButton,
Table,
TableBody,
TableCell,
TableHeader,
TableHeaderCell,
TableRow,
} from "@nexera-ui/react";
import { LuEllipsis, LuPencil, LuUsers } from "react-icons/lu";
interface Employee {
readonly id: string;
readonly name: string;
readonly email: string;
readonly role: string;
readonly status: "Active" | "On leave";
readonly tags: readonly string[];
readonly salary: number;
readonly budget: number;
}
const employees: readonly Employee[] = [
{
id: "ayesha",
name: "Ayesha Khan",
email: "ayesha.khan@company.com",
role: "Product designer",
status: "Active",
tags: ["React", "Design", "Figma", "Research"],
salary: 245000,
budget: 72,
},
{
id: "bilal",
name: "Bilal Ahmed",
email: "bilal.ahmed@company.com",
role: "Engineering manager",
status: "On leave",
tags: ["Go", "Rust"],
salary: 310000,
budget: 45,
},
{
id: "sana",
name: "Sana Iqbal",
email: "sana.iqbal@company.com",
role: "People operations",
status: "Active",
tags: ["Payroll"],
salary: 198000,
budget: 91,
},
{
id: "omar",
name: "Omar Farooq",
email: "omar.farooq@company.com",
role: "Data analyst",
status: "Active",
tags: ["SQL", "Python", "BI"],
salary: 220000,
budget: 18,
},
];
const money = new Intl.NumberFormat("en-PK", {
style: "currency",
currency: "PKR",
maximumFractionDigits: 0,
});
function Directory({
selectable = false,
rows = employees,
}: {
selectable?: boolean;
rows?: readonly Employee[];
}) {
const [sort, setSort] = useState<SortDescriptor>({ column: "name", direction: "ascending" });
const [selected, setSelected] = useState<Selection>(new Set(["bilal"]));
const sorted = useMemo(() => {
const list = [...rows];
list.sort((a, b) => {
const field = sort.column === "role" ? "role" : "name";
const key = sort.column === "salary" ? a.salary - b.salary : a[field].localeCompare(b[field]);
return sort.direction === "descending" ? -key : key;
});
return list;
}, [rows, sort]);
return (
<Table
aria-label="Employees"
sortDescriptor={sort}
onSortChange={setSort}
{...(selectable
? {
selectionMode: "multiple" as const,
selectedKeys: selected,
onSelectionChange: setSelected,
}
: {})}
>
<TableHeader>
<TableHeaderCell id="name" isRowHeader allowsSorting label="Employee" />
<TableHeaderCell id="role" allowsSorting label="Role" />
<TableHeaderCell id="status" label="Status" />
<TableHeaderCell id="tags" label="Skills" />
<TableHeaderCell id="budget" label="Budget used" />
<TableHeaderCell id="salary" allowsSorting align="right" label="Salary" />
<TableHeaderCell id="actions" align="right" label="Actions" />
</TableHeader>
<TableBody
items={sorted}
renderEmptyState={() => (
<EmptyState
icon={<LuUsers />}
title="No employees found"
description="Try another filter."
headingLevel={3}
/>
)}
>
{(row) => (
<TableRow id={row.id}>
<TableCell type="avatar" name={row.name} email={row.email} />
<TableCell type="text-and-subtext" primary={row.role} secondary="Lahore HQ" />
<TableCell type="badge">
<Badge tone={row.status === "Active" ? "success" : "leave"} label={row.status} />
</TableCell>
<TableCell type="tags" tags={row.tags} />
<TableCell
type="progress"
value={`${String(row.budget)}%`}
progress={row.budget}
progressLabel={`Budget used by ${row.name}`}
/>
<TableCell type="number" value={money.format(row.salary)} />
<TableCell type="actions">
<IconButton variant="ghost" aria-label={`Edit ${row.name}`} icon={<LuPencil />} />
<IconButton
variant="ghost"
aria-label={`More actions for ${row.name}`}
icon={<LuEllipsis />}
/>
</TableCell>
</TableRow>
)}
</TableBody>
</Table>
);
}
export function NarrowContainer() {
return <Directory selectable />;
}
Long content
Long and translated text wraps in the cells; rows grow instead of truncating.
import { useMemo, useState } from "react";
import { type Selection, type SortDescriptor } from "react-aria-components";
import {
Badge,
EmptyState,
IconButton,
Table,
TableBody,
TableCell,
TableHeader,
TableHeaderCell,
TableRow,
} from "@nexera-ui/react";
import { LuEllipsis, LuPencil, LuUsers } from "react-icons/lu";
interface Employee {
readonly id: string;
readonly name: string;
readonly email: string;
readonly role: string;
readonly status: "Active" | "On leave";
readonly tags: readonly string[];
readonly salary: number;
readonly budget: number;
}
const employees: readonly Employee[] = [
{
id: "ayesha",
name: "Ayesha Khan",
email: "ayesha.khan@company.com",
role: "Product designer",
status: "Active",
tags: ["React", "Design", "Figma", "Research"],
salary: 245000,
budget: 72,
},
{
id: "bilal",
name: "Bilal Ahmed",
email: "bilal.ahmed@company.com",
role: "Engineering manager",
status: "On leave",
tags: ["Go", "Rust"],
salary: 310000,
budget: 45,
},
{
id: "sana",
name: "Sana Iqbal",
email: "sana.iqbal@company.com",
role: "People operations",
status: "Active",
tags: ["Payroll"],
salary: 198000,
budget: 91,
},
{
id: "omar",
name: "Omar Farooq",
email: "omar.farooq@company.com",
role: "Data analyst",
status: "Active",
tags: ["SQL", "Python", "BI"],
salary: 220000,
budget: 18,
},
];
const money = new Intl.NumberFormat("en-PK", {
style: "currency",
currency: "PKR",
maximumFractionDigits: 0,
});
function Directory({
selectable = false,
rows = employees,
}: {
selectable?: boolean;
rows?: readonly Employee[];
}) {
const [sort, setSort] = useState<SortDescriptor>({ column: "name", direction: "ascending" });
const [selected, setSelected] = useState<Selection>(new Set(["bilal"]));
const sorted = useMemo(() => {
const list = [...rows];
list.sort((a, b) => {
const field = sort.column === "role" ? "role" : "name";
const key = sort.column === "salary" ? a.salary - b.salary : a[field].localeCompare(b[field]);
return sort.direction === "descending" ? -key : key;
});
return list;
}, [rows, sort]);
return (
<Table
aria-label="Employees"
sortDescriptor={sort}
onSortChange={setSort}
{...(selectable
? {
selectionMode: "multiple" as const,
selectedKeys: selected,
onSelectionChange: setSelected,
}
: {})}
>
<TableHeader>
<TableHeaderCell id="name" isRowHeader allowsSorting label="Employee" />
<TableHeaderCell id="role" allowsSorting label="Role" />
<TableHeaderCell id="status" label="Status" />
<TableHeaderCell id="tags" label="Skills" />
<TableHeaderCell id="budget" label="Budget used" />
<TableHeaderCell id="salary" allowsSorting align="right" label="Salary" />
<TableHeaderCell id="actions" align="right" label="Actions" />
</TableHeader>
<TableBody
items={sorted}
renderEmptyState={() => (
<EmptyState
icon={<LuUsers />}
title="No employees found"
description="Try another filter."
headingLevel={3}
/>
)}
>
{(row) => (
<TableRow id={row.id}>
<TableCell type="avatar" name={row.name} email={row.email} />
<TableCell type="text-and-subtext" primary={row.role} secondary="Lahore HQ" />
<TableCell type="badge">
<Badge tone={row.status === "Active" ? "success" : "leave"} label={row.status} />
</TableCell>
<TableCell type="tags" tags={row.tags} />
<TableCell
type="progress"
value={`${String(row.budget)}%`}
progress={row.budget}
progressLabel={`Budget used by ${row.name}`}
/>
<TableCell type="number" value={money.format(row.salary)} />
<TableCell type="actions">
<IconButton variant="ghost" aria-label={`Edit ${row.name}`} icon={<LuPencil />} />
<IconButton
variant="ghost"
aria-label={`More actions for ${row.name}`}
icon={<LuEllipsis />}
/>
</TableCell>
</TableRow>
)}
</TableBody>
</Table>
);
}
export function LongContent() {
return (
<Directory
rows={[
{
id: "long",
name: "Muhammad Abdul Rehman Siddiqui-Khan",
email: "muhammad.abdul.rehman.siddiqui-khan@very-long-company-domain.example.com",
role: "Leitender Produktdesigner fuer mobile Anwendungen",
status: "On leave",
tags: ["Barrierefreiheit", "Design-Systeme", "Forschung", "Prototyping"],
salary: 1245000,
budget: 64,
},
]}
/>
);
}
Right to left
Right-to-left: columns run from the right, the number and action columns align to the left edge, numbers stay left-to-right.
import {
Table,
TableBody,
TableCell,
TableHeader,
TableHeaderCell,
TableRow,
} from "@nexera-ui/react";
export function RightToLeft() {
return (
<Table aria-label="الموظفون" selectionMode="multiple">
<TableHeader>
<TableHeaderCell id="name" isRowHeader allowsSorting label="الموظف" />
<TableHeaderCell id="salary" align="right" label="الراتب" />
</TableHeader>
<TableBody>
<TableRow id="1">
<TableCell type="avatar" name="عائشة خان" email="ayesha@company.com" initials="ع خ" />
<TableCell type="number" value="245,000" />
</TableRow>
</TableBody>
</Table>
);
}
Props 21
Press "Try it" on a card to load that prop into the playground.
21 props shown
typeNexera"number" | "avatar" | "progress" | "tags" | "text" | "text-and-subtext" | "badge" | "actions" | "checkbox" | "expander" | "expander-open"Text cell (the default).
Two lines of text.
Person cell.
Badge cell.
Number cell. Give the column header align="right" too.
Progress cell.
Tags cell.
Actions cell.
Checkbox cell, 48 px wide. TableRow adds it when the table is selectable.
Expander cell, 48 px wide. Figma has two variants that differ only in the chevron (LuChevronRight, LuChevronDown); pass
the chevron that matches the row's state. Both values lay out the same.
children*NexeraReactNodeThe text. Wraps instead of truncating.
A <Badge />. The badge text carries the meaning, not its colour.
IconButtons, each with its own accessible name ("Edit Ayesha Khan").
A <Checkbox slot="selection" aria-label="..." />.
An IconButton with an accessible name ("Expand Ayesha Khan") and aria-expanded; its chevron is wrapped in DirectionalIcon when it points right.
classNameNexerastringExtra classes for the cell, merged last so they win over the defaults.
styleNexeraCSSPropertiesInline style for the cell.
primary*NexeraReactNodeBold first line.
secondary*NexeraReactNodeMuted second line.
name*NexerastringName.
emailNexerastringEmail or role under the name. Wraps; shown when provided.
initialsNexerastringLetters in the avatar. Defaults to the first letters of the first and last word
of name.
avatarNexeraReactNodeReplaces the initials avatar, for example <Avatar type="photo" src="..." size="md" isDecorative />. Decorative: the name is text.
value*NexerastringThe figure, formatted for the locale. Kept left-to-right. The value shown before the bar.
progress*NexeranumberFill of the bar, 0 to 100.
progressLabel*NexerastringWhat the bar measures, as its accessible name, for example "Budget used". The bar announces its percentage ("72%"). Translate it.
tags*Nexerareadonly string[]All the tags.
maxTagsNexeranumberHow many tags are shown before the "+N".
moreLabelNexera(hidden: readonly string[]) => stringText for assistive technology after the visible tags, from the tags that are not shown. Translate it.
idReact AriaKeyThe unique id of the cell.
textValueReact AriastringA string representation of the cell's contents, used for features like typeahead.
colSpanReact ArianumberIndicates how many columns the data cell spans.
focusModeReact Aria"child" | "cell"Whether the cell or its first focusable child element should be focused when the cell is focused. Defaults to 'child' in arrow keyboard navigation mode and 'cell' in tab keyboard navigation mode.
allowsArrowNavigationReact AriabooleanWhether the cell should support arrow key navigation even when the containing table uses tab keyboard navigation. Allows users to navigate between cells and rows with arrow keys while focus is on an interactive child element within the cell.
* 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
6 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.10ReflowLevel AA · tested directly
- 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:.
<TableCell className="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.