Troubleshooting
Use this page when the table is already integrated but something feels wrong. Start with the symptom that is closest to what you see.
First debugging step
Turn on debug logging while reproducing the issue.
<Datatable tableKey="customers" debug />That helps you distinguish a rendering problem from a state-loading, persistence, or online-query problem.
The table renders unstyled or popovers look broken
Likely cause
Tailwind is not scanning the copied table source, or the required CSS variables are missing.
Check
@import "tailwindcss";
@source ".";
@source "./src/react-datatable";Also make sure your theme exposes the variables used by the shipped UI, including:
--background--foreground--border--input--ring--popover--muted--primary
Usually fixes it
- add the copied datatable source to Tailwind scanning
- copy or map the expected design-token variables
- verify your local wrapper did not drop the table stylesheet imports
Online mode does not load data
Likely cause
The online.query function is throwing, returning the wrong response shape, or receiving unsupported filter/grouping input.
Check
<Datatable
tableKey="customers"
online={{
mode: "pagination",
queryKey: ["customers"],
query: async (input) => {
console.log(input)
return fetchRows(input)
},
}}
/>Usually fixes it
- log the query input and confirm the backend accepts the filter, sort, grouping, and pagination fields
- return
rows,totalDataRows,totalRenderedRows, andhasMore - set
supportedGroupingColumnswhen only some columns can be grouped server-side
Local mode, saved views, URL sync, and copy links do not require a URL-state provider.
Selection, preview, or saved views point at the wrong rows or columns
Likely cause
Row IDs or column IDs are unstable.
Check
<Datatable
tableKey="customers"
data={rows}
columns={columns}
getRowId={(row) => row.id}
/>Usually fixes it
- use a real row identifier, not an array index
- keep column
idvalues stable across renders and releases - do not derive IDs from labels that product text may change later
Identity drift is one of the fastest ways to break selection, preview, persistence, grouping, and online grouping summaries all at once.
Current-view persistence does not restore what you expected
Likely cause
The wrong persistence scope is loading, or a higher-priority source is overriding it.
Check
persistState.adapteris configuredtableKeyis stableworkspaceIdanduserIdmatch the intended scope- storage is available in the current browser context
- URL state or a saved view is not overriding the persisted state during initialization
The precedence
The coordinated startup model loads state in layers. In practice, persisted state can still be overridden by later sources like selected views or accepted URL parameters.
Usually fixes it
- confirm the exact table scope keys
- clear stale persisted state when changing table identity
- check whether a saved default view is winning
- check whether
acceptUrlParamsis applying a one-time shared state on load
A copied link works once, then the URL clears
Likely cause
This is expected shareable-link behavior, not a bug.
When urlSync uses enabled: false with acceptUrlParams: true, the table reads the datatable params once and then removes them from the address bar.
Usually fixes confusion
- use this mode for one-time shared links
- use continuous
urlSyncif the URL should remain a live product surface - document the difference for support and product teams
URL sync is writing too many history entries or back/forward feels wrong
Likely cause
historyMode does not match the page behavior you want.
Check
historyMode: "push"creates navigation entries as state changeshistoryMode: "replace"keeps the URL current without building a long browser history trail
Usually fixes it
- use
replacefor dashboards where URL state should stay current quietly - use
pushwhen back/forward through table states is part of the product experience
Shared links are too long to be practical
Likely cause
The copied link contains a large table state directly in the URL.
Usually fixes it
- save the state as a named view instead of a URL
- reduce large filter payloads or overly wide shared state
- prefer current-view persistence or saved views for heavy internal workflows
Online rows, totals, or grouping counts look wrong
Likely cause
The backend response does not match the online contract.
Check
totalDataRowscounts matching data recordstotalRenderedRowsincludes structural rows like group headersgroupingsummary matches the same backend scope asrowsfacetsare computed from the same filtered dataset as the returned rows
Usually fixes it
- keep tenant, permission, search, filters, grouping, and sorting in one backend-owned query scope
- do not compute facets from a broader or narrower dataset than the rows
- keep grouped infinite mode explicit about data-row offsets versus rendered-row counts
If route-prefetched initialData appears for the wrong query, re-check initialDataQueryState.
Online grouping selections or persisted grouping values disappear
Likely cause
The current grouping state includes columns your backend does not support.
Check
Set supportedGroupingColumns in the online config when only some groupings are allowed.
Usually fixes it
- declare the supported grouping columns explicitly
- let the table sanitize persisted or URL grouping state before it reaches the backend
- keep your server and docs aligned on which grouped views are actually supported
Filters show the wrong options or filter chips do not match backend truth
Likely cause
Facet keys, filter types, or product-specific filter UI assumptions drifted away from the actual column contract.
Check
- facet keys match column IDs exactly
- column
filterTypematches the UI you expect - select-style filters receive option domains that match the actual backend data
Usually fixes it
- align facet response keys with column IDs
- keep
text,text-list,number, and date filters on the documented UI path - treat boolean, ID-list, and custom filters as product-owned extension surfaces unless you built the matching UI yourself
Infinite scrolling feels jumpy or loads the wrong rows
Likely cause
Backend ordering is unstable, or pagination concepts are leaking into infinite mode.
Check
- the backend ordering is deterministic
- tie-breakers exist when two rows sort equally
offsetis treated as a data-row offsetprefetchRowsis sized for how quickly users move through the list
Usually fixes it
- make the backend ordering stable for the full active query
- reset to the start of the result set when filters, search, sorting, or grouping changes
- keep virtualization concerns separate from online query semantics
Virtualization shows misaligned rows, sticky-column glitches, or rough scrolling
Likely cause
Rendered row heights are not predictable, or too much detail is being forced into the grid.
Check
<Datatable
tableKey="customers"
rowHeight={48}
headerHeight={48}
virtualization={{ mode: "viewport" }}
/>Usually fixes it
- keep row and header heights predictable
- move long or multi-line detail into preview panels or detail routes
- tune overscan before changing deeper rendering assumptions
- keep sticky-column counts modest enough that horizontal scroll remains usable
Grouping shows unexpected empty groups
Likely cause
showEmptyGroups is enabled and the grouped column exposes a finite option domain.
Usually fixes it
- turn off
showEmptyGroupsif those zero-count buckets are not useful - keep it on only when users genuinely need to see missing categories
- remember that online mode needs backend support for the same behavior
After copying source updates, the table regressed
Likely cause
A source refresh overwrote local adapters, theme mapping, or product-specific integrations.
Usually fixes it
Before shipping a copied update:
- compare your local edits against the copied source diff
- preserve adapter wiring, theme tokens, and product-specific wrappers
- re-test persistence, views, URL sync, online mode, and bulk actions together
- run the repository checks, not just a visual smoke test