tsimport {XDSTable} from '@xds/core/Table'
| Guidance | Practices |
|---|---|
| Do | Use density and divider variants to match the information density and scanning needs of your data. |
| Do | Compose rich cell content with XDS components like XDSBadge, XDSStatusDot, and XDSAvatar via renderCell. |
| Don't | Use a table for data without consistent columns — use a list or card layout for heterogeneous content. |
| Don't | Enable every plugin at once — add only the features your use case requires to keep the interface focused. |
| Prop | Type | Description |
|---|---|---|
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 | ReactNode | Children 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. |
| Prop | Type | Description |
|---|---|---|
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 | ReactNode | Children mode — render XDSTableRow/XDSTableCell directly instead of using data-driven rendering. |
xstyle | StyleXStyles | StyleX styles for layout customization (margins, positioning, sizing). Must be a stylex.create() value — not an inline style object like style={{}}. |
| Prop | Type | Description |
|---|---|---|
children | ReactNode | Cell content. |
| Prop | Type | Description |
|---|---|---|
children | ReactNode | Header cell content. |
| Prop | Type | Description |
|---|---|---|
childrenrequired | ReactNode | Row cell elements. |
| Prop | Type | Description |
|---|---|---|
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[]) => void | Called when active columns change (toggle, reorder). |
defaultColumnKeys | string[] | Default column set for "Reset to default". When omitted, reset shows all columns. |
| Prop | Type | Description |
|---|---|---|
pagerequired | number | Current page number (1-based). |
onPageChangerequired | (page: number) => void | Called when the page changes. |
totalItems | number | Total number of items across all pages. Used to calculate total page count. |
totalPages | number | Total number of pages. Use when you know the page count but not item count. |
hasMore | boolean | Whether 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) => void | Called 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. |
| Prop | Type | Description |
|---|---|---|
getIsItemSelectedrequired | (item: T) => boolean | Returns whether the given item is currently selected. |
onSelectItemrequired | (event: {item: T; isSelected: boolean}) => void | Called when a row checkbox is toggled. isSelected is the new desired state. |
onSelectAllrequired | (event: {isAllSelected: boolean}) => void | Called when the select-all header checkbox is toggled. |
getIsAllSelectedrequired | () => boolean | Returns whether all selectable items are currently selected. |
getIsIndeterminate | () => boolean | Returns 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. |
| Prop | Type | Description |
|---|---|---|
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. |
| Prop | Type | Description |
|---|---|---|
sortrequired | XDSTableSortState<TSortKey> | Current sort state. Ordered array of {sortKey, direction} entries. First entry is the primary sort. |
onSortChangerequired | (sort: XDSTableSortState<TSortKey>) => void | Called 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. |
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><XDSToolbarlabel="Table actions"startContent={<XDSText type="label">Team</XDSText>}endContent={<XDSMultiSelectorlabel="Columns"isLabelHiddenoptions={selectorOptions}value={[...state.activeColumnKeys]}onChange={state.setActiveColumnKeys}/>}/></XDSLayoutHeader><XDSLayoutContent><XDSTabledata={users}columns={allColumns}idKey="id"hasHoverplugins={{columnSettings: plugin}}/></XDSLayoutContent></XDSLayout>);}
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 (<XDSTabledata={metrics}columns={columns}idKey="id"dividers="grid"density="compact"/>);}
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>);}
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 (<XDSTabledata={data}columns={columns}idKey="id"plugins={{filter: filterPlugin}}/>);}
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><XDSTabledata={paginateData(users, page, pageSize)}columns={columns}idKey="id"plugins={{pagination: plugin}}/></XDSSection>);}
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 (<XDSTabledata={data}columns={columns}idKey="id"plugins={{filter: filterPlugin}}/>);}
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 (<XDSTabledata={users}columns={columns}idKey="id"hasHoverplugins={{columnResize: resizePlugin}}/>);}
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 => (<XDSBadgelabel={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 />;}
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 (<XDSTabledata={users}columns={columns}idKey="id"plugins={{selection: selectionPlugin}}/>);}
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 (<XDSTabledata={sortedData}columns={columns}idKey="id"plugins={{sortable: sortablePlugin}}/>);}
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 />);}
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>);}