Diagnosing MUI Data Grid Server‑Side Pagination Stale Data Issues
Step‑by‑step guide to diagnose why MUI Data Grid’s server‑side pagination shows stale rows or a permanent spinner, with checks, fixes, and escalation paths.
26 Jul 2026, 06:07 UTC

Recognizable condition
When using @mui/x-data-grid in server‑side mode (paginationMode='server'), the grid may continue to show rows from a previous page or keep a loading spinner after a successful API call. The network tab shows a valid response, yet the UI does not update.
Short cause/diagnostic table
| Symptom | Likely cause |
|---|---|
| Stale rows or empty grid despite new data | Missing or incorrect totalCount in server response |
| Loading spinner never disappears | rowCount returned does not match the actual number of rows |
| Page size seems ignored | Grid pageSize prop differs from the limit/offset sent to the API |
| Sorting/filtering resets to defaults | Sort/model or filter/model not forwarded in getRows params |
Ordered checks
-
Inspect the network request/response – Open Chrome DevTools → Network, filter XHR, locate the data‑grid request. Verify:
- Query parameters include
limit(orpageSize) andoffset(orpage). - Response JSON contains both
rows(array) andtotalCount(integer).
- Query parameters include
-
Log the
getRowsreturn value – Add a temporary console log inside your data source:
Check thatconst getRows = async (params) => { const { rows, totalCount } = await fetchData(params); console.log('getRows returning:', { rowsLength: rows.length, totalCount }); return { rows, rowCount: totalCount }; };rowsLengthmatches therowCountyou logged. Where to run: In your React component file; requires dev build. Risk: Only adds logging; remove before production. -
Temporarily switch to client‑side pagination – Set
paginationMode='client'and pass a static array (e.g., the first page of data) to therowsprop. If the UI updates correctly, the issue lies in server‑side handling. Where to run: Same component; no extra permissions. Risk: Changes UI behavior; revert after test. -
Disable sorting/filtering – Remove
sortModelandfilterModelprops or set them to empty arrays. Observe whether pagination works. If it does, the problem is likely missing propagation of those models to the server. Where to run: In the grid props; no special permissions.
Fixes tied to findings
- Missing
totalCount– Adjust the backend to include a field named exactlytotalCount(or map it ingetRows). Example adjustment:
// Express.js example
app.get('/api/rows', (req, res) => {
const limit = parseInt(req.query.limit) || 100;
const offset = parseInt(req.query.offset) || 0;
db.query('SELECT COUNT(*) AS total FROM items', (err, countRes) => {
if (err) return res.status(500).send(err);
const total = countRes[0].total;
db.query('SELECT * FROM items LIMIT ? OFFSET ?', [limit, offset], (err, rows) => {
if (err) return res.status(500).send(err);
res.json({ rows, totalCount: total }); // <-- must match Grid expectation
});
});
});
- Incorrect
rowCount– Ensure the value you return equals the total number of records, not the length of the current page. - Page‑size mismatch – Align the Grid prop with the API limit:
If you use a different name for the limit (e.g.,<DataGrid paginationMode="server" pageSize={100} // must equal the limit sent in the request getRows={myGetRows} />pageSizeon the server), map it insidegetRows. - Sort/filter models not forwarded – Read the
paramsobject and pass itssortModelandfilterModelto the API:const getRows = async (params) => { const { sortModel, filterModel, pagination } = params; const query = { limit: pagination.pageSize, offset: pagination.page * pagination.pageSize }; if (sortModel.length) query.sort = sortModel.map(s => `${s.field} ${s.sort}`); if (filterModel.length) query.filter = filterModel.map(f => ({ [f.field]: f.value })); const resp = await fetch('/api/rows', { method: 'POST', body: JSON.stringify(query) }); return resp.json(); };
After applying a fix, force a refresh if needed:
// Using the grid's API reference
const gridRef = useRef(null);
// … after data change
gridRef.current?.apiRef.current.forceUpdate();
Escalation criteria
If you have verified:
- The API returns correct
rowsandtotalCount. - The
getRowsfunction returns an object shaped as{ rows: RowModel[], rowCount: number }. - Grid props
paginationMode='server'andpageSizematch the request limit. - Sorting/filtering models are properly forwarded.
and the UI still shows stale data, consider:
- Upgrading to the latest
@mui/x-data-grid(v6+ at time of writing) – known pagination bugs were fixed in recent releases. - Reviewing the MUI X GitHub issue tracker for similar reports.
- Creating a minimal reproducible example using the official template and filing a bug with the MUI issue template.
Practical verification
After a fix, open React DevTools, select the DataGrid component, and watch its rows and rowCount props change as you navigate pages. Then:
- Go to page 2, then back to page 1.
- Confirm the displayed rows match the freshly fetched data for page 1 (no leftover rows from page 2).
- Ensure the loading spinner disappears once the API resolves.
If the props update but the UI does not, check for accidental mutation of the rows array elsewhere in your code; treat data as immutable to let the grid’s change detection work.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.