From 3356158367c3c182432d4ffb3100194fc8c7fb01 Mon Sep 17 00:00:00 2001 From: John Betancur <1385932+jbetancur@users.noreply.github.com> Date: Sun, 12 Jul 2026 07:55:59 -0400 Subject: [PATCH 1/3] allow opt out of skelton loading behaviour (#1349) --- CHANGELOG.md | 8 +++++ .../src/components/demos/FooterBasicDemo.tsx | 1 - .../components/demos/InlineEditingDemo.tsx | 31 ++++++++++------ .../docs/src/components/demos/LoadingDemo.tsx | 35 ++++++++++++++++++- apps/docs/src/pages/docs/api.md | 3 +- apps/docs/src/pages/docs/loading.astro | 11 ++++-- src/DataTable.css | 8 +++++ src/__tests__/DataTable.test.tsx | 25 +++++++++++++ src/components/DataTable.tsx | 2 ++ src/components/DataTableBody.tsx | 11 ++++-- src/defaultProps.tsx | 1 + src/types.ts | 1 + 12 files changed, 119 insertions(+), 18 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8ff9a8bf..b01162fe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,14 @@ A summary of notable changes per release. For the full commit history see the [repository on GitHub](https://github.com/jbetancur/react-data-table-component/commits/master). +## 8.6.1 + +### Bug fixes + +- A custom `progressComponent` could not be shown on the initial load — the skeleton rows always won. New `progressSkeleton` prop (default `true`) lets you set it to `false` to show your `progressComponent` on initial load instead. → [Loading state](/docs/loading) + +--- + ## 8.6.0 ### New features diff --git a/apps/docs/src/components/demos/FooterBasicDemo.tsx b/apps/docs/src/components/demos/FooterBasicDemo.tsx index 20e6b31e..7ae92321 100644 --- a/apps/docs/src/components/demos/FooterBasicDemo.tsx +++ b/apps/docs/src/components/demos/FooterBasicDemo.tsx @@ -89,7 +89,6 @@ export default function FooterBasicDemo() { columns={columns} data={data} highlightOnHover - dense /> ); diff --git a/apps/docs/src/components/demos/InlineEditingDemo.tsx b/apps/docs/src/components/demos/InlineEditingDemo.tsx index 666442ba..2ad8b08d 100644 --- a/apps/docs/src/components/demos/InlineEditingDemo.tsx +++ b/apps/docs/src/components/demos/InlineEditingDemo.tsx @@ -11,14 +11,15 @@ interface Employee { department: Department; status: Status; salary: number; + remote: boolean; } const initialData: Employee[] = [ - { id: 1, name: 'Aria Chen', department: 'Engineering', status: 'Active', salary: 155000 }, - { id: 2, name: 'Marcus Webb', department: 'Product', status: 'Active', salary: 132000 }, - { id: 3, name: 'Priya Kapoor', department: 'Design', status: 'On Leave', salary: 118000 }, - { id: 4, name: 'Jordan Ellis', department: 'Analytics', status: 'Active', salary: 143000 }, - { id: 5, name: 'Sam Rivera', department: 'Engineering', status: 'Terminated', salary: 128000 }, + { id: 1, name: 'Aria Chen', department: 'Engineering', status: 'Active', salary: 155000, remote: true }, + { id: 2, name: 'Marcus Webb', department: 'Product', status: 'Active', salary: 132000, remote: false }, + { id: 3, name: 'Priya Kapoor', department: 'Design', status: 'On Leave', salary: 118000, remote: true }, + { id: 4, name: 'Jordan Ellis', department: 'Analytics', status: 'Active', salary: 143000, remote: false }, + { id: 5, name: 'Sam Rivera', department: 'Engineering', status: 'Terminated', salary: 128000, remote: false }, ]; const statusColors: Record = { @@ -33,9 +34,8 @@ export default function InlineEditingDemo() { const handleCellEdit = (row: Employee, value: string, column: TableColumn) => { const field = column.id as keyof Employee; - setData(prev => - prev.map(r => (r.id === row.id ? { ...r, [field]: field === 'salary' ? Number(value) || r.salary : value } : r)), - ); + const parsed = field === 'salary' ? Number(value) || row.salary : field === 'remote' ? value === 'true' : value; + setData(prev => prev.map(r => (r.id === row.id ? { ...r, [field]: parsed } : r))); setLastEdit(`Updated ${row.name} → ${String(column.name)}: "${value}"`); }; @@ -101,15 +101,24 @@ export default function InlineEditingDemo() { editable: true, onCellEdit: handleCellEdit, }, + { + id: 'remote', + name: 'Remote', + selector: r => r.remote, + format: r => (r.remote ? 'Yes' : 'No'), + center: true, + editor: { type: 'checkbox' }, + onCellEdit: handleCellEdit, + }, ]; return (

Click any cell to edit. Name and Salary are text inputs;{' '} - Department and Status are dropdowns. Enter commits, Esc{' '} - cancels. Keyboard navigation is enabled too: click or Tab into the table, move between cells and headers with - the arrow keys, and press Enter to edit or sort. + Department and Status are dropdowns; Remote is a checkbox.{' '} + Enter commits, Esc cancels. Keyboard navigation is enabled too: click or Tab into the + table, move between cells and headers with the arrow keys, and press Enter to edit or sort.

{lastEdit &&
{lastEdit}
} diff --git a/apps/docs/src/components/demos/LoadingDemo.tsx b/apps/docs/src/components/demos/LoadingDemo.tsx index b9ae77f0..22676b15 100644 --- a/apps/docs/src/components/demos/LoadingDemo.tsx +++ b/apps/docs/src/components/demos/LoadingDemo.tsx @@ -31,10 +31,19 @@ function delay(ms: number) { type Mode = 'initial' | 'refetch' | 'idle'; +const customLoader = ( +
+
+ Fetching employees… +
+); + export default function LoadingDemo() { const [mode, setMode] = useState('idle'); const [data, setData] = useState(ROWS); const [pending, setPending] = useState(false); + const [useCustom, setUseCustom] = useState(false); + const [skeleton, setSkeleton] = useState(true); async function simulateInitial() { setData([]); @@ -67,9 +76,31 @@ export default function LoadingDemo() { + + {mode !== 'idle' && ( - {mode === 'initial' ? 'Loading with no existing data — skeleton rows shown' : 'Re-fetching — existing rows dimmed, spinner overlaid'} + {mode === 'initial' + ? skeleton + ? 'Loading with no existing data — skeleton rows shown' + : 'Loading with no existing data — progress component shown' + : 'Re-fetching — existing rows dimmed, indicator overlaid'} )}
@@ -77,6 +108,8 @@ export default function LoadingDemo() { columns={columns} data={data} progressPending={pending} + progressComponent={useCustom ? customLoader : undefined} + progressSkeleton={skeleton} highlightOnHover striped /> diff --git a/apps/docs/src/pages/docs/api.md b/apps/docs/src/pages/docs/api.md index 38da2f3c..18149cc0 100644 --- a/apps/docs/src/pages/docs/api.md +++ b/apps/docs/src/pages/docs/api.md @@ -29,7 +29,8 @@ Complete reference for every prop, type, and export in `react-data-table-compone | `columns` | `TableColumn[]` | - | **Required.** Column definitions. | | `keyField` | `string` | `"id"` | Property on each row used as a stable React key. | | `progressPending` | `boolean` | `false` | Show a loading state. On initial load (no data yet) renders shimmer skeleton rows. On re-fetch (data already loaded) dims the existing rows and overlays a centered spinner. The column header always stays visible. | -| `progressComponent` | `ReactNode` | built-in spinner | Custom loading indicator. | +| `progressComponent` | `ReactNode` | built-in spinner | Custom loading indicator shown in the re-fetch overlay, and on initial load when `progressSkeleton` is `false`. | +| `progressSkeleton` | `boolean` | `true` | Show shimmer skeleton rows on the initial load (no data yet). Set to `false` to show `progressComponent` on initial load instead. | | `noDataComponent` | `ReactNode` | built-in message | Rendered when `data` is empty. | ### Layout & appearance diff --git a/apps/docs/src/pages/docs/loading.astro b/apps/docs/src/pages/docs/loading.astro index 1c752b97..c457128f 100644 --- a/apps/docs/src/pages/docs/loading.astro +++ b/apps/docs/src/pages/docs/loading.astro @@ -18,6 +18,12 @@ import LoadingDemo from '../../components/demos/LoadingDemo';
  • Re-fetch (rows already shown): existing rows stay in place, dim to 40% opacity, and a spinner overlays the centre. The header and pagination never disappear.
  • +

    + Set progressSkeleton to false to opt out of the skeleton on initial load: + your progressComponent is shown instead of the shimmer rows. It defaults to true, + so a custom progressComponent still uses skeleton rows on first load unless you turn this off. +

    + Custom spinner or message

    - The default progressComponent is a CSS spinner circle. Pass any React node to replace it. - It will be centred in the overlay during re-fetches, or shown instead of skeletons on initial load. + The default progressComponent is a CSS spinner circle. Pass any React node to replace it: + your node is centred in the overlay during re-fetches. On initial load the skeleton rows show instead; + set progressSkeleton={false} to show your progressComponent there too.

    { expect(container.querySelector('.rdt_skeletonPulse')).not.toBeNull(); }); + test('should skip skeleton rows on initial load when progressSkeleton is false', () => { + const mock = dataMock(); + const { container, getByText } = render( + Loading…
    } + />, + ); + + expect(container.querySelector('.rdt_skeletonPulse')).toBeNull(); + expect(getByText('Loading…')).not.toBeNull(); + }); + + test('should still render skeleton rows when a custom progressComponent is passed but progressSkeleton is left default', () => { + const mock = dataMock(); + const { container } = render( + Loading…} />, + ); + + expect(container.querySelector('.rdt_skeletonPulse')).not.toBeNull(); + }); + describe('when persistTableHead', () => { test('should render the progress component and keep TableHead when progressPending toggles to true', () => { const mock = dataMock(); diff --git a/src/components/DataTable.tsx b/src/components/DataTable.tsx index 29aa892a..73743b23 100644 --- a/src/components/DataTable.tsx +++ b/src/components/DataTable.tsx @@ -78,6 +78,7 @@ function DataTableInner(props: TableProps, ref: React.ForwardedRef(props: TableProps, ref: React.ForwardedRef { columnCount: number; noDataComponent: React.ReactNode; progressComponent: React.ReactNode; + progressSkeleton: boolean; expandableRowExpanded?: RowState; expandableRowDisabled?: RowState; bodyRef: React.RefObject; @@ -57,6 +58,7 @@ function DataTableBody({ columnCount, noDataComponent, progressComponent, + progressSkeleton, expandableRowExpanded, expandableRowDisabled, bodyRef, @@ -163,8 +165,8 @@ function DataTableBody({ {/* Empty + not loading */} {!hasData && !isBusy && {noDataComponent}} - {/* Initial load: no existing data — show skeleton rows */} - {isBusy && !hasData && ( + {/* Initial load with skeleton enabled: no existing data — show skeleton rows */} + {isBusy && !hasData && progressSkeleton && ( {Array.from({ length: SKELETON_ROW_COUNT }).map((_, i) => ( @@ -172,6 +174,11 @@ function DataTableBody({ )} + {/* Initial load with skeleton disabled: show the progress component instead */} + {isBusy && !hasData && !progressSkeleton && ( +
    {progressComponent}
    + )} + {/* Has data — always render rows, overlay when re-fetching */} {hasData && (
    diff --git a/src/defaultProps.tsx b/src/defaultProps.tsx index 29de9d5e..d666aa6c 100644 --- a/src/defaultProps.tsx +++ b/src/defaultProps.tsx @@ -66,6 +66,7 @@ export const defaultProps = { }} /> ), + progressSkeleton: true, persistTableHead: false, sortIcon: null, sortFunction: null, diff --git a/src/types.ts b/src/types.ts index a7432fad..3f33e6d2 100644 --- a/src/types.ts +++ b/src/types.ts @@ -341,6 +341,7 @@ type BaseTableProps = { pointerOnHover?: boolean; progressComponent?: React.ReactNode; progressPending?: boolean; + progressSkeleton?: boolean; responsive?: boolean; striped?: boolean; style?: CSSObject; From d3463ca6a5a19ec2eb173a254d23b6bb7922c123 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 12 Jul 2026 12:02:25 +0000 Subject: [PATCH 2/3] chore: release v8.6.1 [skip ci] --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 012476c6..8c430c43 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-data-table-component", - "version": "8.6.0", + "version": "8.6.1", "description": "A fast, feature-rich React data table. Working table in 10 lines.", "funding": [ { From 0dc7bec5008b49580d9e3f6dfc59993de1ae2531 Mon Sep 17 00:00:00 2001 From: John Betancur <1385932+jbetancur@users.noreply.github.com> Date: Sun, 12 Jul 2026 09:34:15 -0400 Subject: [PATCH 3/3] docs: update doc api references (#1350) --- .github/FUNDING.yml | 1 - README.md | 18 +- apps/docs/public/llms.txt | 3 +- apps/docs/src/components/PropsTable.astro | 85 ++ apps/docs/src/data/backers.ts | 9 + apps/docs/src/data/props.ts | 1000 +++++++++++++++++ apps/docs/src/pages/docs/animations.astro | 5 + apps/docs/src/pages/docs/api.astro | 648 +++++++++++ apps/docs/src/pages/docs/api.md | 698 ------------ apps/docs/src/pages/docs/column-groups.astro | 11 + apps/docs/src/pages/docs/column-pinning.astro | 8 +- apps/docs/src/pages/docs/column-reorder.astro | 10 + .../src/pages/docs/column-separators.astro | 5 + .../src/pages/docs/conditional-styles.astro | 11 + apps/docs/src/pages/docs/context-menu.astro | 10 +- apps/docs/src/pages/docs/custom-styles.astro | 10 + apps/docs/src/pages/docs/expandable.astro | 35 +- apps/docs/src/pages/docs/filtering.astro | 10 + apps/docs/src/pages/docs/fixed-header.astro | 11 +- apps/docs/src/pages/docs/footer.astro | 10 + .../docs/src/pages/docs/getting-started.astro | 17 +- apps/docs/src/pages/docs/inline-editing.astro | 36 +- .../src/pages/docs/keyboard-navigation.astro | 8 +- apps/docs/src/pages/docs/loading.astro | 5 + apps/docs/src/pages/docs/localization.astro | 5 + apps/docs/src/pages/docs/mobile.astro | 10 + apps/docs/src/pages/docs/pagination.astro | 30 +- apps/docs/src/pages/docs/resizable.astro | 6 +- .../src/pages/docs/row-interactions.astro | 5 + apps/docs/src/pages/docs/rtl.astro | 8 +- apps/docs/src/pages/docs/selection.astro | 5 + apps/docs/src/pages/docs/sorting.astro | 19 +- apps/docs/src/pages/docs/themes.astro | 15 +- apps/docs/src/pages/index.astro | 22 +- apps/docs/src/pages/support.astro | 53 +- package-lock.json | 4 - package.json | 4 - 37 files changed, 1956 insertions(+), 894 deletions(-) create mode 100644 apps/docs/src/components/PropsTable.astro create mode 100644 apps/docs/src/data/backers.ts create mode 100644 apps/docs/src/data/props.ts create mode 100644 apps/docs/src/pages/docs/api.astro delete mode 100644 apps/docs/src/pages/docs/api.md diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index e2eda8a3..fca15021 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -4,7 +4,6 @@ github: [jbetancur] # patreon: # Replace with a single Patreon username open_collective: react-data-table-component # ko_fi: # Replace with a single Ko-fi username -tidelift: npm/react-data-table-component # community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry # liberapay: # Replace with a single Liberapay username # issuehunt: # Replace with a single IssueHunt username diff --git a/README.md b/README.md index 85487e17..f7b966ed 100644 --- a/README.md +++ b/README.md @@ -42,30 +42,28 @@ The documentation contains information about installation, usage and contributio # Supporting React Data Table Component -React Data Table Component has been actively maintained since 2018 and is downloaded ~215k times a week. If your team ships products with it, your support keeps it maintained, bug-free, and moving forward. +React Data Table Component has been actively maintained since 2018 and is downloaded ![weekly downloads](https://img.shields.io/npm/dw/react-data-table-component.svg?label=%20&color=blue&style=flat-square) times a week. If your team ships products with it, your support keeps it maintained, bug-free, and moving forward. ## Sponsor the project -Sponsoring puts your company logo in front of ~215k developers a week: in the README, the docs site, and every release. It's the right move if your team depends on this library and you want it to keep improving. +Sponsoring puts your company logo in front of the ![weekly downloads](https://img.shields.io/npm/dw/react-data-table-component.svg?label=%20&color=blue&style=flat-square) developers who use it every week: in the README, the docs site, and every release. It's the right move if your team depends on this library and you want it to keep improving. | Tier | Price/month | Perk | | --- | --- | --- | | ☕ Supporter | $5 | Your name in the README supporters list | | 🎗 Backer | $20 | Name + link in README + listed in the Backers section on reactdatatable.com | -| 🥉 Bronze | $100 | Priority issue triage + small logo in README + docs site footer | -| 🥈 Silver | $200 | Priority issue triage + medium logo in README + docs site sidebar | -| 🥇 Gold | $500 | Priority issue triage + direct maintainer line + large logo in README + hero spot on reactdatatable.com. Limited to 3. | +| 🥉 Bronze | $100 | Visibility: small logo in README + docs site footer, and sponsor credit in every release's notes | +| 🥈 Silver | $200 | Everything in Bronze + medium logo in the docs sidebar + priority issue queue: sponsor issues go to the front of the backlog, first response within 2 business days | +| 🥇 Gold | $500 | Everything in Silver + hero logo on reactdatatable.com + top README placement + written support commitments: 12-month patch window for the previous major, security advisories before public disclosure, one compliance questionnaire per year, direct email channel (async, business hours). Limited to 3. | + +These are honest, written commitments from a solo maintainer: async and business hours. No fake 24/7 SLA. [![Sponsor on GitHub Sponsors](https://img.shields.io/badge/Sponsor-GitHub%20Sponsors-ea4aaa?logo=github)](https://github.com/sponsors/jbetancur) [![Sponsor on OpenCollective](https://img.shields.io/badge/Sponsor-OpenCollective-blue?logo=opencollective)](https://opencollective.com/react-data-table-component) ## Enterprise support -Available as part of the Tidelift Subscription. - -The maintainer of react-data-table-component and thousands of other packages are working with Tidelift to deliver commercial support and maintenance for the open source dependencies you use to build your applications. Save time, reduce risk, and improve code health, while paying the maintainers of the exact dependencies you use. [Learn more.](https://tidelift.com/subscription/pkg/npm-react-data-table-component?utm_source=npm-react-data-table-component&utm_medium=referral&utm_campaign=readme) - - +Need commercial support, an SLA, or help with a major version upgrade? I offer direct consulting and priority support arrangements for teams that depend on react-data-table-component in production. Reach out via the [support page](https://reactdatatable.com/support). ## Need help? diff --git a/apps/docs/public/llms.txt b/apps/docs/public/llms.txt index 61a38b93..b332f946 100644 --- a/apps/docs/public/llms.txt +++ b/apps/docs/public/llms.txt @@ -34,6 +34,7 @@ const columns = [ - [Column Reordering](https://reactdatatable.com/docs/column-reorder): drag to reorder, mouse or touch (press-and-hold on touch) - [Column Resizing](https://reactdatatable.com/docs/resizable): drag column edges by mouse or touch, persist widths - [Column Pinning](https://reactdatatable.com/docs/column-pinning): pin columns left or right +- [Column Separators](https://reactdatatable.com/docs/column-separators): vertical divider lines between header and body columns - [Column Filtering](https://reactdatatable.com/docs/filtering): per-column text, number, and select filters - [Sorting](https://reactdatatable.com/docs/sorting): client-side and server-side sorting @@ -87,6 +88,6 @@ const columns = [ ## Optional - [Changelog](https://reactdatatable.com/docs/changelog): release history -- [Support & Sponsorship](https://reactdatatable.com/support): sponsor tiers, enterprise support via Tidelift +- [Support & Sponsorship](https://reactdatatable.com/support): sponsor tiers, direct consulting and enterprise support - [GitHub repository](https://github.com/jbetancur/react-data-table-component) - [npm package](https://www.npmjs.com/package/react-data-table-component) diff --git a/apps/docs/src/components/PropsTable.astro b/apps/docs/src/components/PropsTable.astro new file mode 100644 index 00000000..93c6bd54 --- /dev/null +++ b/apps/docs/src/components/PropsTable.astro @@ -0,0 +1,85 @@ +--- +import DocsTable from './DocsTable.astro'; +import { dataTableProps, propGroupOrder, type PropDef, type PropFeature, type PropGroup } from '../data/props'; + +type Column = 'prop' | 'type' | 'default' | 'description'; + +interface Props { + // Filter by feature tag (feature pages) … + feature?: PropFeature; + // … or render whole api.md-style groups (the API reference). + group?: PropGroup; + groups?: PropGroup[]; + // Restrict/order the rows explicitly by prop name (overrides feature/group). + only?: string[]; + // Which columns to render. Default omits the Default column on feature pages. + columns?: Column[]; + // Prefer the shorter `brief` copy where available. Defaults to true unless a + // group is given (the API reference wants the canonical description). + brief?: boolean; + // Header label for the first column. + propHeader?: string; +} + +const { + feature, + group, + groups, + only, + columns, + brief = !group && !groups, + propHeader = 'Prop', +} = Astro.props as Props; + +const byName = new Map(dataTableProps.map(p => [p.name, p] as const)); + +let rows: PropDef[]; +if (only) { + rows = only.map(name => { + const p = byName.get(name); + if (!p) throw new Error(`PropsTable: unknown prop "${name}" in \`only\``); + return p; + }); +} else if (feature) { + rows = dataTableProps.filter(p => p.features.includes(feature)); +} else { + const wanted = groups ?? (group ? [group] : propGroupOrder); + rows = dataTableProps.filter(p => wanted.includes(p.group)); +} + +if (rows.length === 0) { + throw new Error(`PropsTable: no props matched (feature=${feature}, group=${group}).`); +} + +// Row events have no defaults in the source; drop the Default column when none +// of the selected rows define one, unless the caller pins the columns. +const anyDefault = rows.some(p => p.default != null && p.default !== ''); +const cols: Column[] = columns ?? (anyDefault ? ['prop', 'type', 'default', 'description'] : ['prop', 'type', 'description']); + +const headerFor: Record = { + prop: propHeader, + type: 'Type', + default: 'Default', + description: 'Description', +}; +const headers = cols.map(col => headerFor[col]); + +const cellFor = (p: PropDef, col: Column): string => { + switch (col) { + case 'prop': { + const label = `${p.name}`; + return p.deprecated ? `${label}` : label; + } + case 'type': + return p.type; + case 'default': + return p.default ?? '-'; + case 'description': + return (brief && p.brief) || p.description; + } +}; + +const tableRows = rows.map(p => cols.map(col => cellFor(p, col))); +--- + + diff --git a/apps/docs/src/data/backers.ts b/apps/docs/src/data/backers.ts new file mode 100644 index 00000000..7997b86b --- /dev/null +++ b/apps/docs/src/data/backers.ts @@ -0,0 +1,9 @@ +// Single source of truth for Backer-tier ($20/mo) sponsors and above. +// Rendered on both /support and the homepage sponsors section. +// Add names here as they sign on — keep README.md's "## Backers" list in sync by hand. +export interface Backer { + name: string; + url?: string; +} + +export const backers: Backer[] = [{ name: 'Rich Tillman', url: 'https://opencollective.com/rich-tillman' }]; diff --git a/apps/docs/src/data/props.ts b/apps/docs/src/data/props.ts new file mode 100644 index 00000000..e9136317 --- /dev/null +++ b/apps/docs/src/data/props.ts @@ -0,0 +1,1000 @@ +// Single source of truth for DataTable prop reference tables. +// +// Both the API reference (/docs/api) and every feature page render their prop +// tables from this catalog via . Add a prop once here, tag it with +// the feature pages it belongs on, and it appears everywhere with no drift. +// +// `description` is the canonical (fuller) copy shown on the API reference. +// `brief` is an optional shorter description shown on feature pages; when a +// feature page renders a prop with no `brief`, it falls back to `description`. +// All string fields are HTML (use , >, <), matching how the +// component renders cells via set:html. + +export type PropFeature = + | 'core' + | 'layout' + | 'theming' + | 'sorting' + | 'pagination' + | 'footer' + | 'selection' + | 'expandable' + | 'row-events' + | 'keyboard' + | 'resizable' + | 'column-groups' + | 'column-pinning' + | 'filtering' + | 'column-reorder' + | 'context-menu' + | 'localization' + | 'cells' + | 'inline-editing' + | 'column-visibility' + | 'rtl' + | 'fixed-header' + | 'loading'; + +// Which api.md `###` subsection a prop renders under, in source order. +export type PropGroup = + | 'Data' + | 'Layout & appearance' + | 'Theming & styling' + | 'Sorting' + | 'Pagination' + | 'Footer' + | 'Row selection' + | 'Expandable rows' + | 'Row events' + | 'Keyboard navigation' + | 'Column features' + | 'Context menu' + | 'Localization'; + +export interface PropDef { + name: string; + /** `true` = DataTable prop, `column` = TableColumn field, `ref` = imperative handle method. */ + on?: 'table' | 'column' | 'ref'; + type: string; + default?: string; + /** Canonical description, shown on the API reference. HTML. */ + description: string; + /** Optional shorter description for feature pages. Falls back to `description`. HTML. */ + brief?: string; + group: PropGroup; + features: PropFeature[]; + deprecated?: boolean; +} + +const c = (s: string) => `${s}`; + +export const dataTableProps: PropDef[] = [ + // ── Data ───────────────────────────────────────────────────────────────── + { + name: 'data', + type: c('T[]'), + default: '-', + description: `Required. Array of row objects.`, + brief: 'Row data (required)', + group: 'Data', + features: ['core'], + }, + { + name: 'columns', + type: c('TableColumn<T>[]'), + default: '-', + description: `Required. Column definitions.`, + brief: 'Column definitions (required)', + group: 'Data', + features: ['core'], + }, + { + name: 'keyField', + type: c('string'), + default: c('"id"'), + description: `Property on each row used as a stable React key.`, + group: 'Data', + features: ['core'], + }, + { + name: 'progressPending', + type: c('boolean'), + default: c('false'), + description: `Show a loading state. On initial load (no data yet) renders shimmer skeleton rows. On re-fetch (data already loaded) dims the existing rows and overlays a centered spinner. The column header always stays visible.`, + brief: 'Show a loading state (skeleton on first load, overlay spinner on re-fetch).', + group: 'Data', + features: ['loading'], + }, + { + name: 'progressComponent', + type: c('ReactNode'), + default: 'built-in spinner', + description: `Custom loading indicator shown in the re-fetch overlay, and on initial load when ${c('progressSkeleton')} is ${c('false')}.`, + group: 'Data', + features: ['loading'], + }, + { + name: 'progressSkeleton', + type: c('boolean'), + default: c('true'), + description: `Show shimmer skeleton rows on the initial load (no data yet). Set to ${c('false')} to show ${c('progressComponent')} on initial load instead.`, + group: 'Data', + features: ['loading'], + }, + { + name: 'noDataComponent', + type: c('ReactNode'), + default: 'built-in message', + description: `Rendered when ${c('data')} is empty.`, + group: 'Data', + features: ['loading'], + }, + + // ── Layout & appearance ────────────────────────────────────────────────── + { + name: 'title', + type: `${c('string')} | ${c('ReactNode')}`, + default: '-', + description: `Table title shown in the header bar.`, + group: 'Layout & appearance', + features: ['layout'], + }, + { + name: 'actions', + type: `${c('ReactNode')} | ${c('ReactNode[]')}`, + default: '-', + description: `Content rendered on the right side of the header bar.`, + group: 'Layout & appearance', + features: ['layout'], + }, + { + name: 'subHeader', + type: c('ReactNode'), + default: '-', + description: `Content for the sub-header bar. Providing any value shows the bar.`, + group: 'Layout & appearance', + features: ['layout'], + }, + { + name: 'subHeaderAlign', + type: c('Alignment'), + default: c('"right"'), + description: `Alignment of sub-header content.`, + group: 'Layout & appearance', + features: ['layout'], + }, + { + name: 'subHeaderWrap', + type: c('boolean'), + default: c('true'), + description: `Allow sub-header to wrap onto multiple lines.`, + group: 'Layout & appearance', + features: ['layout'], + }, + { + name: 'noHeader', + type: c('boolean'), + default: c('false'), + description: `Force-hide the title/actions header bar. The bar otherwise renders only when a ${c('title')} or ${c('actions')} is provided, so you rarely need this.`, + group: 'Layout & appearance', + features: ['layout'], + }, + { + name: 'noTableHead', + type: c('boolean'), + default: c('false'), + description: `Hide the column header row.`, + group: 'Layout & appearance', + features: ['layout'], + }, + { + name: 'persistTableHead', + type: c('boolean'), + default: c('false'), + description: `Show the column header even when data is empty. The header always stays visible during ${c('progressPending')} regardless of this prop.`, + group: 'Layout & appearance', + features: ['layout', 'loading'], + }, + { + name: 'dense', + type: c('boolean'), + default: c('false'), + description: `Reduce row height for a compact look.`, + group: 'Layout & appearance', + features: ['layout'], + }, + { + name: 'responsive', + type: c('boolean'), + default: c('true'), + description: `Wrap the table in a horizontally scrollable container. Disable only when a parent element owns scrolling, see Turning the scroll container off.`, + group: 'Layout & appearance', + features: ['layout', 'fixed-header'], + }, + { + name: 'fixedHeader', + type: c('boolean'), + default: c('false'), + description: `Stick the column header at the top when scrolling.`, + brief: 'Stick the column header at the top when scrolling.', + group: 'Layout & appearance', + features: ['fixed-header'], + }, + { + name: 'fixedHeaderScrollHeight', + type: c('string'), + default: c('"100vh"'), + description: `Max height of the scrollable body when ${c('fixedHeader')} is on.`, + brief: `Max height of the scrollable body when ${c('fixedHeader')} is on.`, + group: 'Layout & appearance', + features: ['fixed-header'], + }, + { + name: 'onScroll', + type: c('(event) => void'), + default: '-', + description: `Called when the user scrolls the table body. Works with both ${c('fixedHeader')} enabled and disabled.`, + brief: `Called when the user scrolls the table body. Works with ${c('fixedHeader')} on or off.`, + group: 'Layout & appearance', + features: ['fixed-header'], + }, + { + name: 'direction', + type: c('Direction'), + default: c('Direction.AUTO'), + description: `Text direction (${c('ltr')}, ${c('rtl')}, ${c('auto')}).`, + brief: `Text direction: ${c('"ltr"')}, ${c('"rtl"')}, or ${c('"auto"')}.`, + group: 'Layout & appearance', + features: ['layout', 'rtl'], + }, + { + name: 'className', + type: c('string'), + default: '-', + description: `Extra CSS class on the root element.`, + group: 'Layout & appearance', + features: ['layout'], + }, + { + name: 'style', + type: c('CSSProperties'), + default: '-', + description: `Inline styles on the root element.`, + group: 'Layout & appearance', + features: ['layout'], + }, + { + name: 'ariaLabel', + type: c('string'), + default: '-', + description: `Value for the table's ${c('aria-label')}.`, + group: 'Layout & appearance', + features: ['layout'], + }, + { + name: 'disabled', + type: c('boolean'), + default: c('false'), + description: `Disable all interactive controls.`, + group: 'Layout & appearance', + features: ['layout'], + }, + + // ── Theming & styling ──────────────────────────────────────────────────── + { + name: 'theme', + type: c('ThemeProp'), + default: c('"default"'), + description: `Named built-in theme, a ${c('Theme')} object from ${c('createTheme()')}, or a raw CSS-variable map.`, + brief: `Named theme (e.g. ${c('"material"')}, ${c('"catppuccin"')}, ${c('"crisp"')}).`, + group: 'Theming & styling', + features: ['theming'], + }, + { + name: 'colorMode', + type: c('ColorMode'), + default: c('"system"'), + description: `Controls when the theme's dark-mode overrides apply. ${c('"system"')} follows ${c('localStorage')}, the ${c('html.dark')} class, then ${c('prefers-color-scheme')}. ${c('"light"')} / ${c('"dark"')} force a mode.`, + group: 'Theming & styling', + features: ['theming'], + }, + { + name: 'customStyles', + type: c('TableStyles'), + default: '-', + description: `Fine-grained style overrides. See TableStyles.`, + brief: `Fine-grained style overrides. See Custom styles.`, + group: 'Theming & styling', + features: ['theming'], + }, + { + name: 'conditionalRowStyles', + type: c('ConditionalStyles<T>[]'), + default: '-', + description: `Apply styles or class names to rows when a predicate matches.`, + group: 'Theming & styling', + features: ['theming'], + }, + { + name: 'striped', + type: c('boolean'), + default: c('false'), + description: `Alternate row background colors.`, + group: 'Theming & styling', + features: ['theming'], + }, + { + name: 'highlightOnHover', + type: c('boolean'), + default: c('false'), + description: `Highlight rows on mouse-over.`, + group: 'Theming & styling', + features: ['theming'], + }, + { + name: 'pointerOnHover', + type: c('boolean'), + default: c('false'), + description: `Show a pointer cursor on row hover.`, + group: 'Theming & styling', + features: ['theming'], + }, + { + name: 'columnSeparator', + type: `${c('boolean')} | ${c('"subtle"')} | ${c('"full"')}`, + default: '-', + description: `Vertical lines between body columns. ${c('true')}/${c('"subtle"')} = inset 60%-height line, ${c('"full"')} = full-height line.`, + brief: 'Body column separators (headers always shown).', + group: 'Theming & styling', + features: ['theming'], + }, + { + name: 'headerSeparator', + type: `${c('boolean')} | ${c('"subtle"')} | ${c('"full"')}`, + default: c('true'), + description: `Vertical lines between header cells. ${c('true')}/${c('"subtle"')} = inset line (default), ${c('"full"')} = full-height, ${c('false')} = none.`, + group: 'Theming & styling', + features: ['theming'], + }, + { + name: 'animateRows', + type: c('boolean'), + default: c('false'), + description: `Staggered entrance and sort animations. Respects ${c('prefers-reduced-motion')}.`, + brief: 'Staggered row entrance + sort animation.', + group: 'Theming & styling', + features: ['theming', 'expandable'], + }, + + // ── Sorting ────────────────────────────────────────────────────────────── + { + name: 'defaultSortFieldId', + type: `${c('string')} | ${c('number')}`, + default: '-', + description: `Column ${c('id')} to sort by on initial render.`, + brief: `Column ${c('id')} to sort by on first render.`, + group: 'Sorting', + features: ['sorting'], + }, + { + name: 'defaultSortAsc', + type: c('boolean'), + default: c('true'), + description: `Initial sort direction.`, + brief: `Initial sort direction. ${c('false')} = descending.`, + group: 'Sorting', + features: ['sorting'], + }, + { + name: 'sortServer', + type: c('boolean'), + default: c('false'), + description: `Disable client-side sorting; fire ${c('onSort')} and let the server sort.`, + brief: `Disable client-side sorting. Use with ${c('onSort')} to sort remotely.`, + group: 'Sorting', + features: ['sorting'], + }, + { + name: 'sortMulti', + type: c('boolean'), + default: c('false'), + description: `Enable multi-column sorting via Ctrl/⌘-click. Clicking still cycles asc → desc → off per column.`, + brief: 'Enable multi-column sorting. Ctrl/⌘-click a header to add it to the existing sort.', + group: 'Sorting', + features: ['sorting'], + }, + { + name: 'sortFunction', + type: `${c('SortFunction<T>')} | ${c('null')}`, + default: '-', + description: `Global custom sort function applied to all sortable columns.`, + brief: 'Table-level sort algorithm. Replaces the default sort for all columns.', + group: 'Sorting', + features: ['sorting'], + }, + { + name: 'sortIcon', + type: c('ReactNode'), + default: 'built-in chevron', + description: `Custom sort direction indicator.`, + brief: 'Custom icon rendered inside sortable column headers.', + group: 'Sorting', + features: ['sorting'], + }, + { + name: 'onSort', + type: c('(column, direction, sortedRows, sortColumns) => void'), + default: '-', + description: `Called whenever the sort changes. ${c('sortColumns')} is the full sort config in priority order; an empty array means the sort was cleared.`, + brief: `Called on every sort change with the primary column, its direction, the sorted rows, and the full ${c('sortColumns')} config in priority order. An empty ${c('sortColumns')} array means the sort was cleared.`, + group: 'Sorting', + features: ['sorting'], + }, + { + name: 'column.sortable', + on: 'column', + type: c('boolean'), + default: c('false'), + description: `Enable sorting on this column.`, + group: 'Sorting', + features: ['sorting'], + }, + { + name: 'column.sortFunction', + on: 'column', + type: c('(a, b) => number'), + default: '-', + description: `Per-column sort comparator. Takes priority over the table-level ${c('sortFunction')}.`, + group: 'Sorting', + features: ['sorting'], + }, + { + name: 'column.sortField', + on: 'column', + type: c('string'), + default: '-', + description: `Backend field name passed to ${c('onSort')} when it differs from ${c('column.id')}.`, + group: 'Sorting', + features: ['sorting'], + }, + { + name: 'ref.clearSort()', + on: 'ref', + type: c('DataTableHandle'), + default: '-', + description: `Imperatively reset sort to the default state. See DataTableHandle.`, + brief: `Imperatively reset sort to the default (${c('defaultSortFieldId')} / ${c('defaultSortAsc')}), or unsorted if no defaults are set. See DataTableHandle.`, + group: 'Sorting', + features: ['sorting'], + }, + + // ── Pagination ─────────────────────────────────────────────────────────── + { + name: 'pagination', + type: c('boolean'), + default: c('false'), + description: `Enable built-in pagination controls.`, + brief: 'Enable built-in pagination', + group: 'Pagination', + features: ['pagination', 'core'], + }, + { + name: 'paginationPerPage', + type: c('number'), + default: c('10'), + description: `Rows shown per page.`, + group: 'Pagination', + features: ['pagination'], + }, + { + name: 'paginationPosition', + type: `${c("'top'")} | ${c("'bottom'")} | ${c("'both'")}`, + default: c("'bottom'"), + description: `Where the pagination bar renders. ${c("'both'")} shows it above and below the table simultaneously.`, + group: 'Pagination', + features: ['pagination'], + }, + { + name: 'paginationRowsPerPageOptions', + type: c('number[]'), + default: c('[10,15,20,25,30]'), + description: `Options in the rows-per-page dropdown.`, + group: 'Pagination', + features: ['pagination'], + }, + { + name: 'paginationDefaultPage', + type: c('number'), + default: c('1'), + description: `Initial active page.`, + group: 'Pagination', + features: ['pagination'], + }, + { + name: 'paginationPage', + type: c('number'), + default: '-', + description: `Controlled active page. When provided, the table navigates to this page whenever the value changes. Use together with ${c('onChangePage')} to keep them in sync.`, + group: 'Pagination', + features: ['pagination'], + }, + { + name: 'paginationResetDefaultPage', + type: c('boolean'), + default: c('false'), + description: `Toggle to reset to page 1 (e.g. after a filter change). Prefer ${c('paginationPage')} for new code.`, + group: 'Pagination', + features: ['pagination'], + }, + { + name: 'paginationTotalRows', + type: c('number'), + default: '-', + description: `Total row count for server-side pagination.`, + group: 'Pagination', + features: ['pagination'], + }, + { + name: 'paginationServer', + type: c('boolean'), + default: c('false'), + description: `Delegate page changes to ${c('onChangePage')} / ${c('onChangeRowsPerPage')}.`, + group: 'Pagination', + features: ['pagination'], + }, + { + name: 'paginationServerOptions', + type: c('PaginationServerOptions'), + default: '-', + description: `Selection-persistence options for server-side mode.`, + group: 'Pagination', + features: ['pagination'], + }, + { + name: 'paginationComponentOptions', + type: c('PaginationOptions'), + default: '-', + description: `Localisation and display options for the built-in paginator.`, + group: 'Pagination', + features: ['pagination'], + }, + { + name: 'paginationComponent', + type: c('React.ComponentType'), + default: '-', + description: `Replace the built-in pagination UI entirely.`, + group: 'Pagination', + features: ['pagination'], + }, + { + name: 'paginationIcons', + type: c('PaginationIcons'), + default: '-', + description: `Override any or all pagination icons. Pass a partial object: ${c('{ next: <MyIcon /> }')}.`, + group: 'Pagination', + features: ['pagination'], + }, + { + name: 'onChangePage', + type: c('(page, totalRows) => void'), + default: '-', + description: `Called when the active page changes.`, + group: 'Pagination', + features: ['pagination'], + }, + { + name: 'onChangeRowsPerPage', + type: c('(rowsPerPage, page) => void'), + default: '-', + description: `Called when rows-per-page selection changes.`, + group: 'Pagination', + features: ['pagination'], + }, + + // ── Footer ─────────────────────────────────────────────────────────────── + { + name: 'footerComponent', + type: c('ComponentType<FooterComponentProps<T>>'), + default: '-', + description: `Replace the footer row with a custom component. Receives ${c('{ rows, columns }')}. Takes precedence over column-level ${c('footer')} fields.`, + group: 'Footer', + features: ['footer'], + }, + { + name: 'showFooter', + type: c('boolean'), + default: '-', + description: `Force the footer row on or off. By default the footer renders when ${c('footerComponent')} is set or any visible column declares a ${c('footer')}. Set to ${c('false')} to suppress, ${c('true')} to render an empty footer row.`, + group: 'Footer', + features: ['footer'], + }, + + // ── Row selection ──────────────────────────────────────────────────────── + { + name: 'selectableRows', + type: c('boolean'), + default: c('false'), + description: `Show a checkbox column for row selection.`, + brief: 'Enable row checkboxes', + group: 'Row selection', + features: ['selection', 'core'], + }, + { + name: 'selectableRowsSingle', + type: c('boolean'), + default: c('false'), + description: `Allow only one row to be selected at a time.`, + group: 'Row selection', + features: ['selection'], + }, + { + name: 'selectableRowsNoSelectAll', + type: c('boolean'), + default: c('false'), + description: `Hide the "select all" checkbox in the header.`, + group: 'Row selection', + features: ['selection'], + }, + { + name: 'selectableRowsVisibleOnly', + type: c('boolean'), + default: c('false'), + description: `"Select all" only selects rows on the current page.`, + group: 'Row selection', + features: ['selection'], + }, + { + name: 'selectableRowsHighlight', + type: c('boolean'), + default: c('false'), + description: `Highlight selected rows using the theme's selected color.`, + group: 'Row selection', + features: ['selection'], + }, + { + name: 'selectableRowsRange', + type: c('boolean'), + default: c('true'), + description: `Enable Shift-click range selection. Disabled automatically in single-select mode.`, + group: 'Row selection', + features: ['selection'], + }, + { + name: 'selectableRowDisabled', + type: c('(row: T) => boolean'), + default: '-', + description: `Disable selection for a specific row.`, + group: 'Row selection', + features: ['selection'], + }, + { + name: 'selectableRowSelected', + type: c('(row: T) => boolean'), + default: '-', + description: `Pre-select rows that satisfy the predicate.`, + group: 'Row selection', + features: ['selection'], + }, + { + name: 'selectedRows', + type: c('T[]'), + default: '-', + description: `Controlled selection. When supplied, drives selection state from the outside; matched against ${c('keyField')}.`, + group: 'Row selection', + features: ['selection'], + }, + { + name: 'selectableRowsComponent', + type: `${c('"input"')} | ${c('ReactNode')}`, + default: 'built-in checkbox', + description: `Custom checkbox component.`, + group: 'Row selection', + features: ['selection'], + }, + { + name: 'selectableRowsComponentProps', + type: c('object'), + default: '-', + description: `Extra props forwarded to the custom checkbox component. Function values are called with the checkbox's indeterminate state and their return value is passed instead.`, + group: 'Row selection', + features: ['selection'], + }, + { + name: 'onSelectedRowsChange', + type: c('(state) => void'), + default: '-', + description: `Called whenever selection changes. Receives ${c('{ allSelected, selectedCount, selectedRows }')}.`, + group: 'Row selection', + features: ['selection'], + }, + { + name: 'clearSelectedRows', + type: c('boolean'), + default: '-', + description: `Deprecated. Toggle to clear selection. Use ${c('ref.current.clearSelectedRows()')} instead.`, + group: 'Row selection', + features: ['selection'], + deprecated: true, + }, + { + name: 'ref.clearSelectedRows()', + on: 'ref', + type: c('DataTableHandle'), + default: '-', + description: `Imperatively deselect all selected rows. See DataTableHandle.`, + group: 'Row selection', + features: ['selection'], + }, + + // ── Expandable rows ────────────────────────────────────────────────────── + { + name: 'expandableRows', + type: c('boolean'), + default: c('false'), + description: `Enable the expandable row feature.`, + brief: 'Enable expandable row panels', + group: 'Expandable rows', + features: ['expandable', 'core'], + }, + { + name: 'expandableRowsComponent', + type: c('React.ComponentType'), + default: '-', + description: `Component rendered in the expanded panel. Receives ${c('{ data: T }')}.`, + group: 'Expandable rows', + features: ['expandable'], + }, + { + name: 'expandableRowsComponentProps', + type: c('object'), + default: '-', + description: `Extra props merged into ${c('expandableRowsComponent')}.`, + group: 'Expandable rows', + features: ['expandable'], + }, + { + name: 'expandableRowExpanded', + type: c('(row: T) => boolean'), + default: '-', + description: `Control which rows start expanded.`, + group: 'Expandable rows', + features: ['expandable'], + }, + { + name: 'expandableRowDisabled', + type: c('(row: T) => boolean'), + default: '-', + description: `Prevent specific rows from being expanded.`, + group: 'Expandable rows', + features: ['expandable'], + }, + { + name: 'expandableRowsHideExpander', + type: c('boolean'), + default: c('false'), + description: `Hide the expander chevron column (use with ${c('expandOnRowClicked')}).`, + group: 'Expandable rows', + features: ['expandable'], + }, + { + name: 'expandOnRowClicked', + type: c('boolean'), + default: c('false'), + description: `Toggle expansion by clicking anywhere on the row.`, + group: 'Expandable rows', + features: ['expandable'], + }, + { + name: 'expandOnRowDoubleClicked', + type: c('boolean'), + default: c('false'), + description: `Toggle expansion by double-clicking anywhere on the row.`, + group: 'Expandable rows', + features: ['expandable'], + }, + { + name: 'expandableInheritConditionalStyles', + type: c('boolean'), + default: c('false'), + description: `Apply the parent row's conditional styles to the expanded panel.`, + group: 'Expandable rows', + features: ['expandable'], + }, + { + name: 'expandableIcon', + type: c('{ collapsed, expanded }'), + default: 'built-in chevron', + description: `Custom expand/collapse icons.`, + group: 'Expandable rows', + features: ['expandable'], + }, + { + name: 'onRowExpandToggled', + type: c('(expanded, row) => void'), + default: '-', + description: `Called when a row is expanded or collapsed.`, + group: 'Expandable rows', + features: ['expandable'], + }, + + // ── Row events ─────────────────────────────────────────────────────────── + { + name: 'onRowClicked', + type: c('(row, event) => void'), + description: `Called when a row is left-clicked.`, + group: 'Row events', + features: ['row-events'], + }, + { + name: 'onRowDoubleClicked', + type: c('(row, event) => void'), + description: `Called when a row is double-clicked.`, + group: 'Row events', + features: ['row-events'], + }, + { + name: 'onRowMiddleClicked', + type: c('(row, event) => void'), + description: `Called when a row is middle-clicked (scroll-click). Use with ${c('onRowClicked')} to implement open-in-new-tab behaviour.`, + group: 'Row events', + features: ['row-events'], + }, + { + name: 'onRowMouseEnter', + type: c('(row, event) => void'), + description: `Called when the pointer enters a row.`, + group: 'Row events', + features: ['row-events'], + }, + { + name: 'onRowMouseLeave', + type: c('(row, event) => void'), + description: `Called when the pointer leaves a row.`, + group: 'Row events', + features: ['row-events'], + }, + + // ── Keyboard navigation ────────────────────────────────────────────────── + { + name: 'cellNavigation', + type: c('boolean'), + default: c('false'), + description: `Spreadsheet-style keyboard navigation using the WAI-ARIA grid pattern (${c('role="grid"')}, single Tab stop, roving tabindex). Arrow keys move between cells, including header, selection, and expander cells; ${c('Home')}/${c('End')} and ${c('Ctrl+Home')}/${c('Ctrl+End')} jump to row and grid edges; ${c('Enter')}/${c('F2')} open a cell editor; ${c('Enter')}/${c('Space')} sort from a header. See Keyboard navigation.`, + brief: `Enable spreadsheet-style keyboard navigation (WAI-ARIA grid pattern: ${c("role='grid'")}, single Tab stop, roving tabindex). Defaults to ${c('false')}.`, + group: 'Keyboard navigation', + features: ['keyboard'], + }, + + // ── Column features ────────────────────────────────────────────────────── + { + name: 'resizable', + type: c('boolean'), + default: c('false'), + description: `Show drag handles on column headers for resizing.`, + brief: 'Show drag handles on column headers for resizing.', + group: 'Column features', + features: ['resizable'], + }, + { + name: 'columnGroups', + type: c('ColumnGroup[]'), + default: '-', + description: `Spanning group headers rendered above the column header row.`, + group: 'Column features', + features: ['column-groups'], + }, + { + name: 'filterValues', + type: c('Record<string | number, FilterState>'), + default: '-', + description: `Controlled filter state. Omit to use internal state. See Filtering.`, + group: 'Column features', + features: ['filtering'], + }, + { + name: 'onFilterChange', + type: c('(columnId, filter: FilterState) => void'), + default: '-', + description: `Called when the user clicks Apply or Clear in a filter popup.`, + group: 'Column features', + features: ['filtering'], + }, + { + name: 'onColumnOrderChange', + type: c('(columns: TableColumn<T>[]) => void'), + default: '-', + description: `Called after a drag-to-reorder column operation with the new column order.`, + group: 'Column features', + features: ['column-reorder'], + }, + { + name: 'onColumnGroupOrderChange', + type: c('(groups: ColumnGroup[], columns: TableColumn<T>[]) => void'), + default: '-', + description: `Called after a group drag-reorder with the new group order and the matching updated column order.`, + group: 'Column features', + features: ['column-reorder', 'column-groups'], + }, + { + name: 'column.pinned', + on: 'column', + type: `${c("'left'")} | ${c("'right'")}`, + default: '-', + description: `Freeze the column to an edge during horizontal scroll. Only visible when the table overflows its container — give columns explicit widths. See Column pinning.`, + brief: `Freeze the column to an edge during horizontal scroll. Only visible when the table overflows its container — give columns explicit widths.`, + group: 'Column features', + features: ['column-pinning'], + }, + + // ── Context menu ───────────────────────────────────────────────────────── + { + name: 'contextMenu', + type: `${c("boolean | { header?: boolean; row?: boolean; trigger?: 'right-click' | 'menu-button' | 'both'; menuPosition?: 'start' | 'end' }")}`, + default: '-', + description: `Enable the context menu. ${c('true')} enables header and row menus with the right-click trigger. The header menu has built-in sort/pin/hide/reset actions; the row menu only opens when ${c('contextMenuActions.row')} returns items. ${c('menuPosition')} sets which inline edge the row menu button sits on (default ${c("'end'")} — right in LTR). See Context menu.`, + brief: `Enable the context menu. ${c('true')} enables header and row menus with the right-click trigger. The header menu has built-in sort/pin/hide/reset actions; the row menu only opens when ${c('contextMenuActions.row')} returns items.`, + group: 'Context menu', + features: ['context-menu'], + }, + { + name: 'contextMenuActions', + type: c('{ header?: ContextMenuAction[] | (column) => ContextMenuAction[]; row?: (row, rowIndex) => ContextMenuAction[] }'), + default: '-', + description: `Custom menu items. Header items are appended after the built-ins; row items are the entire row menu.`, + group: 'Context menu', + features: ['context-menu'], + }, + { + name: 'onContextMenuAction', + type: c('(action: ContextMenuAction, ctx: ContextMenuActionContext<T>) => void'), + default: '-', + description: `Called for every selected item, built-ins included (after their effect is applied). ${c('ctx')} is ${c("{ type: 'header', column }")} or ${c("{ type: 'row', row, rowIndex }")}.`, + group: 'Context menu', + features: ['context-menu'], + }, + + // ── Localization ───────────────────────────────────────────────────────── + { + name: 'localization', + type: c('Localization'), + default: '-', + description: `Override every user-visible string and aria-label in the table. Pass a pre-built locale or build your own. See Localization.`, + brief: `Override every user-visible string and aria-label in the table. Pass a pre-built locale or build your own.`, + group: 'Localization', + features: ['localization', 'expandable', 'pagination'], + }, + { + name: 'columnFilterOptions', + type: c('ColumnFilterOptions'), + default: '-', + description: `Deprecated. Use ${c('localization={{ filter: { ... } }}')} instead. Will be removed in v9.`, + group: 'Localization', + features: ['localization'], + deprecated: true, + }, + { + name: 'expandableRowsOptions', + type: c('ExpandableRowsOptions'), + default: '-', + description: `Deprecated. Use ${c('localization={{ expandable: { ... } }}')} instead. Will be removed in v9.`, + group: 'Localization', + features: ['localization'], + deprecated: true, + }, +]; + +export const propGroupOrder: PropGroup[] = [ + 'Data', + 'Layout & appearance', + 'Theming & styling', + 'Sorting', + 'Pagination', + 'Footer', + 'Row selection', + 'Expandable rows', + 'Row events', + 'Keyboard navigation', + 'Column features', + 'Context menu', + 'Localization', +]; diff --git a/apps/docs/src/pages/docs/animations.astro b/apps/docs/src/pages/docs/animations.astro index 1ce7491c..4ef0db4c 100644 --- a/apps/docs/src/pages/docs/animations.astro +++ b/apps/docs/src/pages/docs/animations.astro @@ -3,6 +3,7 @@ import DocsLayout from '../../layouts/DocsLayout.astro'; import Demo from '../../components/Demo.astro'; import CodeBlock from '../../components/CodeBlock.astro'; import DocsTable from '../../components/DocsTable.astro'; +import PropsTable from '../../components/PropsTable.astro'; import AnimationsDemo from '../../components/demos/AnimationsDemo.tsx'; --- @@ -111,4 +112,8 @@ export default function App() { --rdt-row-anim-duration: 300ms; --rdt-row-anim-stagger: 20ms; }`} lang="css" /> + +

    Prop reference

    + + diff --git a/apps/docs/src/pages/docs/api.astro b/apps/docs/src/pages/docs/api.astro new file mode 100644 index 00000000..6677604f --- /dev/null +++ b/apps/docs/src/pages/docs/api.astro @@ -0,0 +1,648 @@ +--- +import DocsLayout from '../../layouts/DocsLayout.astro'; +import CodeBlock from '../../components/CodeBlock.astro'; +import DocsTable from '../../components/DocsTable.astro'; +import PropsTable from '../../components/PropsTable.astro'; +--- + + +

    API reference

    + +

    + Complete reference for every prop, type, and export in react-data-table-component v8. +

    + +
    +

    + Looking for examples or recipes? This page is the flat reference for "what does prop X do?". + Each feature has its own page with examples. Start there if you're new: +

    + +
    + +

    DataTable props

    + +

    + DataTable<T> accepts the following props where T is the row data type. + Only columns and data are required. +

    + +

    Data

    + + +

    Layout & appearance

    + + +

    Theming & styling

    + + +

    Sorting

    + + +

    Pagination

    + + + + +

    + Column-level footers live on each TableColumn<T> as the + footer field. See Footer for the full walkthrough. +

    + +

    Row selection

    + + +

    Expandable rows

    + + +

    Row events

    + + +

    Keyboard navigation

    + + +

    Column features

    + + +

    Context menu

    + + +

    Localization

    + + +

    ColumnGroup

    + +

    + Defines a spanning group header above one or more columns. Pass an array to the + columnGroups prop. +

    + + + + name', 'ReactNode', '-', 'Required. Group header label. Accepts JSX.'], + ['columnIds', '(string | number)[]', '-', 'Required. Ids of columns under this group. Must match each column's id exactly.'], + ['align', "'left' | 'center' | 'right'", "'center'", 'Horizontal alignment of the group label.'], + ['reorder', 'boolean', '-', 'Allow drag-to-reorder for this group. Dragging moves all member columns as a block. Fires onColumnGroupOrderChange.'], + ]} + /> + +

    Columns not listed in any group span the full group-row height with no label.

    + +

    TableColumn<T>

    + + [] = [ + { + id: 'name', + name: 'Full name', + selector: row => row.name, + sortable: true, + width: '200px', + }, +];`} lang="ts" /> + + id', 'string | number', 'Stable identity used by defaultSortFieldId, columnGroups, and filterValues.'], + ['name', 'ReactNode', 'Column header label.'], + ['selector', '(row, index?) => Primitive | ReactNode', 'Data accessor. For sortable columns return a Primitive (string | number | boolean | bigint | Date). Rows whose selector returns null or undefined sort to the end. bigint displays as its decimal string; Date displays via toLocaleString(), which follows the viewer's locale and timezone. Use format when you need deterministic or SSR-stable output.'], + ['cell', '(row, index, column, id) => ReactNode', 'Custom cell renderer. Overrides the selector display value.'], + ['format', '(row, index) => ReactNode', 'Format the selector value for display without changing the sort key.'], + ['sortable', 'boolean', 'Enable sorting on this column.'], + ['sortFunction', '(a: T, b: T) => number', 'Custom comparator for this column.'], + ['sortField', 'string', 'Field key passed to onSort for server-side sort identification.'], + ['filterable', 'boolean', 'Show a filter popup for this column.'], + ['filterType', '"text" | "number" | "date"', 'Filter operator set and input widget. Defaults to "text". See Filtering.'], + ['filterFunction', '(row, filter: FilterState) => boolean', 'Custom filter predicate. Overrides built-in operator logic. Receives the full FilterState including both conditions.'], + ['width', 'string', 'Fixed column width, e.g. "120px".'], + ['minWidth', 'string', 'Minimum width.'], + ['maxWidth', 'string', 'Maximum width.'], + ['grow', 'number', 'Flex-grow factor (default 1). Set to 0 to prevent growing.'], + ['right', 'boolean', 'Right-align cell content.'], + ['center', 'boolean', 'Center cell content.'], + ['wrap', 'boolean', 'Allow cell text to wrap.'], + ['compact', 'boolean', 'Remove cell padding.'], + ['button', 'boolean', 'Center content and suppress row click propagation.'], + ['allowOverflow', 'boolean', 'Allow cell content to overflow (useful for dropdowns and popovers).'], + ['ignoreRowClick', 'boolean', 'Prevent clicks in this cell from firing onRowClicked.'], + ['hide', 'Media | number', 'Hide the column below the given breakpoint (Media.SM = 599px, MD = 959px, LG = 1280px) or a custom pixel value.'], + ['omit', 'boolean', 'Exclude the column entirely. Toggle this to show/hide a column.'], + ['reorder', 'boolean', 'Allow drag-to-reorder for this column (requires reorder on at least two columns).'], + ['pinned', "'left' | 'right'", 'Freeze the column to an edge during horizontal scroll. Only visible when the table overflows its container — give columns explicit widths. See Column pinning.'], + ['style', 'CSSProperties', 'Inline styles applied to every cell in this column.'], + ['conditionalCellStyles', 'ConditionalStyles<T>[]', 'Per-cell conditional styles.'], + ['footer', 'ReactNode | (rows: T[]) => ReactNode', 'Footer cell for this column. Static node or a function receiving the filtered+sorted rows (typically used to render aggregates like sums or averages). When any visible column has a footer, a footer row renders below the body. See Footer.'], + ]} + /> + +

    Inline editing

    + + editable', 'boolean', 'Shorthand for editor: { type: 'text' }. Ignored when editor is also set.'], + ['editor', 'CellEditor', 'Editor configuration. See CellEditor below. Takes precedence over editable.'], + ['validate', '(value, row, column) => true | false | string', 'Gate the edit before onCellEdit fires. Return true to accept, false to reject silently, or a string error to keep the editor open with an inline tooltip.'], + ['onCellEdit', 'CellEditCallback<T>', 'Called when the user commits an edit. Receives (row: T, value: string, column: TableColumn<T>). The value is always a string; parse it to the target type in your handler.'], + ]} + /> + +

    See Inline editing for examples, CSS variables, and styling guidance.

    + +

    CellEditor

    + + = + | { type: 'text'; placeholder?: string } + | { type: 'number'; placeholder?: string; min?: number; max?: number; step?: number } + | { type: 'date'; min?: string; max?: string } + | { type: 'checkbox' } + | { + type: 'select'; + options: Array<{ value: string; label: React.ReactNode }>; + placeholder?: string; + } + | { + type: 'custom'; + render: (ctx: CustomCellEditorContext) => React.ReactNode; + }; + +interface CustomCellEditorContext { + row: T; + value: string; + setValue: (next: string) => void; + commit: (value?: string) => void; + cancel: () => void; + column: TableColumn; +}`} lang="ts" /> + + type', '"text" | "number" | "date" | "checkbox" | "select" | "custom"', 'Editor widget to render.'], + ['text / number / select', 'placeholder', 'string', 'Placeholder text. For select, shown as a disabled hidden option when the current value is empty.'], + ['number', 'min / max / step', 'number', 'Forwarded to the native <input type="number">.'], + ['date', 'min / max', 'string', 'Forwarded to the native <input type="date"> as ISO dates.'], + ['select', 'options', '{ value: string; label: ReactNode }[]', 'Dropdown options. The value is what gets committed; label is displayed (must be string or number inside native <option>).'], + ['custom', 'render', '(ctx) => ReactNode', 'Render any editor. Call ctx.commit(value) to save, ctx.cancel() to discard.'], + ]} + /> + +

    Inline editing CSS classes

    + + rdt_cellEditable', 'Cell container', 'Added on every cell that has an editor defined (idle state).'], + ['rdt_cellEditing', 'Cell container', 'Added while the editor is open. Removes padding and paints the focus ring.'], + ['rdt_editInput', '<input>', 'Text editor control inside an editing cell.'], + ['rdt_editSelect', '<select>', 'Dropdown editor control inside an editing cell.'], + ]} + /> + +

    Inline editing CSS variables

    + + --rdt-color-cell-edit-bg', '8% primary on bg', 'Background of the cell while editing.'], + ['--rdt-color-cell-edit-hover', '6% primary', 'Background of an editable cell on hover.'], + ['--rdt-color-cell-edit-hover-border', '40% primary', 'Dashed underline colour on editable cell hover.'], + ]} + /> + +

    ContextMenuAction

    + +

    + A single item in a context menu. Pass arrays of these via contextMenuActions; + selections arrive in onContextMenuAction. +

    + + [ + { id: 'edit', label: 'Edit' }, + { id: 'delete', label: 'Delete', disabled: row.locked }, +];`} lang="ts" /> + + id', 'string', 'Identifier passed to onContextMenuAction. The built-in header actions reserve sort-asc, sort-desc, clear-sort, pin-left, pin-right, unpin, hide-column, and reset.'], + ['label', 'ReactNode', 'Item content.'], + ['disabled', 'boolean', 'Render the item disabled.'], + ['icon', 'ReactNode', 'Optional icon rendered before the label.'], + ]} + /> + +

    + Related types: ContextMenuConfig (the object form of the contextMenu prop), + ContextMenuActions<T> (the contextMenuActions prop shape), and + ContextMenuActionContext<T> (the ctx argument of onContextMenuAction) + are all exported. See Context menu. +

    + +

    DataTableHandle (ref)

    + +

    Attach a ref to DataTable to imperatively control it.

    + + (null); + + return ( + <> + + + + + ); +}`} lang="tsx" /> + + clearSelectedRows()', 'Deselect all currently selected rows.'], + ['clearSort()', 'Reset sort to the default (defaultSortFieldId / defaultSortAsc), or unsorted if no defaults are set.'], + ]} + /> + +

    TableStyles

    + +

    + Pass to customStyles to override styles for any part of DataTable. + Each key accepts a style object (React.CSSProperties) and optional extra properties. +

    + + + + table', '-', 'The outermost table element.'], + ['tableWrapper', '-', 'Wrapper <div> around the table.'], + ['responsiveWrapper', '-', 'Horizontal-scroll wrapper (only present when responsive).'], + ['header', 'fontColor, fontSize', 'Title / actions bar at the top.'], + ['subHeader', '-', 'Sub-header bar.'], + ['head', '-', 'The <thead> element.'], + ['headRow', 'denseStyle', 'Column header row.'], + ['headCells', 'draggingStyle', 'Individual header cells.'], + ['cells', 'draggingStyle', 'Body cells.'], + ['rows', 'selectedHighlightStyle, denseStyle, highlightOnHoverStyle, stripedStyle', 'Body rows.'], + ['expanderRow', '-', 'The expanded content row.'], + ['expanderCell', '-', 'Cell containing the expand/collapse button.'], + ['expanderButton', '-', 'The expand/collapse button itself.'], + ['pagination', 'pageButtonsStyle', 'Pagination bar and page buttons.'], + ['footer', '-', 'The footer row container.'], + ['footerCells', '-', 'Individual footer cells.'], + ['noData', '-', 'Empty-state container.'], + ['progress', '-', 'Loading indicator container.'], + ]} + /> + +

    ConditionalStyles<T>

    + +

    + Used with conditionalRowStyles and conditionalCellStyles to apply styles + or class names based on row data. +

    + + row.status === 'active', + style: { backgroundColor: '#f0fdf4', color: '#166534' }, + }, + { + when: row => row.salary > 100000, + // style can also be a function: + style: row => ({ fontWeight: row.salary > 150000 ? 700 : 400 }), + }, + { + when: row => row.flagged, + classNames: ['flagged-row'], + }, +];`} lang="tsx" /> + + when', '(row: T) => boolean', 'Required. Predicate that applies styles when this returns true.'], + ['style', 'CSSProperties | ((row: T) => CSSProperties)', 'Inline styles to apply.'], + ['classNames', 'string[]', 'CSS class names to add.'], + ]} + /> + +

    createTheme()

    + +

    Registers a custom named theme. Call this outside your component tree so it runs once.

    + + + + name', 'string', 'Theme name to reference in the theme prop.'], + ['overrides', 'Partial<Theme>', 'Token overrides. Only the keys you provide are changed.'], + ['inherit', 'string', 'Optional name of a built-in or previously registered theme to inherit from.'], + ]} + /> + +

    The Theme interface token keys:

    + + text', 'primary, secondary, disabled'], + ['background', 'default'], + ['context', 'background, text'], + ['divider', 'default'], + ['button', 'default, hover, focus, disabled'], + ['selected', 'default, text'], + ['highlightOnHover', 'default, text'], + ['striped', 'default, text'], + ['colorScheme', '"light" | "dark". Sets native form control rendering.'], + ]} + /> + +

    Pagination types

    + +

    PaginationOptions

    + +

    Passed to paginationComponentOptions.

    + + rowsPerPageText', 'string', '"Rows per page:"', 'Label for the rows-per-page select.'], + ['rangeSeparatorText', 'string', '"of"', 'Separator between current range and total, e.g. "1–10 of 50".'], + ['noRowsPerPage', 'boolean', 'false', 'Hide the rows-per-page selector.'], + ['selectAllRowsItem', 'boolean', 'false', 'Add an "All" option to the rows-per-page selector.'], + ['selectAllRowsItemText', 'string', '"All"', 'Label for the "All" option.'], + ]} + /> + +

    PaginationServerOptions

    + +

    Passed to paginationServerOptions when using server-side pagination.

    + + persistSelectedOnPageChange', 'boolean', 'false', 'Keep the current selection when the page changes.'], + ['persistSelectedOnSort', 'boolean', 'false', 'Keep the current selection when the sort column or direction changes.'], + ]} + /> + +

    PaginationComponentProps

    + +

    The props your custom paginationComponent will receive.

    + + rowsPerPage', 'number', 'Currently selected rows-per-page value.'], + ['rowCount', 'number', 'Total row count.'], + ['currentPage', 'number', 'Current active page (1-based).'], + ['onChangePage', '(page, totalRows) => void', 'Call this to change page.'], + ['onChangeRowsPerPage', '(rowsPerPage, page) => void', 'Call this to change rows-per-page.'], + ]} + /> + +

    Enums & constants

    + +

    All enums and constants are re-exported from the package root.

    + + + + Media', 'SM = "sm" (≤ 599px), MD = "md" (≤ 959px), LG = "lg" (≤ 1280px)', 'Breakpoints for the column hide prop.'], + ['Direction', 'LTR = "ltr", RTL = "rtl", AUTO = "auto"', 'Text direction for the direction prop.'], + ['Alignment', 'LEFT = "left", RIGHT = "right", CENTER = "center"', 'Alignment for subHeaderAlign.'], + ['SortOrder', 'ASC = "asc", DESC = "desc"', 'Sort direction passed to onSort and sortFunction.'], + ]} + /> + +

    useColumnVisibility

    + +

    + Hook for managing column show/hide state outside of DataTable. Pairs with the + omit column prop. +

    + + [] = [ + { id: 'name', name: 'Name', selector: r => r.name }, + { id: 'dept', name: 'Department', selector: r => r.dept }, +]; + +function App() { + const { columns, visibility, toggle, setAll } = useColumnVisibility(rawColumns); + + return ( + <> + {visibility.map(({ id, name, visible }) => ( + + ))} + + + + ); +}`} lang="tsx" /> + +

    UseColumnVisibilityResult

    + + columns', 'TableColumn<T>[]', 'Columns with omit set according to current visibility. Pass directly to DataTable.'], + ['visibility', 'ColumnVisibilityEntry[]', 'Current visibility state for every column.'], + ['toggle', '(id: string | number) => void', 'Toggle the visibility of a single column by id.'], + ['setAll', '(visible: boolean) => void', 'Show or hide all columns at once.'], + ]} + /> + +

    ColumnVisibilityEntry

    + + id', 'string | number', 'Column id.'], + ['name', 'ReactNode', 'Column name (mirrors the name field from the column definition).'], + ['visible', 'boolean', 'Whether the column is currently visible.'], + ]} + /> + +

    useTableExport

    + +

    + Hook for generating CSV / JSON from your columns and rows, with download + clipboard helpers. + See Export (CSV / JSON) for the full guide. +

    + + [] = [ + { id: 'id', name: 'ID', selector: r => r.id }, + { id: 'name', name: 'Name', selector: r => r.name }, +]; + +function App({ rows }: { rows: Row[] }) { + const { download, copy, toCSV, toJSON } = useTableExport({ columns, rows }); + + return ( + <> + + + + + ); +}`} lang="tsx" /> + +

    UseTableExportOptions

    + + columns', 'TableColumn<T>[]', 'required', 'Same array you pass to DataTable. omit: true columns are skipped.'], + ['rows', 'T[]', 'required', 'Rows to export.'], + ['valueSource', "'selector' | 'format'", "'selector'", "'selector' exports raw values; 'format' runs column.format first."], + ['headerOverrides', 'Record<string | number, string>', '–', 'Replace header label per column id.'], + ['columnOrder', '(string | number)[]', '–', 'Restrict and reorder columns by id.'], + ]} + /> + +

    UseTableExportResult

    + + toCSV', '() => string', 'Build a CSV string.'], + ['toJSON', '() => string', 'Build a pretty-printed JSON string.'], + ['download', "(filename: string, format?: 'csv' | 'json') => void", 'Trigger a browser download. SSR-safe (no-op when document is undefined).'], + ['copy', "(format?: 'csv' | 'json') => Promise<void>", 'Copy to clipboard via navigator.clipboard. Rejects when unavailable.'], + ]} + /> + +

    Package exports

    + + +
    diff --git a/apps/docs/src/pages/docs/api.md b/apps/docs/src/pages/docs/api.md deleted file mode 100644 index 18149cc0..00000000 --- a/apps/docs/src/pages/docs/api.md +++ /dev/null @@ -1,698 +0,0 @@ ---- -layout: '../../layouts/DocsLayout.astro' -title: 'API reference | react-data-table-component' -description: 'Complete prop, type, and export reference for react-data-table-component v8.' ---- - -# API reference - -Complete reference for every prop, type, and export in `react-data-table-component` v8. - -> **Looking for examples or recipes?** This page is the flat reference for "what does prop X do?". Each feature has its own page with examples. Start there if you're new: -> -> - [Columns](/docs/columns) · [Cell rendering](/docs/cells) · [Inline editing](/docs/inline-editing) -> - [Sorting](/docs/sorting) · [Pagination](/docs/pagination) · [Filtering](/docs/filtering) -> - [Selection](/docs/selection) · [Expandable rows](/docs/expandable) -> - [Resizable](/docs/resizable) · [Pinning](/docs/column-pinning) · [Groups](/docs/column-groups) -> - [Themes](/docs/themes) · [Custom styles](/docs/custom-styles) · [Performance](/docs/performance) -> - [TypeScript](/docs/typescript) · [Headless hooks](/docs/headless) - -## DataTable props - -`DataTable` accepts the following props where `T` is the row data type. Only `columns` and `data` are required. - -### Data - -| Prop | Type | Default | Description | -|---|---|---|---| -| `data` | `T[]` | - | **Required.** Array of row objects. | -| `columns` | `TableColumn[]` | - | **Required.** Column definitions. | -| `keyField` | `string` | `"id"` | Property on each row used as a stable React key. | -| `progressPending` | `boolean` | `false` | Show a loading state. On initial load (no data yet) renders shimmer skeleton rows. On re-fetch (data already loaded) dims the existing rows and overlays a centered spinner. The column header always stays visible. | -| `progressComponent` | `ReactNode` | built-in spinner | Custom loading indicator shown in the re-fetch overlay, and on initial load when `progressSkeleton` is `false`. | -| `progressSkeleton` | `boolean` | `true` | Show shimmer skeleton rows on the initial load (no data yet). Set to `false` to show `progressComponent` on initial load instead. | -| `noDataComponent` | `ReactNode` | built-in message | Rendered when `data` is empty. | - -### Layout & appearance - -| Prop | Type | Default | Description | -|---|---|---|---| -| `title` | `string \| ReactNode` | - | Table title shown in the header bar. | -| `actions` | `ReactNode \| ReactNode[]` | - | Content rendered on the right side of the header bar. | -| `subHeader` | `ReactNode` | - | Content for the sub-header bar. Providing any value shows the bar. | -| `subHeaderAlign` | `Alignment` | `"right"` | Alignment of sub-header content. | -| `subHeaderWrap` | `boolean` | `true` | Allow sub-header to wrap onto multiple lines. | -| `noHeader` | `boolean` | `false` | Force-hide the title/actions header bar. The bar otherwise renders only when a `title` or `actions` is provided, so you rarely need this. | -| `noTableHead` | `boolean` | `false` | Hide the column header row. | -| `persistTableHead` | `boolean` | `false` | Show the column header even when data is empty. The header always stays visible during `progressPending` regardless of this prop. | -| `dense` | `boolean` | `false` | Reduce row height for a compact look. | -| `responsive` | `boolean` | `true` | Wrap the table in a horizontally scrollable container. Disable only when a parent element owns scrolling, see [Turning the scroll container off](/docs/fixed-header#responsive-false). | -| `fixedHeader` | `boolean` | `false` | Stick the column header at the top when scrolling. | -| `fixedHeaderScrollHeight` | `string` | `"100vh"` | Max height of the scrollable body when `fixedHeader` is on. | -| `onScroll` | `(event) => void` | - | Called when the user scrolls the table body. Works with both `fixedHeader` enabled and disabled. | -| `direction` | `Direction` | `"ltr"` | Text direction (`ltr`, `rtl`, `auto`). | -| `className` | `string` | - | Extra CSS class on the root element. | -| `style` | `CSSProperties` | - | Inline styles on the root element. | -| `ariaLabel` | `string` | - | Value for the table's `aria-label`. | -| `disabled` | `boolean` | `false` | Disable all interactive controls. | - -### Theming & styling - -| Prop | Type | Default | Description | -|---|---|---|---| -| `theme` | `string` | `"default"` | Named theme registered with `createTheme()`. | -| `customStyles` | `TableStyles` | - | Fine-grained style overrides. See [TableStyles](#tablestyles). | -| `conditionalRowStyles` | `ConditionalStyles[]` | - | Apply styles or class names to rows when a predicate matches. | -| `striped` | `boolean` | `false` | Alternate row background colors. | -| `highlightOnHover` | `boolean` | `false` | Highlight rows on mouse-over. | -| `pointerOnHover` | `boolean` | `false` | Show a pointer cursor on row hover. | -| `columnSeparator` | `boolean \| "subtle" \| "full"` | - | Vertical lines between body columns. `true`/`"subtle"` = inset 60%-height line, `"full"` = full-height line. | -| `headerSeparator` | `boolean \| "subtle" \| "full"` | `true` | Vertical lines between header cells. `true`/`"subtle"` = inset line (default), `"full"` = full-height, `false` = none. | -| `animateRows` | `boolean` | `false` | Staggered entrance and sort animations. Respects `prefers-reduced-motion`. | - -### Sorting - -| Prop | Type | Default | Description | -|---|---|---|---| -| `defaultSortFieldId` | `string \| number` | - | Column `id` to sort by on initial render. | -| `defaultSortAsc` | `boolean` | `true` | Initial sort direction. | -| `sortServer` | `boolean` | `false` | Disable client-side sorting; fire `onSort` and let the server sort. | -| `sortMulti` | `boolean` | `false` | Enable multi-column sorting via Ctrl/⌘-click. Clicking still cycles asc → desc → off per column. | -| `sortFunction` | `SortFunction \| null` | - | Global custom sort function applied to all sortable columns. | -| `sortIcon` | `ReactNode` | built-in chevron | Custom sort direction indicator. | -| `onSort` | `(column, direction, sortedRows, sortColumns) => void` | - | Called whenever the sort changes. `sortColumns` is the full sort config in priority order; an empty array means the sort was cleared. | -| `ref.clearSort()` | `DataTableHandle` | - | Imperatively reset sort to the default state. See [DataTableHandle](#datatablehandle-ref). | - -### Pagination - -| Prop | Type | Default | Description | -|---|---|---|---| -| `pagination` | `boolean` | `false` | Enable built-in pagination controls. | -| `paginationPerPage` | `number` | `10` | Rows shown per page. | -| `paginationPosition` | `'top' \| 'bottom' \| 'both'` | `'bottom'` | Where the pagination bar renders. `'both'` shows it above and below the table simultaneously. | -| `paginationRowsPerPageOptions` | `number[]` | `[10,15,20,25,30]` | Options in the rows-per-page dropdown. | -| `paginationDefaultPage` | `number` | `1` | Initial active page. | -| `paginationPage` | `number` | - | Controlled active page. When provided, the table navigates to this page whenever the value changes. Use together with `onChangePage` to keep them in sync. | -| `paginationResetDefaultPage` | `boolean` | `false` | Toggle to reset to page 1 (e.g. after a filter change). Prefer `paginationPage` for new code. | -| `paginationTotalRows` | `number` | - | Total row count for server-side pagination. | -| `paginationServer` | `boolean` | `false` | Delegate page changes to `onChangePage` / `onChangeRowsPerPage`. | -| `paginationServerOptions` | `PaginationServerOptions` | - | Selection-persistence options for server-side mode. | -| `paginationComponentOptions` | `PaginationOptions` | - | Localisation and display options for the built-in paginator. | -| `paginationComponent` | `React.ComponentType` | - | Replace the built-in pagination UI entirely. | -| `paginationIcons` | `PaginationIcons` | - | Override any or all pagination icons. Pass a partial object: `{ next: }`. | -| `onChangePage` | `(page, totalRows) => void` | - | Called when the active page changes. | -| `onChangeRowsPerPage` | `(rowsPerPage, page) => void` | - | Called when rows-per-page selection changes. | - -### Footer - -| Prop | Type | Default | Description | -|---|---|---|---| -| `footerComponent` | `ComponentType>` | - | Replace the footer row with a custom component. Receives `{ rows, columns }`. Takes precedence over column-level `footer` fields. | -| `showFooter` | `boolean` | - | Force the footer row on or off. By default the footer renders when `footerComponent` is set or any visible column declares a `footer`. Set to `false` to suppress, `true` to render an empty footer row. | - -Column-level footers live on each [`TableColumn`](#tablecolumnt) as the `footer` field. See [Footer](/docs/footer) for the full walkthrough. - -### Row selection - -| Prop | Type | Default | Description | -|---|---|---|---| -| `selectableRows` | `boolean` | `false` | Show a checkbox column for row selection. | -| `selectableRowsSingle` | `boolean` | `false` | Allow only one row to be selected at a time. | -| `selectableRowsNoSelectAll` | `boolean` | `false` | Hide the "select all" checkbox in the header. | -| `selectableRowsVisibleOnly` | `boolean` | `false` | "Select all" only selects rows on the current page. | -| `selectableRowsHighlight` | `boolean` | `false` | Highlight selected rows using the theme's selected color. | -| `selectableRowsRange` | `boolean` | `true` | Enable Shift-click range selection. Disabled automatically in single-select mode. | -| `selectableRowDisabled` | `(row: T) => boolean` | - | Disable selection for a specific row. | -| `selectableRowSelected` | `(row: T) => boolean` | - | Pre-select rows that satisfy the predicate. | -| `selectedRows` | `T[]` | - | Controlled selection. When supplied, drives selection state from the outside; matched against `keyField`. | -| `selectableRowsComponent` | `"input" \| ReactNode` | built-in checkbox | Custom checkbox component. | -| `selectableRowsComponentProps` | `object` | - | Extra props forwarded to the custom checkbox component. Function values are called with the checkbox's indeterminate state and their return value is passed instead. | -| `onSelectedRowsChange` | `(state) => void` | - | Called whenever selection changes. Receives `{ allSelected, selectedCount, selectedRows }`. | -| `clearSelectedRows` | `boolean` | - | **Deprecated.** Toggle to clear selection. Use `ref.current.clearSelectedRows()` instead. | -| `ref.clearSelectedRows()` | `DataTableHandle` | - | Imperatively deselect all selected rows. See [DataTableHandle](#datatablehandle-ref). | - -### Expandable rows - -| Prop | Type | Default | Description | -|---|---|---|---| -| `expandableRows` | `boolean` | `false` | Enable the expandable row feature. | -| `expandableRowsComponent` | `React.ComponentType` | - | Component rendered in the expanded panel. Receives `{ data: T }`. | -| `expandableRowsComponentProps` | `object` | - | Extra props merged into `expandableRowsComponent`. | -| `expandableRowExpanded` | `(row: T) => boolean` | - | Control which rows start expanded. | -| `expandableRowDisabled` | `(row: T) => boolean` | - | Prevent specific rows from being expanded. | -| `expandableRowsHideExpander` | `boolean` | `false` | Hide the expander chevron column (use with `expandOnRowClicked`). | -| `expandOnRowClicked` | `boolean` | `false` | Toggle expansion by clicking anywhere on the row. | -| `expandOnRowDoubleClicked` | `boolean` | `false` | Toggle expansion by double-clicking anywhere on the row. | -| `expandableInheritConditionalStyles` | `boolean` | `false` | Apply the parent row's conditional styles to the expanded panel. | -| `expandableIcon` | `{ collapsed, expanded }` | built-in chevron | Custom expand/collapse icons. | -| `onRowExpandToggled` | `(expanded, row) => void` | - | Called when a row is expanded or collapsed. | - -### Row events - -| Prop | Type | Description | -|---|---|---| -| `onRowClicked` | `(row, event) => void` | Called when a row is left-clicked. | -| `onRowDoubleClicked` | `(row, event) => void` | Called when a row is double-clicked. | -| `onRowMiddleClicked` | `(row, event) => void` | Called when a row is middle-clicked (scroll-click). Use with `onRowClicked` to implement open-in-new-tab behaviour. | -| `onRowMouseEnter` | `(row, event) => void` | Called when the pointer enters a row. | -| `onRowMouseLeave` | `(row, event) => void` | Called when the pointer leaves a row. | - -### Keyboard navigation - -| Prop | Type | Default | Description | -|---|---|---|---| -| `cellNavigation` | `boolean` | `false` | Spreadsheet-style keyboard navigation using the WAI-ARIA grid pattern (`role="grid"`, single Tab stop, roving tabindex). Arrow keys move between cells, including header, selection, and expander cells; `Home`/`End` and `Ctrl+Home`/`Ctrl+End` jump to row and grid edges; `Enter`/`F2` open a cell editor; `Enter`/`Space` sort from a header. See [Keyboard navigation](/docs/keyboard-navigation). | - -### Column features - -| Prop | Type | Default | Description | -|---|---|---|---| -| `resizable` | `boolean` | `false` | Show drag handles on column headers for resizing. | -| `columnGroups` | `ColumnGroup[]` | - | Spanning group headers rendered above the column header row. | -| `filterValues` | `Record` | - | Controlled filter state. Omit to use internal state. See [Filtering](/docs/filtering). | -| `onFilterChange` | `(columnId, filter: FilterState) => void` | - | Called when the user clicks Apply or Clear in a filter popup. | -| `onColumnOrderChange` | `(columns: TableColumn[]) => void` | - | Called after a drag-to-reorder column operation with the new column order. | -| `onColumnGroupOrderChange` | `(groups: ColumnGroup[], columns: TableColumn[]) => void` | - | Called after a group drag-reorder with the new group order and the matching updated column order. | - -### Context menu - -| Prop | Type | Default | Description | -|---|---|---|---| -| `contextMenu` | `boolean \| { header?: boolean; row?: boolean; trigger?: 'right-click' \| 'menu-button' \| 'both'; menuPosition?: 'start' \| 'end' }` | - | Enable the context menu. `true` enables header and row menus with the right-click trigger. The header menu has built-in sort/pin/hide/reset actions; the row menu only opens when `contextMenuActions.row` returns items. `menuPosition` sets which inline edge the row menu button sits on (default `'end'` — right in LTR). See [Context menu](/docs/context-menu). | -| `contextMenuActions` | `{ header?: ContextMenuAction[] \| (column) => ContextMenuAction[]; row?: (row, rowIndex) => ContextMenuAction[] }` | - | Custom menu items. Header items are appended after the built-ins; row items are the entire row menu. | -| `onContextMenuAction` | `(action: ContextMenuAction, ctx: ContextMenuActionContext) => void` | - | Called for every selected item, built-ins included (after their effect is applied). `ctx` is `{ type: 'header', column }` or `{ type: 'row', row, rowIndex }`. | - -### Localization - -| Prop | Type | Default | Description | -|---|---|---|---| -| `localization` | `Localization` | - | Override every user-visible string and aria-label in the table. Pass a pre-built locale or build your own. See [Localization](/docs/localization). | -| ~~`columnFilterOptions`~~ | `ColumnFilterOptions` | - | **Deprecated.** Use `localization={{ filter: { ... } }}` instead. Will be removed in v9. | -| ~~`expandableRowsOptions`~~ | `ExpandableRowsOptions` | - | **Deprecated.** Use `localization={{ expandable: { ... } }}` instead. Will be removed in v9. | - -## ColumnGroup - -Defines a spanning group header above one or more columns. Pass an array to the `columnGroups` prop. - -```ts -import { type ColumnGroup } from 'react-data-table-component'; - -const columnGroups: ColumnGroup[] = [ - { name: 'Employee', columnIds: ['first', 'last', 'dept'] }, - { name: 'Compensation', columnIds: ['base', 'bonus'], align: 'left' }, -]; -``` - -| Field | Type | Default | Description | -|---|---|---|---| -| `name` | `ReactNode` | - | **Required.** Group header label. Accepts JSX. | -| `columnIds` | `(string \| number)[]` | - | **Required.** Ids of columns under this group. Must match each column's `id` exactly. | -| `align` | `'left' \| 'center' \| 'right'` | `'center'` | Horizontal alignment of the group label. | -| `reorder` | `boolean` | - | Allow drag-to-reorder for this group. Dragging moves all member columns as a block. Fires `onColumnGroupOrderChange`. | - -Columns not listed in any group span the full group-row height with no label. - -## TableColumn\ - -```ts -import { type TableColumn } from 'react-data-table-component'; - -const columns: TableColumn[] = [ - { - id: 'name', - name: 'Full name', - selector: row => row.name, - sortable: true, - width: '200px', - }, -]; -``` - -| Prop | Type | Description | -|---|---|---| -| `id` | `string \| number` | Stable identity used by `defaultSortFieldId`, `columnGroups`, and `filterValues`. | -| `name` | `ReactNode` | Column header label. | -| `selector` | `(row, index?) => Primitive \| ReactNode` | Data accessor. For sortable columns return a `Primitive` (`string \| number \| boolean \| bigint \| Date`). Rows whose selector returns `null` or `undefined` sort to the end. `bigint` displays as its decimal string; `Date` displays via `toLocaleString()`, which follows the viewer's locale and timezone. Use `format` when you need deterministic or SSR-stable output. | -| `cell` | `(row, index, column, id) => ReactNode` | Custom cell renderer. Overrides the `selector` display value. | -| `format` | `(row, index) => ReactNode` | Format the selector value for display without changing the sort key. | -| `sortable` | `boolean` | Enable sorting on this column. | -| `sortFunction` | `(a: T, b: T) => number` | Custom comparator for this column. | -| `sortField` | `string` | Field key passed to `onSort` for server-side sort identification. | -| `filterable` | `boolean` | Show a filter popup for this column. | -| `filterType` | `"text" \| "number" \| "date"` | Filter operator set and input widget. Defaults to `"text"`. See [Filtering](/docs/filtering). | -| `filterFunction` | `(row, filter: FilterState) => boolean` | Custom filter predicate. Overrides built-in operator logic. Receives the full `FilterState` including both conditions. | -| `width` | `string` | Fixed column width, e.g. `"120px"`. | -| `minWidth` | `string` | Minimum width. | -| `maxWidth` | `string` | Maximum width. | -| `grow` | `number` | Flex-grow factor (default `1`). Set to `0` to prevent growing. | -| `right` | `boolean` | Right-align cell content. | -| `center` | `boolean` | Center cell content. | -| `wrap` | `boolean` | Allow cell text to wrap. | -| `compact` | `boolean` | Remove cell padding. | -| `button` | `boolean` | Center content and suppress row click propagation. | -| `allowOverflow` | `boolean` | Allow cell content to overflow (useful for dropdowns and popovers). | -| `ignoreRowClick` | `boolean` | Prevent clicks in this cell from firing `onRowClicked`. | -| `hide` | `Media \| number` | Hide the column below the given breakpoint (`Media.SM` = 599px, `MD` = 959px, `LG` = 1280px) or a custom pixel value. | -| `omit` | `boolean` | Exclude the column entirely. Toggle this to show/hide a column. | -| `reorder` | `boolean` | Allow drag-to-reorder for this column (requires `reorder` on at least two columns). | -| `pinned` | `'left' \| 'right'` | Freeze the column to an edge during horizontal scroll. Only visible when the table overflows its container — give columns explicit widths. See [Column pinning](/docs/column-pinning). | -| `style` | `CSSProperties` | Inline styles applied to every cell in this column. | -| `conditionalCellStyles` | `ConditionalStyles[]` | Per-cell conditional styles. | -| `footer` | `ReactNode \| (rows: T[]) => ReactNode` | Footer cell for this column. Static node or a function receiving the filtered+sorted rows (typically used to render aggregates like sums or averages). When any visible column has a `footer`, a footer row renders below the body. See [Footer](/docs/footer). | - -### Inline editing - -| Prop | Type | Description | -|---|---|---| -| `editable` | `boolean` | Shorthand for `editor: { type: 'text' }`. Ignored when `editor` is also set. | -| `editor` | `CellEditor` | Editor configuration. See [CellEditor](#celleditor) below. Takes precedence over `editable`. | -| `validate` | `(value, row, column) => true \| false \| string` | Gate the edit before `onCellEdit` fires. Return `true` to accept, `false` to reject silently, or a string error to keep the editor open with an inline tooltip. | -| `onCellEdit` | `CellEditCallback` | Called when the user commits an edit. Receives `(row: T, value: string, column: TableColumn)`. The `value` is always a string; parse it to the target type in your handler. | - -See [Inline editing](/docs/inline-editing) for examples, CSS variables, and styling guidance. - -## CellEditor - -```ts -import { type CellEditor } from 'react-data-table-component'; - -type CellEditor = - | { type: 'text'; placeholder?: string } - | { type: 'number'; placeholder?: string; min?: number; max?: number; step?: number } - | { type: 'date'; min?: string; max?: string } - | { type: 'checkbox' } - | { - type: 'select'; - options: Array<{ value: string; label: React.ReactNode }>; - placeholder?: string; - } - | { - type: 'custom'; - render: (ctx: CustomCellEditorContext) => React.ReactNode; - }; - -interface CustomCellEditorContext { - row: T; - value: string; - setValue: (next: string) => void; - commit: (value?: string) => void; - cancel: () => void; - column: TableColumn; -} -``` - -| Variant | Field | Type | Description | -| --- | --- | --- | --- | -| All | `type` | `"text" \| "number" \| "date" \| "checkbox" \| "select" \| "custom"` | Editor widget to render. | -| `text` / `number` / `select` | `placeholder` | `string` | Placeholder text. For `select`, shown as a disabled hidden option when the current value is empty. | -| `number` | `min` / `max` / `step` | `number` | Forwarded to the native ``. | -| `date` | `min` / `max` | `string` | Forwarded to the native `` as ISO dates. | -| `select` | `options` | `{ value: string; label: ReactNode }[]` | Dropdown options. The `value` is what gets committed; `label` is displayed (must be string or number inside native `