Skip to content
CraftDocs
GitHub
Home
Home
Changelog
What's New
Guide
Guide
Getting Started
Principles
Styling Components
Theme System
Foundations
All Tokens
Color
Elevation
Icons
Motion
Shape
Spacing
Typography
Libraries
Libraries
@xds/cli
@xds/core
Themes
Themes
Theme: daily
Default Theme
Theme: matcha
Neutral Theme
Components
Components
AppShell
AspectRatio
Avatar
Avatar
AvatarStatusDot
Badge
Banner
Breadcrumbs
BreadcrumbItem
Breadcrumbs
Button
Button
IconButton
ToggleButton
ToggleButtonGroup
Calendar
Card
Carousel
Chat
ChatComposer
ChatComposerDrawer
ChatComposerInput
ChatComposerTokenElement
ChatDictationButton
ChatLayout
ChatLayoutScrollButton
ChatMessage
ChatMessageBubble
ChatMessageList
ChatMessageMetadata
ChatSendButton
ChatSystemMessage
ChatTokenizedText
ChatToolCalls
Checkbox
CheckboxInput
CheckboxList
CheckboxListItem
ClickableCard
Code
CodeBlock
Collapsible
Collapsible
CollapsibleGroup
useXDSCollapsible
CommandPalette
CommandPalette
CommandPaletteEmpty
CommandPaletteFooter
CommandPaletteGroup
CommandPaletteInput
CommandPaletteItem
CommandPaletteList
DateInput
Dialog
AlertDialog
Dialog
DialogHeader
useXDSImperativeAlertDialog
useXDSImperativeDialog
Divider
DropdownMenu
DropdownMenu
DropdownMenuDivider
DropdownMenuItem
DropdownMenuItemData
DropdownMenuSection
EmptyState
Field
Field
FieldLabel
FieldStatus
Heading
HoverCard
Icon
Kbd
Layout
Center
FormLayout
Grid
GridSpan
HStack
Layout
LayoutContainer
LayoutContent
LayoutFooter
LayoutHeader
LayoutPanel
Section
StackItem
VStack
Link
List
List
ListItem
Markdown
MetadataList
MetadataList
MetadataListItem
MobileNav
MoreMenu
NavHeadingMenu
NavIcon
NumberInput
OverflowList
Pagination
Popover
PowerSearch
ProgressBar
Radio
RadioList
RadioListItem
Resizable
ResizeHandle
useXDSResizable
SegmentedControl
SegmentedControl
SegmentedControlItem
SelectableCard
Selector
MultiSelector
Selector
SelectorOption
SideNav
SideNav
SideNavCollapseButton
SideNavHeading
SideNavItem
SideNavSection
Skeleton
Slider
Spinner
StatusDot
Switch
Table
BaseTable
Table
TableCell
TableHeaderCell
TableRow
useXDSTableColumnSettings
useXDSTablePagination
useXDSTableSelection
useXDSTableSelectionState
useXDSTableSortable
Tabs
Tab
TabList
TabMenu
Text
TextArea
TextInput
Thumbnail
TimeInput
Timestamp
Toast
Toast
useXDSToast
Token
Tokenizer
Toolbar
Tooltip
TopNav
TopNav
TopNavHeading
TopNavItem
TopNavMegaMenu
TopNavMegaMenuFeaturedCard
TopNavMegaMenuItem
TopNavMenu
TreeList
Typeahead
BaseTypeahead
Typeahead
TypeaheadItem
useXDSHoverCard
useXDSPopover
useXDSTooltip
Utilities
Utilities
LinkProvider
MediaTheme
SyntaxTheme
Theme
useClickableContainer
useEntryAnimation
useFocusTrap
useGridFocus
useImageMode
useInputContainer
useListFocus
useMediaQuery
useOverflow
useScrollLock
useScrollOverflow
useXDSLayer
useXDSStreamingText
Terms of UsePrivacy Policy
Type to search
↑↓Navigate↵SelectEscClose
Table@xds/core · XDSTable v0.0.13

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.
ts
import {XDSTable} from '@xds/core/Table'

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.

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>
);
}