MC Data Table
A table component with sorting, row selection, search, and pagination.
Overview
The mc-data-table component is a composable data table for Micro Club UI, built on TanStack Table v9.
It includes:
McDataTablewhich takescolumnsanddataand handles sorting, row selection, search, and pagination- Card chrome (
McDataTableCard,McDataTableHeader,McDataTableToolbar) for the title, actions, tabs, and search above the table McDataTableEmptyfor empty, no-results, and error statesMcDataTableCellUserfor the common avatar + title + subtitle cell- Table primitives (
McTable,McTableRow,McTableCell, …) for static tables that need no logic
Preview
'use client';import * as React from 'react';import { PencilIcon, Trash2Icon } from 'lucide-react';import { McAvatar, McAvatarFallback } from '@/components/ui/mc-avatar';import { McBadge } from '@/components/ui/mc-badge';import { McButton } from '@/components/ui/mc-button';import { createMcDataTableColumnHelper, McDataTable, McDataTableCard, McDataTableCellUser, McDataTableHeader,} from '@/components/ui/mc-data-table';type Member = { id: string; name: string; username: string; status: 'Active' | 'Inactive'; role: string; email: string; teams: string[];};const people = [ ['Olivia Rhye', 'Product Designer'], ['Phoenix Baker', 'Product Manager'], ['Lana Steiner', 'Frontend Developer'], ['Demi Wilkinson', 'Backend Developer'], ['Candice Wu', 'Fullstack Developer'], ['Natali Craig', 'UX Designer'], ['Drew Cano', 'UX Copywriter'], ['Orlando Diggs', 'UI Designer'], ['Andi Lane', 'Product Manager'], ['Kate Morrison', 'QA Engineer'],];const members: Member[] = Array.from({ length: 50 }, (_, i) => { const [name, role] = people[i % people.length]; const first = name.split(' ')[0].toLowerCase(); return { id: String(i + 1), name, username: `@${first}`, status: i % 7 === 3 ? 'Inactive' : 'Active', role, email: `${first}@microclub.info`, teams: ['Design', 'Product', 'Marketing', 'Research', 'Ops'].slice(0, 2 + (i % 4)), };});const teamColors = [ 'bg-accent text-accent-foreground', 'bg-warning-foreground text-warning', 'bg-destructive-foreground text-destructive',];const helper = createMcDataTableColumnHelper<Member>();const columns = helper.columns([ helper.accessor('name', { header: 'Name', cell: ({ row }) => ( <McDataTableCellUser avatar={ <McAvatar size="md"> <McAvatarFallback> {row.original.name .split(' ') .map((part) => part[0]) .join('')} </McAvatarFallback> </McAvatar> } title={row.original.name} subtitle={row.original.username} /> ), }), helper.accessor('status', { header: 'Status', cell: ({ getValue }) => { const active = getValue() === 'Active'; return ( <McBadge className={active ? 'bg-success-foreground text-success' : 'bg-muted text-foreground'} > <span className="size-1.5 rounded-full bg-current" /> {getValue()} </McBadge> ); }, }), helper.accessor('role', { header: 'Role', meta: { headerTooltip: 'The role this member holds in the club.' }, }), helper.accessor('email', { header: 'Email address', }), helper.accessor('teams', { header: 'Teams', enableSorting: false, cell: ({ getValue }) => { const teams = getValue(); return ( <div className="flex items-center gap-1"> {teams.slice(0, 3).map((team, i) => ( <McBadge key={team} className={teamColors[i]}> {team} </McBadge> ))} {teams.length > 3 && ( <McBadge className="bg-muted text-foreground">+{teams.length - 3}</McBadge> )} </div> ); }, }), helper.display({ id: 'actions', cell: ({ row }) => ( <div className="flex items-center justify-end gap-1"> <McButton variant="tertiary" size="sm" icon="only" iconDefinition={<Trash2Icon className="size-4.5" />} className="bg-transparent text-muted-foreground hover:bg-muted hover:text-foreground" aria-label={`Delete ${row.original.name}`} /> <McButton variant="tertiary" size="sm" icon="only" iconDefinition={<PencilIcon className="size-4.5" />} className="bg-transparent text-muted-foreground hover:bg-muted hover:text-foreground" aria-label={`Edit ${row.original.name}`} /> </div> ), }),]);export default function McDataTableDemo() { return ( <McDataTableCard> <McDataTableHeader title="Team members" badge={`${members.length} users`} /> <McDataTable columns={columns} data={members} getRowId={(member) => member.id} enableRowSelection /> </McDataTableCard> );}Add to Your Project
Deploy the mc-data-table directly to your components/ui directory:
This also installs mc-button, mc-checkbox, and mc-tooltip.
Manual Setup
If you prefer manual control:
mc-button, mc-checkbox, and mc-tooltip'use client';import * as React from 'react';import { columnFilteringFeature, createColumnHelper, createFilteredRowModel, createPaginatedRowModel, createSortedRowModel, filterFn_includesString, flexRender, globalFilteringFeature, metaHelper, rowPaginationFeature, rowSelectionFeature, rowSortingFeature, sortFn_alphanumeric, sortFn_basic, sortFn_datetime, sortFn_text, tableFeatures, useTable, type ColumnDef, type Header, type OnChangeFn, type RowData, type RowSelectionState, type Table,} from '@tanstack/react-table';import { ArrowDownIcon, ArrowLeftIcon, ArrowRightIcon, ArrowUpIcon, HelpCircleIcon,} from 'lucide-react';import { cn } from '@/lib/utils';import { McButton } from '@/components/ui/mc-button';import { McCheckbox } from '@/components/ui/mc-checkbox';import { McTooltip, McTooltipContent, McTooltipTrigger } from '@/components/ui/mc-tooltip';// table primitivesfunction McTable({ className, ...props }: React.ComponentProps<'table'>) { return ( <div data-slot="table-container" className="relative w-full overflow-x-auto"> <table data-slot="table" className={cn('w-full caption-bottom border-collapse text-sm', className)} {...props} /> </div> );}function McTableHeader({ className, ...props }: React.ComponentProps<'thead'>) { return ( <thead data-slot="table-header" className={cn('bg-muted/50 [&_tr]:border-b [&_tr]:hover:bg-transparent', className)} {...props} /> );}function McTableBody({ className, ...props }: React.ComponentProps<'tbody'>) { return ( <tbody data-slot="table-body" className={cn('[&_tr:last-child]:border-0', className)} {...props} /> );}function McTableRow({ className, ...props }: React.ComponentProps<'tr'>) { return ( <tr data-slot="table-row" className={cn( 'border-b border-border transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted', className )} {...props} /> );}function McTableHead({ className, ...props }: React.ComponentProps<'th'>) { return ( <th data-slot="table-head" className={cn( 'h-11 px-6 py-3 text-left align-middle text-xs font-medium whitespace-nowrap text-muted-foreground', className )} {...props} /> );}function McTableCell({ className, ...props }: React.ComponentProps<'td'>) { return ( <td data-slot="table-cell" className={cn( 'h-18 px-6 py-4 align-middle whitespace-nowrap text-muted-foreground', className )} {...props} /> );}function McTableCaption({ className, ...props }: React.ComponentProps<'caption'>) { return ( <caption data-slot="table-caption" className={cn('mt-4 text-sm text-muted-foreground', className)} {...props} /> );}// card chromefunction McDataTableCard({ className, ...props }: React.ComponentProps<'div'>) { return ( <div data-slot="data-table-card" className={cn( 'flex w-full flex-col overflow-hidden rounded-xl border border-border bg-card text-card-foreground shadow-xs', className )} {...props} /> );}function McDataTableHeader({ className, title, badge, description, actions, ...props}: Omit<React.ComponentProps<'div'>, 'title'> & { title: React.ReactNode; badge?: React.ReactNode; description?: React.ReactNode; actions?: React.ReactNode;}) { return ( <div data-slot="data-table-header" className={cn( 'flex flex-col gap-4 border-b border-border px-4 py-5 sm:flex-row sm:items-start sm:justify-between sm:px-6', className )} {...props} > <div className="flex min-w-0 flex-col gap-1"> <div className="flex items-center gap-2"> <h3 className="text-lg font-semibold text-foreground">{title}</h3> {badge && ( <span className="rounded-full bg-accent px-2 py-0.5 text-xs font-medium text-accent-foreground"> {badge} </span> )} </div> {description && <p className="text-sm text-muted-foreground">{description}</p>} </div> {actions && <div className="flex shrink-0 items-center gap-3">{actions}</div>} </div> );}function McDataTableToolbar({ className, ...props }: React.ComponentProps<'div'>) { return ( <div data-slot="data-table-toolbar" className={cn( 'flex flex-col gap-3 border-b border-border px-4 py-3 md:flex-row md:items-center md:justify-between', className )} {...props} /> );}function McDataTableEmpty({ className, icon, title, description, actions, ...props}: Omit<React.ComponentProps<'div'>, 'title'> & { icon?: React.ReactNode; title: React.ReactNode; description?: React.ReactNode; actions?: React.ReactNode;}) { return ( <div data-slot="data-table-empty" className={cn( 'mx-auto flex max-w-sm flex-col items-center gap-4 px-4 py-10 text-center whitespace-normal', className )} {...props} > {icon && ( <div className="flex size-12 items-center justify-center rounded-full bg-muted text-primary [&_svg]:size-6"> {icon} </div> )} <div className="flex flex-col gap-1"> <p className="text-base font-semibold text-foreground">{title}</p> {description && <p className="text-sm text-muted-foreground">{description}</p>} </div> {actions && <div className="flex items-center gap-3">{actions}</div>} </div> );}function McDataTableCellUser({ className, avatar, title, subtitle, ...props}: Omit<React.ComponentProps<'div'>, 'title'> & { avatar?: React.ReactNode; title: React.ReactNode; subtitle?: React.ReactNode;}) { return ( <div data-slot="data-table-cell-user" className={cn('flex items-center gap-3', className)} {...props} > {avatar} <div className="flex min-w-0 flex-col"> <span className="truncate text-sm font-medium text-foreground">{title}</span> {subtitle && <span className="truncate text-sm text-muted-foreground">{subtitle}</span>} </div> </div> );}// table logicexport interface McDataTableColumnMeta { headerTooltip?: string; headerClassName?: string; cellClassName?: string;}const mcDataTableFeatures = tableFeatures({ rowSortingFeature, sortedRowModel: createSortedRowModel(), sortFns: { alphanumeric: sortFn_alphanumeric, basic: sortFn_basic, datetime: sortFn_datetime, text: sortFn_text, }, columnFilteringFeature, globalFilteringFeature, filteredRowModel: createFilteredRowModel(), filterFns: { includesString: filterFn_includesString }, rowSelectionFeature, rowPaginationFeature, paginatedRowModel: createPaginatedRowModel(), columnMeta: metaHelper<McDataTableColumnMeta>(),});type McDataTableFeatures = typeof mcDataTableFeatures;// eslint-disable-next-line @typescript-eslint/no-explicit-anytype McDataTableColumnDef<TData extends RowData> = ColumnDef<McDataTableFeatures, TData, any>;function createMcDataTableColumnHelper<TData extends RowData>() { return createColumnHelper<McDataTableFeatures, TData>();}type McDataTablePaginationVariant = 'numbered' | 'compact' | 'minimal';function McDataTableColumnHeader<TData extends RowData>({ header,}: { header: Header<McDataTableFeatures, TData, unknown>;}) { const { column } = header; const label = flexRender(column.columnDef.header, header.getContext()); const tooltip = column.columnDef.meta?.headerTooltip; const sorted = column.getIsSorted(); const help = tooltip && ( <McTooltip> <McTooltipTrigger aria-label="More information" className="inline-flex text-muted-foreground hover:text-foreground" > <HelpCircleIcon className="size-4" /> </McTooltipTrigger> <McTooltipContent description={tooltip} /> </McTooltip> ); if (!column.getCanSort()) { return ( <span className="inline-flex items-center gap-1"> {label} {help} </span> ); } const SortIcon = sorted === 'asc' ? ArrowUpIcon : ArrowDownIcon; return ( <span className="inline-flex items-center gap-1"> <button type="button" onClick={column.getToggleSortingHandler()} className={cn( 'group/sort inline-flex items-center gap-1 rounded-sm outline-none hover:text-foreground focus-visible:ring-4 focus-visible:ring-ring', sorted && 'text-foreground' )} > {label} <SortIcon className={cn( 'size-3.5 transition-opacity', sorted ? 'opacity-100' : 'opacity-0 group-hover/sort:opacity-50' )} /> </button> {help} </span> );}function getPageItems(page: number, pageCount: number): (number | 'ellipsis')[] { if (pageCount <= 7) { return Array.from({ length: pageCount }, (_, i) => i + 1); } if (page <= 3 || page >= pageCount - 2) { return [1, 2, 3, 'ellipsis', pageCount - 2, pageCount - 1, pageCount]; } return [1, 'ellipsis', page - 1, page, page + 1, 'ellipsis', pageCount];}function McDataTablePagination<TData extends RowData>({ table, variant = 'numbered', className,}: { table: Table<McDataTableFeatures, TData>; variant?: McDataTablePaginationVariant; className?: string;}) { const page = table.atoms.pagination.get().pageIndex + 1; const pageCount = Math.max(table.getPageCount(), 1); const canPrevious = table.getCanPreviousPage(); const canNext = table.getCanNextPage(); const pageLabel = ( <span className="text-sm font-medium text-foreground"> Page {page} of {pageCount} </span> ); if (variant === 'minimal') { return ( <div data-slot="data-table-pagination" className={cn('flex items-center justify-between gap-3 px-4 py-3', className)} > <McButton variant="secondary" size="sm" icon="only" iconDefinition={<ArrowLeftIcon />} aria-label="Go to previous page" disabled={!canPrevious} onClick={() => table.previousPage()} /> {pageLabel} <McButton variant="secondary" size="sm" icon="only" iconDefinition={<ArrowRightIcon />} aria-label="Go to next page" disabled={!canNext} onClick={() => table.nextPage()} /> </div> ); } if (variant === 'compact') { return ( <div data-slot="data-table-pagination" className={cn('flex items-center justify-between gap-3 px-6 py-3', className)} > {pageLabel} <div className="flex items-center gap-3"> <McButton variant="secondary" size="sm" disabled={!canPrevious} onClick={() => table.previousPage()} > Previous </McButton> <McButton variant="secondary" size="sm" disabled={!canNext} onClick={() => table.nextPage()} > Next </McButton> </div> </div> ); } return ( <nav aria-label="pagination" data-slot="data-table-pagination" className={cn('flex items-center justify-between gap-3 px-6 py-3', className)} > <McButton variant="secondary" size="sm" icon="leading" iconDefinition={<ArrowLeftIcon />} disabled={!canPrevious} onClick={() => table.previousPage()} > Previous </McButton> <span className="text-sm font-medium text-foreground md:hidden"> Page {page} of {pageCount} </span> <ul className="hidden items-center gap-0.5 md:flex"> {getPageItems(page, pageCount).map((item, i) => ( <li key={item === 'ellipsis' ? `ellipsis-${i}` : item}> {item === 'ellipsis' ? ( <span aria-hidden className="flex size-10 items-center justify-center text-sm text-muted-foreground" > … </span> ) : ( <button type="button" aria-current={item === page ? 'page' : undefined} onClick={() => table.setPageIndex(item - 1)} className={cn( 'flex size-10 items-center justify-center rounded-lg text-sm font-medium text-muted-foreground outline-none transition-colors hover:bg-muted hover:text-foreground focus-visible:ring-4 focus-visible:ring-ring', item === page && 'bg-muted text-foreground' )} > {item} </button> )} </li> ))} </ul> <McButton variant="secondary" size="sm" icon="trailing" iconDefinition={<ArrowRightIcon />} disabled={!canNext} onClick={() => table.nextPage()} > Next </McButton> </nav> );}export interface McDataTableProps<TData extends RowData> { columns: McDataTableColumnDef<TData>[]; data: TData[]; getRowId?: (row: TData, index: number) => string; enableRowSelection?: boolean; rowSelection?: RowSelectionState; onRowSelectionChange?: OnChangeFn<RowSelectionState>; globalFilter?: string; pageSize?: number; pagination?: McDataTablePaginationVariant | false; empty?: React.ReactNode; className?: string;}function McDataTable<TData extends RowData>({ columns, data, getRowId, enableRowSelection = false, rowSelection, onRowSelectionChange, globalFilter, pageSize = 10, pagination = 'numbered', empty, className,}: McDataTableProps<TData>) { const table = useTable({ features: mcDataTableFeatures, columns, data, getRowId, enableRowSelection, enableRowRangeSelection: false, globalFilterFn: 'includesString', manualPagination: pagination === false, initialState: { pagination: { pageIndex: 0, pageSize } }, state: { ...(globalFilter !== undefined && { globalFilter }), ...(rowSelection !== undefined && { rowSelection }), }, ...(onRowSelectionChange && { onRowSelectionChange }), }); const rows = table.getRowModel().rows; const columnCount = table.getAllLeafColumns().length; return ( <div data-slot="data-table" className={cn('flex w-full flex-col', className)}> <McTable> <McTableHeader> {table.getHeaderGroups().map((headerGroup) => ( <McTableRow key={headerGroup.id}> {headerGroup.headers.map((header, index) => { const sorted = header.column.getIsSorted(); return ( <McTableHead key={header.id} colSpan={header.colSpan} aria-sort={ sorted === 'asc' ? 'ascending' : sorted === 'desc' ? 'descending' : undefined } className={header.column.columnDef.meta?.headerClassName} > <div className="flex items-center gap-3"> {enableRowSelection && index === 0 && ( <McCheckbox aria-label="Select all rows on this page" checked={table.getIsAllPageRowsSelected()} indeterminate={ !table.getIsAllPageRowsSelected() && table.getIsSomePageRowsSelected() } onCheckedChange={(checked) => table.toggleAllPageRowsSelected(checked)} /> )} {!header.isPlaceholder && <McDataTableColumnHeader header={header} />} </div> </McTableHead> ); })} </McTableRow> ))} </McTableHeader> <McTableBody> {rows.length > 0 ? ( rows.map((row) => ( <McTableRow key={row.id} data-state={row.getIsSelected() ? 'selected' : undefined}> {row.getAllCells().map((cell, index) => ( <McTableCell key={cell.id} className={cell.column.columnDef.meta?.cellClassName}> {enableRowSelection && index === 0 ? ( <div className="flex items-center gap-3"> <McCheckbox aria-label="Select row" checked={row.getIsSelected()} disabled={!row.getCanSelect()} onCheckedChange={(checked) => row.toggleSelected(checked)} /> {flexRender(cell.column.columnDef.cell, cell.getContext())} </div> ) : ( flexRender(cell.column.columnDef.cell, cell.getContext()) )} </McTableCell> ))} </McTableRow> )) ) : ( <McTableRow className="hover:bg-transparent"> <McTableCell colSpan={columnCount} className="h-auto p-0"> {empty ?? <McDataTableEmpty title="No results" />} </McTableCell> </McTableRow> )} </McTableBody> </McTable> {pagination && ( <McDataTablePagination table={table} variant={pagination} className="border-t border-border" /> )} </div> );}export { McTable, McTableHeader, McTableBody, McTableRow, McTableHead, McTableCell, McTableCaption, McDataTable, McDataTableCard, McDataTableHeader, McDataTableToolbar, McDataTableEmpty, McDataTableCellUser, McDataTableColumnHeader, McDataTablePagination, mcDataTableFeatures, createMcDataTableColumnHelper, type McDataTableFeatures, type McDataTableColumnDef, type McDataTablePaginationVariant,};Ensure your @/lib/utils (or equivalent classname helper) is correctly configured.
Usage
Define columns with createMcDataTableColumnHelper, then pass them to McDataTable with your data.
import {
createMcDataTableColumnHelper,
McDataTable,
McDataTableCard,
McDataTableHeader,
} from '@/components/ui/mc-data-table';
type Member = { id: string; name: string; role: string };
const helper = createMcDataTableColumnHelper<Member>();
const columns = helper.columns([
helper.accessor('name', { header: 'Name' }),
helper.accessor('role', { header: 'Role' }),
]);
export default function Example({ members }: { members: Member[] }) {
return (
<McDataTableCard>
<McDataTableHeader title="Team members" badge={`${members.length} users`} />
<McDataTable columns={columns} data={members} getRowId={(member) => member.id} />
</McDataTableCard>
);
}Keep columns outside the component (or memoize it) so the table does not rebuild on every render.
Examples
Custom cells
Use cell to render anything in a column. McDataTableCellUser covers the avatar + name + subtitle layout.
helper.accessor('name', {
header: 'Name',
cell: ({ row }) => (
<McDataTableCellUser
avatar={
<McAvatar size="md">
<McAvatarImage src={row.original.avatar} />
<McAvatarFallback>OR</McAvatarFallback>
</McAvatar>
}
title={row.original.name}
subtitle={row.original.email}
/>
),
}),Sorting
Every accessor column is sortable by default. Click a header to cycle ascending, descending, and unsorted. Turn it off per column with enableSorting: false.
helper.accessor('teams', { header: 'Teams', enableSorting: false }),Header tooltip
Add a help icon with a tooltip next to a header through the column meta.
helper.accessor('role', {
header: 'Role',
meta: { headerTooltip: 'The role this member holds in the club.' },
}),meta also accepts headerClassName and cellClassName to style a single column.
Row actions
Use a display column for per-row buttons.
helper.display({
id: 'actions',
cell: ({ row }) => (
<McButton
variant="tertiary"
size="sm"
icon="only"
iconDefinition={<PencilIcon />}
aria-label={`Edit ${row.original.name}`}
/>
),
}),Row selection
enableRowSelection adds checkboxes to the first column. The header checkbox selects every row on the current page and shows an indeterminate state when only some are selected. Pass rowSelection and onRowSelectionChange to control the selection yourself.
const [rowSelection, setRowSelection] = React.useState<RowSelectionState>({});
<McDataTable
columns={columns}
data={members}
getRowId={(member) => member.id}
enableRowSelection
rowSelection={rowSelection}
onRowSelectionChange={setRowSelection}
/>;Always pass getRowId with selection so selected rows stay stable when data is sorted, filtered, or reloaded.
Toolbar and search
Put tabs, search, and filter buttons in McDataTableToolbar, and pass the search text as globalFilter.
const [search, setSearch] = React.useState('');
<McDataTableCard>
<McDataTableHeader
title="Vendor movements"
badge="240 vendors"
description="Keep track of vendors and their security ratings."
actions={<McButton size="sm">Add vendor</McButton>}
/>
<McDataTableToolbar>
<McTabs defaultValue="all">...</McTabs>
<McInput
placeholder="Search"
value={search}
onChange={(event) => setSearch(event.target.value)}
/>
</McDataTableToolbar>
<McDataTable columns={columns} data={vendors} globalFilter={search} pagination="compact" />
</McDataTableCard>;Pagination styles
| Value | Footer |
|---|---|
"numbered" | Previous, page numbers, Next |
"compact" | "Page 1 of 10" with Previous and Next buttons |
"minimal" | Arrow buttons around "Page 1 of 10", for mobile |
false | No footer; every row is shown |
Empty and error states
empty is rendered in place of the rows when there is no data or the search matches nothing. For a failed load, render McDataTableEmpty instead of the table.
<McDataTable
columns={columns}
data={vendors}
globalFilter={search}
empty={
<McDataTableEmpty
icon={<SearchIcon />}
title="No vendors found"
description={`Your search "${search}" did not match any vendors.`}
actions={<McButton onClick={() => setSearch('')}>Clear search</McButton>}
/>
}
/>API Reference
McDataTable
| Prop | Type | Default | Description |
|---|---|---|---|
columns | McDataTableColumnDef<TData>[] | - | Column definitions |
data | TData[] | - | Rows to display |
getRowId | (row: TData, index: number) => string | row index | Stable id for each row |
enableRowSelection | boolean | false | Show selection checkboxes in the first column |
rowSelection | RowSelectionState | undefined | Controlled selection ({ [rowId]: true }) |
onRowSelectionChange | OnChangeFn<RowSelectionState> | undefined | Called when the selection changes |
globalFilter | string | undefined | Search text matched against every column |
pageSize | number | 10 | Rows per page |
pagination | "numbered" | "compact" | "minimal" | false | "numbered" | Footer style, or false to show every row |
empty | React.ReactNode | "No results" | Rendered when there are no rows |
className | string | undefined | Extra classes for the wrapper |
Column meta
| Field | Type | Description |
|---|---|---|
headerTooltip | string | Shows a help icon with this tooltip text |
headerClassName | string | Extra classes for this column's header cell |
cellClassName | string | Extra classes for this column's body cells |
McDataTableHeader
| Prop | Type | Default | Description |
|---|---|---|---|
title | React.ReactNode | - | Table title |
badge | React.ReactNode | - | Count badge next to the title |
description | React.ReactNode | - | Supporting text under the title |
actions | React.ReactNode | - | Buttons aligned to the right |
Also supports standard HTMLDivElement props.
McDataTableEmpty
| Prop | Type | Default | Description |
|---|---|---|---|
icon | React.ReactNode | - | Icon shown in a tinted circle |
title | React.ReactNode | - | Heading |
description | React.ReactNode | - | Supporting text |
actions | React.ReactNode | - | Buttons under the text |
Also supports standard HTMLDivElement props.
McDataTableCellUser
| Prop | Type | Default | Description |
|---|---|---|---|
avatar | React.ReactNode | - | Avatar, logo, or file icon |
title | React.ReactNode | - | Primary text |
subtitle | React.ReactNode | - | Secondary text |
Other exports
McDataTableCardandMcDataTableToolbarare styleddivwrappers.McDataTablePaginationandMcDataTableColumnHeaderare the piecesMcDataTablerenders internally, exported for custom layouts.McTable,McTableHeader,McTableBody,McTableRow,McTableHead,McTableCell, andMcTableCaptionare styled table elements with no logic.mcDataTableFeaturesis the TanStack feature set the table registers (sorting, filtering, selection, pagination). Use it withuseTableif you need full control.