XDSTable@xds/core · Table

Usage

Table displays structured data in rows and columns with consistent dimensionality. It supports rich cell content, sorting, selection, pagination, and column management through a composable plugin system. Use Table for data sets with uniform structure; for simpler or inconsistent data, consider a list or card layout instead.

Best practices

GuidancePractices
DoUse density and divider variants to match the information density and scanning needs of your data.
DoCompose rich cell content with XDS components like XDSBadge, XDSStatusDot, and XDSAvatar via renderCell.
Don'tUse a table for data without consistent columns — use a list or card layout for heterogeneous content.
Don'tEnable every plugin at once — add only the features your use case requires to keep the interface focused.

Anatomy

ElementDescription
Column HeaderrequiredDisplays titles, sorting controls, and bulk selection.
Body RowsrequiredRows with consistent data structure.
FooterDisplays summary or totals.
Top BarContains title, toolbar, and filters.
Bottom BarContains pagination controls.
Support PanelsDisplays row details in a side panel.

Import

ts
import {XDSTable} from '@xds/core/Table'

Props

PropTypeDescription
data
T[]Array of data items to render as rows.
columns
XDSTableColumn<T>[]Column definitions. If omitted, columns are auto-generated from data object keys.
idKey
(keyof T & string) | ((item: T) => string | number)Row key for React reconciliation. Pass a property name string or a function. Falls back to row index if omitted.
density
'compact' | 'balanced' | 'spacious' (default: 'balanced')Row density controlling cell padding and font size.
dividers
'rows' | 'columns' | 'grid' | 'none' (default: 'rows')Divider style rendered between cells.
isStriped
boolean (default: false)Applies a background wash to even-numbered rows.
hasHover
boolean (default: false)Applies a hover highlight background to rows on pointer devices.
plugins
Record<string, TablePlugin<T>>Named plugins that extend table behavior via the transform pipeline. Converted to an ordered array internally.
children
ReactNodeChildren mode — render XDSTableRow/XDSTableCell directly instead of using data-driven rendering.
xstyle
StyleXStylesStyleX styles for layout customization (margins, positioning, sizing). Must be a stylex.create() value — not an inline style object like style={{}}.

Sub-components

Table is a compound component with 10 sub-components.

XDSBaseTable

Unstyled structural table component with a plugin transform pipeline and a components prop for swapping in custom row/cell renderers.
PropTypeDescription
data
T[]Array of data items to render as rows.
columns
XDSTableColumn<T>[]Column definitions. If omitted, columns are auto-generated from data object keys.
idKey
(keyof T & string) | ((item: T) => string | number)Row key for React reconciliation. Pass a property name string or a function. Falls back to row index if omitted.
plugins
TablePlugin<T>[]Ordered array of plugins applied as a sequential transform pipeline.
components
{Row?: ComponentType<TableRowComponentProps>; Cell?: ComponentType<TableCellComponentProps>; HeaderCell?: ComponentType<TableHeaderCellComponentProps>}Component overrides for row and cell elements. When provided, these components receive xstyle from plugin transforms.
children
ReactNodeChildren mode — render rows directly in the tbody instead of using data-driven rendering.
tableProps
HTMLAttributes<HTMLTableElement>Additional HTML attributes passed directly to the root <table> element.

XDSTable

Styled, data-driven table with density, dividers, hover highlight, striped rows, and named plugin support.
PropTypeDescription
data
T[]Array of data items to render as rows.
columns
XDSTableColumn<T>[]Column definitions. If omitted, columns are auto-generated from data object keys.
idKey
(keyof T & string) | ((item: T) => string | number)Row key for React reconciliation. Pass a property name string or a function. Falls back to row index if omitted.
density
'compact' | 'balanced' | 'spacious' (default: 'balanced')Row density controlling cell padding and font size.
dividers
'rows' | 'columns' | 'grid' | 'none' (default: 'rows')Divider style rendered between cells.
isStriped
boolean (default: false)Applies a background wash to even-numbered rows.
hasHover
boolean (default: false)Applies a hover highlight background to rows on pointer devices.
plugins
Record<string, TablePlugin<T>>Named plugins that extend table behavior via the transform pipeline. Converted to an ordered array internally.
children
ReactNodeChildren mode — render XDSTableRow/XDSTableCell directly instead of using data-driven rendering.
xstyle
StyleXStylesStyleX styles for layout customization (margins, positioning, sizing). Must be a stylex.create() value — not an inline style object like style={{}}.

XDSTableCell

<td> wrapper that reads XDSTableContext to apply density padding, font size, and divider borders when used inside XDSTable.
PropTypeDescription
children
ReactNodeCell content.

XDSTableHeaderCell

<th> wrapper that reads XDSTableContext to apply density padding, semibold weight, secondary text color, and divider borders when used inside XDSTable.
PropTypeDescription
children
ReactNodeHeader cell content.

XDSTableRow

<tr> wrapper that reads XDSTableContext to apply striped, hover, and divider styles when used inside XDSTable.
PropTypeDescription
childrenrequired
ReactNodeRow cell elements.

useXDSTableColumnSettings

Headless column visibility and ordering management for XDSTable. Provides filtered columns, toggle helpers, and pre-built XDSMultiSelector options for a column picker UI.
PropTypeDescription
columnsrequired
XDSColumnSettingsOption[]All available columns with metadata for the settings UI. Each entry has key, label, optional isAlwaysVisible and group.
activeColumnKeysrequired
string[]Currently active column keys, in display order. Only columns with keys in this array are shown.
onChangeActiveColumnKeysrequired
(keys: string[]) => voidCalled when active columns change (toggle, reorder).
defaultColumnKeys
string[]Default column set for "Reset to default". When omitted, reset shows all columns.

useXDSTablePagination

Headless pagination plugin for XDSTable. Supports client-side slicing, server-side pagination, and cursor-based pagination. Renders XDSPagination controls automatically above, below, or both.
PropTypeDescription
pagerequired
numberCurrent page number (1-based).
onPageChangerequired
(page: number) => voidCalled when the page changes.
totalItems
numberTotal number of items across all pages. Used to calculate total page count.
totalPages
numberTotal number of pages. Use when you know the page count but not item count.
hasMore
booleanWhether more pages exist. Use for cursor-based pagination where the total is unknown.
pageSize
number (default: 10)Number of items per page.
onPageSizeChange
(pageSize: number) => voidCalled when the user changes the page size. Shows a page size dropdown when provided with pageSizeOptions.
pageSizeOptions
number[]Available page size options. Shows a page size selector when provided.
variant
'pages' | 'count' | 'compact' | 'dots' | 'none' (default: 'pages')Visual variant for the pagination controls.
position
'below' | 'above' | 'both' | 'none' (default: 'below')Where to render pagination controls relative to the table.
align
'start' | 'center' | 'end' (default: 'center')Horizontal alignment of the pagination controls.

useXDSTableSelection

Hook that returns a TablePlugin implementing row selection with checkboxes, select-all, and aria-selected. Uses React Context for independent checkbox re-renders.
PropTypeDescription
getIsItemSelectedrequired
(item: T) => booleanReturns whether the given item is currently selected.
onSelectItemrequired
(event: {item: T; isSelected: boolean}) => voidCalled when a row checkbox is toggled. isSelected is the new desired state.
onSelectAllrequired
(event: {isAllSelected: boolean}) => voidCalled when the select-all header checkbox is toggled.
getIsAllSelectedrequired
() => booleanReturns whether all selectable items are currently selected.
getIsIndeterminate
() => booleanReturns whether selection is partial (some but not all). Renders the select-all checkbox in indeterminate state.
getIsItemSelectable
(item: T) => boolean (default: () => true)Returns whether a row should show a checkbox. Non-selectable rows render nothing in the selection cell.
getIsItemEnabled
(item: T) => boolean (default: () => true)Returns whether a row checkbox is interactive. Disabled rows show a disabled checkbox.

useXDSTableSelectionState

State management companion for useXDSTableSelection. Handles disabled/selectable row filtering for select-all automatically — disabled rows are frozen (preserved across select-all/deselect-all), non-selectable rows are excluded.
PropTypeDescription
datarequired
T[]The full data array rendered in the table.
idKeyrequired
(keyof T & string) | ((item: T) => string)Key extractor — property name or function returning a unique string ID.
selectedKeysrequired
Set<string>Controlled set of selected item IDs.
setSelectedKeysrequired
Dispatch<SetStateAction<Set<string>>>Setter for the controlled selected keys.
getIsItemSelectable
(item: T) => boolean (default: () => true)Should this row show a checkbox? Non-selectable rows are excluded from select-all.
getIsItemEnabled
(item: T) => boolean (default: () => true)Is this row checkbox interactive? Disabled rows are frozen — select-all preserves their state.

useXDSTableSortable

Headless multi-sort plugin for XDSTable. The consumer owns sort state and provides a callback. Shift+click enables secondary sort columns. Sort indicators render in header cells automatically.
PropTypeDescription
sortrequired
XDSTableSortState<TSortKey>Current sort state. Ordered array of {sortKey, direction} entries. First entry is the primary sort.
onSortChangerequired
(sort: XDSTableSortState<TSortKey>) => voidCalled when the user clicks a header cell to change sort.
allowUnsortedState
boolean (default: false)Allow cycling back to unsorted. When true: asc, desc, unsorted. When false: asc, desc, asc.
isMultiSortEnabled
boolean (default: false)Enable multi-sort via Shift+click. Regular click still replaces the entire sort state.

Examples

Common configurations, variations, and states.
Table — Column SettingsTable with a column visibility picker in the toolbar. Toggle columns on and off.
tsx
'use client';
import {useState} from 'react';
import {
XDSTable,
useXDSTableColumnSettings,
useXDSTableColumnSettingsState,
} from '@xds/core/Table';
import type {XDSTableColumn} from '@xds/core/Table';
import {XDSMultiSelector} from '@xds/core/MultiSelector';
import {XDSLayout, XDSLayoutHeader, XDSLayoutContent} from '@xds/core/Layout';
import {XDSToolbar} from '@xds/core/Toolbar';
import {XDSText} from '@xds/core/Text';
interface User extends Record<string, unknown> {
id: string;
name: string;
email: string;
role: string;
department: string;
status: string;
}
const users: User[] = [
{
id: '1',
name: 'Alice Johnson',
email: 'alice@example.com',
role: 'Engineer',
department: 'Platform',
status: 'Active',
},
{
id: '2',
name: 'Bob Smith',
email: 'bob@example.com',
role: 'Designer',
department: 'Product',
status: 'Active',
},
{
id: '3',
name: 'Charlie Brown',
email: 'charlie@example.com',
role: 'Manager',
department: 'Platform',
status: 'Away',
},
{
id: '4',
name: 'Diana Prince',
email: 'diana@example.com',
role: 'Engineer',
department: 'Infra',
status: 'Active',
},
{
id: '5',
name: 'Eve Davis',
email: 'eve@example.com',
role: 'Admin',
department: 'Operations',
status: 'Inactive',
},
];
const allColumns: XDSTableColumn<User>[] = [
{key: 'name', header: 'Name'},
{key: 'email', header: 'Email'},
{key: 'role', header: 'Role'},
{key: 'department', header: 'Department'},
{key: 'status', header: 'Status'},
];
const columnOptions = [
{key: 'name' as const, label: 'Name', isAlwaysVisible: true},
{key: 'email' as const, label: 'Email'},
{key: 'role' as const, label: 'Role'},
{key: 'department' as const, label: 'Department'},
{key: 'status' as const, label: 'Status'},
];
const allKeys: string[] = ['name', 'email', 'role', 'department', 'status'];
export default function TableColumnSettingsTable() {
const [activeKeys, setActiveKeys] = useState<string[]>(allKeys);
const state = useXDSTableColumnSettingsState({
columns: columnOptions,
activeColumnKeys: activeKeys,
onChangeActiveColumnKeys: keys => setActiveKeys([...keys]),
});
const plugin = useXDSTableColumnSettings<User>(state.columnSettingsConfig);
const selectorOptions = columnOptions.map(c => ({
value: c.key,
label: c.label,
disabled: c.isAlwaysVisible === true,
}));
return (
<XDSLayout>
<XDSLayoutHeader>
<XDSToolbar
label="Table actions"
startContent={<XDSText type="label">Team</XDSText>}
endContent={
<XDSMultiSelector
label="Columns"
isLabelHidden
options={selectorOptions}
value={[...state.activeColumnKeys]}
onChange={state.setActiveColumnKeys}
/>
}
/>
</XDSLayoutHeader>
<XDSLayoutContent>
<XDSTable
data={users}
columns={allColumns}
idKey="id"
hasHover
plugins={{columnSettings: plugin}}
/>
</XDSLayoutContent>
</XDSLayout>
);
}
Table — Grid DividersCompact table with grid dividers showing both row and column borders, suited for dense numeric data.
tsx
'use client';
import {XDSTable, proportional, pixel} from '@xds/core/Table';
import type {XDSTableColumn} from '@xds/core/Table';
interface Metric extends Record<string, unknown> {
id: string;
metric: string;
q1: string;
q2: string;
q3: string;
q4: string;
}
const metrics: Metric[] = [
{
id: '1',
metric: 'Revenue',
q1: '$1.2M',
q2: '$1.4M',
q3: '$1.3M',
q4: '$1.8M',
},
{
id: '2',
metric: 'Users',
q1: '12,400',
q2: '14,200',
q3: '15,800',
q4: '18,100',
},
{id: '3', metric: 'Retention', q1: '82%', q2: '85%', q3: '84%', q4: '88%'},
{id: '4', metric: 'NPS', q1: '42', q2: '45', q3: '48', q4: '51'},
{id: '5', metric: 'CSAT', q1: '4.2', q2: '4.3', q3: '4.5', q4: '4.6'},
];
const columns: XDSTableColumn<Metric>[] = [
{key: 'metric', header: 'Metric', width: proportional(2)},
{key: 'q1', header: 'Q1', width: pixel(90), align: 'end'},
{key: 'q2', header: 'Q2', width: pixel(90), align: 'end'},
{key: 'q3', header: 'Q3', width: pixel(90), align: 'end'},
{key: 'q4', header: 'Q4', width: pixel(90), align: 'end'},
];
export default function TableGridDividersTable() {
return (
<XDSTable
data={metrics}
columns={columns}
idKey="id"
dividers="grid"
density="compact"
/>
);
}
Table — In CardTable composed inside a card with a heading, demonstrating container bleed alignment.
tsx
'use client';
import {XDSTable} from '@xds/core/Table';
import type {XDSTableColumn} from '@xds/core/Table';
import {XDSCard} from '@xds/core/Card';
import {XDSVStack} from '@xds/core/Layout';
import {XDSHeading} from '@xds/core/Text';
interface User extends Record<string, unknown> {
id: string;
name: string;
role: string;
email: string;
}
const users: User[] = [
{id: '1', name: 'Alice Johnson', role: 'Engineer', email: 'alice@example.com'},
{id: '2', name: 'Bob Smith', role: 'Designer', email: 'bob@example.com'},
{id: '3', name: 'Charlie Brown', role: 'PM', email: 'charlie@example.com'},
{id: '4', name: 'Diana Prince', role: 'Engineer', email: 'diana@example.com'},
];
const columns: XDSTableColumn<User>[] = [
{key: 'name', header: 'Name'},
{key: 'role', header: 'Role'},
{key: 'email', header: 'Email'},
];
export default function TableInCard() {
return (
<XDSCard width={520}>
<XDSVStack gap={3}>
<XDSHeading level={3}>Team Members</XDSHeading>
<XDSTable data={users} columns={columns} idKey="id" hasHover />
</XDSVStack>
</XDSCard>
);
}
Table — Inline FiltersTable with inline filter controls rendered directly below each column header.
tsx
'use client';
import {
XDSTable,
useXDSTableFiltering,
useXDSTableFilterState,
toSearchFilters,
} from '@xds/core/Table';
import type {XDSTableColumn} from '@xds/core/Table';
import {usePowerSearchConfig} from '@xds/core/PowerSearch';
import type {PowerSearchFilter} from '@xds/core/PowerSearch';
interface Employee extends Record<string, unknown> {
id: string;
name: string;
email: string;
role: string;
department: string;
}
const employees: Employee[] = [
{
id: '1',
name: 'Alice Johnson',
email: 'alice@example.com',
role: 'Engineer',
department: 'Platform',
},
{
id: '2',
name: 'Bob Smith',
email: 'bob@example.com',
role: 'Designer',
department: 'Product',
},
{
id: '3',
name: 'Charlie Brown',
email: 'charlie@example.com',
role: 'Manager',
department: 'Platform',
},
{
id: '4',
name: 'Diana Prince',
email: 'diana@example.com',
role: 'Engineer',
department: 'Infra',
},
{
id: '5',
name: 'Eve Davis',
email: 'eve@example.com',
role: 'Admin',
department: 'Operations',
},
];
const fieldDefs = [
{key: 'name', type: 'string', label: 'Name'},
{key: 'email', type: 'string', label: 'Email'},
{
key: 'role',
type: 'enum',
label: 'Role',
enumValues: [
{value: 'Engineer', label: 'Engineer'},
{value: 'Designer', label: 'Designer'},
{value: 'Manager', label: 'Manager'},
{value: 'Admin', label: 'Admin'},
],
},
] as const;
const columns: XDSTableColumn<Employee>[] = [
{key: 'name', header: 'Name', filter: 'name'},
{key: 'email', header: 'Email', filter: 'email'},
{key: 'role', header: 'Role', filter: 'role'},
{key: 'department', header: 'Department'},
];
export default function TableInlineFilterTable() {
const {config, applyFilters} = usePowerSearchConfig(fieldDefs);
const {filters, onFilterChange} = useXDSTableFilterState();
const filterPlugin = useXDSTableFiltering<Employee>({
filters,
onFilterChange,
searchConfig: config,
variant: 'inline',
});
const data = applyFilters(
toSearchFilters(filters, columns, config) as PowerSearchFilter[],
employees,
);
return (
<XDSTable
data={data}
columns={columns}
idKey="id"
plugins={{filter: filterPlugin}}
/>
);
}
Table — Paginated DataPaginated data table navigating through a larger dataset page by page.
tsx
'use client';
import {useState} from 'react';
import {XDSTable, useXDSTablePagination, paginateData} from '@xds/core/Table';
import type {XDSTableColumn} from '@xds/core/Table';
import {XDSSection} from '@xds/core/Section';
interface User extends Record<string, unknown> {
id: string;
name: string;
email: string;
role: string;
}
const names = [
'Alice Johnson',
'Bob Smith',
'Charlie Brown',
'Diana Prince',
'Eve Davis',
'Frank Miller',
'Grace Lee',
'Hank Wilson',
'Ivy Chen',
'Jack Turner',
'Karen White',
'Leo Garcia',
'Mia Thompson',
'Noah Martinez',
'Olivia Clark',
'Paul Harris',
'Quinn Walker',
'Rachel Adams',
'Sam Robinson',
'Tina Scott',
];
const roles = ['Engineer', 'Designer', 'Manager', 'Admin', 'Analyst'];
const users: User[] = names.map((name, i) => ({
id: String(i + 1),
name,
email: `${name.split(' ')[0].toLowerCase()}@example.com`,
role: roles[i % roles.length],
}));
const columns: XDSTableColumn<User>[] = [
{key: 'name', header: 'Name'},
{key: 'email', header: 'Email'},
{key: 'role', header: 'Role'},
];
export default function TablePaginatedTable() {
const [page, setPage] = useState(1);
const pageSize = 5;
const plugin = useXDSTablePagination<User>({
page,
onPageChange: setPage,
totalItems: users.length,
pageSize,
});
return (
<XDSSection>
<XDSTable
data={paginateData(users, page, pageSize)}
columns={columns}
idKey="id"
plugins={{pagination: plugin}}
/>
</XDSSection>
);
}
Table — Popover FiltersTable with popover filter controls triggered by icons in column headers.
tsx
'use client';
import {
XDSTable,
useXDSTableFiltering,
useXDSTableFilterState,
toSearchFilters,
} from '@xds/core/Table';
import type {XDSTableColumn} from '@xds/core/Table';
import {usePowerSearchConfig} from '@xds/core/PowerSearch';
import type {PowerSearchFilter} from '@xds/core/PowerSearch';
interface Employee extends Record<string, unknown> {
id: string;
name: string;
email: string;
role: string;
department: string;
}
const employees: Employee[] = [
{id: '1', name: 'Alice', email: 'alice@example.com', role: 'Engineer', department: 'Platform'},
{id: '2', name: 'Bob', email: 'bob@example.com', role: 'Designer', department: 'Product'},
{id: '3', name: 'Charlie', email: 'charlie@example.com', role: 'Manager', department: 'Platform'},
{id: '4', name: 'Diana', email: 'diana@example.com', role: 'Engineer', department: 'Infrastructure'},
{id: '5', name: 'Eve', email: 'eve@example.com', role: 'Admin', department: 'Operations'},
];
const fieldDefs = [
{key: 'name', type: 'string', label: 'Name'},
{key: 'email', type: 'string', label: 'Email'},
{
key: 'role',
type: 'enum',
label: 'Role',
enumValues: [
{value: 'Engineer', label: 'Engineer'},
{value: 'Designer', label: 'Designer'},
{value: 'Manager', label: 'Manager'},
{value: 'Admin', label: 'Admin'},
],
},
] as const;
const columns: XDSTableColumn<Employee>[] = [
{key: 'name', header: 'Name', filter: 'name'},
{key: 'email', header: 'Email', filter: 'email'},
{key: 'role', header: 'Role', filter: 'role'},
{key: 'department', header: 'Department'},
];
export default function TableFilterableTable() {
const {config, applyFilters} = usePowerSearchConfig(fieldDefs);
const {filters, onFilterChange} = useXDSTableFilterState();
const filterPlugin = useXDSTableFiltering<Employee>({
filters,
onFilterChange,
searchConfig: config,
});
const data = applyFilters(
toSearchFilters(filters, columns, config) as PowerSearchFilter[],
employees,
);
return (
<XDSTable
data={data}
columns={columns}
idKey="id"
plugins={{filter: filterPlugin}}
/>
);
}
Table — Resizable ColumnsTable with draggable column resize handles. Drag the right edge of any header to resize.
tsx
'use client';
import {useState} from 'react';
import {
XDSTable,
useXDSTableColumnResize,
proportional,
pixel,
} from '@xds/core/Table';
interface User extends Record<string, unknown> {
id: string;
name: string;
email: string;
role: string;
}
const users: User[] = [
{
id: '1',
name: 'Alice Johnson',
email: 'alice@example.com',
role: 'Engineer',
},
{id: '2', name: 'Bob Smith', email: 'bob@example.com', role: 'Designer'},
{id: '3', name: 'Charlie Brown', email: 'charlie@example.com', role: 'PM'},
{id: '4', name: 'Diana Prince', email: 'diana@example.com', role: 'Engineer'},
{id: '5', name: 'Eve Davis', email: 'eve@example.com', role: 'Analyst'},
];
const columns = [
{key: 'name', header: 'Name'},
{key: 'email', header: 'Email', width: proportional(2)},
{key: 'role', header: 'Role', width: pixel(120)},
];
export default function TableResizableTable() {
const [columnWidths, setColumnWidths] = useState<Record<string, number>>({});
const resizePlugin = useXDSTableColumnResize({
columnWidths,
columns,
onColumnResizeEnd: updates => {
setColumnWidths(prev => ({...prev, ...updates}));
},
});
return (
<XDSTable
data={users}
columns={columns}
idKey="id"
hasHover
plugins={{columnResize: resizePlugin}}
/>
);
}
Table — Rich Cell ContentTable with rich cell content using XDSLink for emails and XDSBadge for role labels.
tsx
'use client';
import {XDSTable, proportional, pixel} from '@xds/core/Table';
import type {XDSTableColumn} from '@xds/core/Table';
import {XDSBadge} from '@xds/core/Badge';
import {XDSLink} from '@xds/core/Link';
interface User extends Record<string, unknown> {
id: string;
name: string;
email: string;
role: string;
age: number;
}
const users: User[] = [
{
id: '1',
name: 'Alice Johnson',
email: 'alice@example.com',
role: 'Engineer',
age: 30,
},
{
id: '2',
name: 'Bob Smith',
email: 'bob@example.com',
role: 'Designer',
age: 25,
},
{
id: '3',
name: 'Charlie Brown',
email: 'charlie@example.com',
role: 'PM',
age: 35,
},
{
id: '4',
name: 'Diana Prince',
email: 'diana@example.com',
role: 'Engineer',
age: 28,
},
{
id: '5',
name: 'Eve Davis',
email: 'eve@example.com',
role: 'Designer',
age: 32,
},
];
const roleVariant: Record<string, 'blue' | 'purple' | 'green'> = {
Engineer: 'blue',
Designer: 'purple',
PM: 'green',
};
const columns: XDSTableColumn<User>[] = [
{key: 'name', header: 'Name'},
{
key: 'email',
header: 'Email',
width: proportional(2),
renderCell: item => (
<XDSLink label={item.email} href={`mailto:${item.email}`}>{item.email}</XDSLink>
),
},
{
key: 'role',
header: 'Role',
renderCell: item => (
<XDSBadge
label={item.role}
variant={roleVariant[item.role] ?? 'neutral'}
/>
),
},
{key: 'age', header: 'Age', width: pixel(80)},
];
export default function TableRichCellTable() {
return <XDSTable data={users} columns={columns} idKey="id" hasHover />;
}
Table — Row SelectionTable with row selection checkboxes and a select-all header checkbox.
tsx
'use client';
import {useState} from 'react';
import {
XDSTable,
useXDSTableSelection,
useXDSTableSelectionState,
} from '@xds/core/Table';
import type {XDSTableColumn} from '@xds/core/Table';
interface User extends Record<string, unknown> {
id: string;
name: string;
email: string;
role: string;
}
const users: User[] = [
{id: '1', name: 'Alice', email: 'alice@example.com', role: 'Engineer'},
{id: '2', name: 'Bob', email: 'bob@example.com', role: 'Designer'},
{id: '3', name: 'Charlie', email: 'charlie@example.com', role: 'Manager'},
{id: '4', name: 'Diana', email: 'diana@example.com', role: 'Engineer'},
{id: '5', name: 'Eve', email: 'eve@example.com', role: 'Admin'},
];
const columns: XDSTableColumn<User>[] = [
{key: 'name', header: 'Name'},
{key: 'email', header: 'Email'},
{key: 'role', header: 'Role'},
];
export default function TableSelectableTable() {
const [selectedKeys, setSelectedKeys] = useState<Set<string>>(new Set());
const {selectionConfig} = useXDSTableSelectionState<User>({
data: users,
idKey: 'id',
selectedKeys,
setSelectedKeys,
});
const selectionPlugin = useXDSTableSelection<User>(selectionConfig);
return (
<XDSTable
data={users}
columns={columns}
idKey="id"
plugins={{selection: selectionPlugin}}
/>
);
}
Table — Sortable ColumnsTable with sortable columns, click headers to sort ascending or descending.
tsx
'use client';
import {
XDSTable,
useXDSTableSortable,
useXDSTableSortableState,
} from '@xds/core/Table';
import type {XDSTableColumn} from '@xds/core/Table';
interface Employee extends Record<string, unknown> {
id: string;
name: string;
email: string;
role: string;
age: number;
}
const employees: Employee[] = [
{id: '1', name: 'Alice', email: 'alice@example.com', role: 'Engineer', age: 32},
{id: '2', name: 'Bob', email: 'bob@example.com', role: 'Designer', age: 28},
{id: '3', name: 'Charlie', email: 'charlie@example.com', role: 'Manager', age: 45},
{id: '4', name: 'Diana', email: 'diana@example.com', role: 'Engineer', age: 37},
{id: '5', name: 'Eve', email: 'eve@example.com', role: 'Admin', age: 29},
];
const columns: XDSTableColumn<Employee>[] = [
{key: 'name', header: 'Name', sortable: true},
{key: 'email', header: 'Email', sortable: true},
{key: 'role', header: 'Role', sortable: true},
{key: 'age', header: 'Age', sortable: true},
];
export default function TableSortableTable() {
const {sortedData, sortConfig} = useXDSTableSortableState<Employee>({
data: employees,
defaultSort: [{sortKey: 'name', direction: 'ascending'}],
});
const sortablePlugin = useXDSTableSortable<Employee>(sortConfig);
return (
<XDSTable
data={sortedData}
columns={columns}
idKey="id"
plugins={{sortable: sortablePlugin}}
/>
);
}
Table — Striped RowsTable with alternating row colors and hover highlighting for easy scanning.
tsx
'use client';
import {XDSTable, proportional, pixel} from '@xds/core/Table';
import type {XDSTableColumn} from '@xds/core/Table';
interface User extends Record<string, unknown> {
id: string;
name: string;
email: string;
role: string;
age: number;
}
const users: User[] = [
{
id: '1',
name: 'Alice Johnson',
email: 'alice@example.com',
role: 'Engineer',
age: 30,
},
{
id: '2',
name: 'Bob Smith',
email: 'bob@example.com',
role: 'Designer',
age: 25,
},
{
id: '3',
name: 'Charlie Brown',
email: 'charlie@example.com',
role: 'PM',
age: 35,
},
{
id: '4',
name: 'Diana Prince',
email: 'diana@example.com',
role: 'Engineer',
age: 28,
},
{
id: '5',
name: 'Eve Davis',
email: 'eve@example.com',
role: 'Designer',
age: 32,
},
];
const columns: XDSTableColumn<User>[] = [
{key: 'name', header: 'Name'},
{key: 'email', header: 'Email', width: proportional(2)},
{key: 'role', header: 'Role'},
{key: 'age', header: 'Age', width: pixel(80)},
];
export default function TableStripedTable() {
return (
<XDSTable data={users} columns={columns} idKey="id" isStriped hasHover />
);
}

Showcase source

tsx
'use client';
import {XDSTable, proportional, pixel} from '@xds/core/Table';
import {XDSCard} from '@xds/core/Card';
import type {XDSTableColumn} from '@xds/core/Table';
interface User extends Record<string, unknown> {
id: string;
name: string;
email: string;
role: string;
age: number;
}
const users: User[] = [
{id: '1', name: 'Alice Johnson', email: 'alice@example.com', role: 'Engineer', age: 30},
{id: '2', name: 'Bob Smith', email: 'bob@example.com', role: 'Designer', age: 25},
{id: '3', name: 'Charlie Brown', email: 'charlie@example.com', role: 'PM', age: 35},
];
const columns: XDSTableColumn<User>[] = [
{key: 'name', header: 'Name'},
{key: 'email', header: 'Email', width: proportional(2)},
{key: 'role', header: 'Role'},
{key: 'age', header: 'Age', width: pixel(80)},
];
export default function TableShowcase() {
return (
<XDSCard>
<XDSTable data={users} columns={columns} idKey="id" hasHover />
</XDSCard>
);
}