Web

MUI DataGrid Server-Side Row Grouping Pagination

Learn how to handle pagination for group items in MUI DataGrid with server-side row grouping. Implement lazy loading, backend queries, and separate pagination controls to avoid performance issues with large groups.

1 answer 2 views

MUI DataGrid server-side row grouping with pagination: how to handle paging for group items?

When using MUI DataGrid with server-side row grouping and pagination, group headings respect the page size. However, all items within the group are displayed, ignoring pagination. This causes performance issues on the backend and frontend rendering when groups have thousands of items.

What is the best way to implement pagination for sub-items within groups? Would they need a separate pagination control?

Example (forked from MUI X ServerSideRowGroupingFullDataGrid demo with pagination): StackBlitz demo

Handling pagination for group items in MUI DataGrid with server-side row grouping requires the Pro version, where you set paginationMode="server" and use rowGroupingModel to fetch only paginated children from your backend on group expansion. Without this, every sub-item loads at once, tanking performance for large groups—think thousands of rows choking your frontend. The best fix? Implement lazy loading via custom API calls for sub-items, paired with a separate pagination control like a nested paginator that triggers backend queries filtered by group ID and page offset.


Contents


The Pagination Problem in MUI DataGrid Row Grouping

Ever expanded a group in your MUI DataGrid expecting just a page of sub-items, only to watch the browser grind to a halt? That’s the core issue with server-side row grouping and pagination. Group headers paginate nicely—your pageSize limits top-level rows—but once expanded, all children dump into the grid. No built-in cutoff. For datasets where groups hold thousands of items, this means massive API payloads, slow renders, and backend timeouts.

Your StackBlitz demo nails it: pagination works at the group level, but sub-items ignore it entirely. Frontend virtualization helps a bit, but you’re still fetching everything upfront. Backend strain? Forget it. Sources like this Medium post on Data Grid features highlight how Pro’s server-side grouping prevents this overload by processing pagination per group on the server.

Why does this happen? Client-side mode loads full datasets; server-side shifts sorting, filtering, and paging to your API. But standard grouping APIs assume complete children loads. Time to level up.


Why MUI X Data Grid Pro is Essential

Free MUI DataGrid? Great for basics like sorting and simple pagination. But row grouping with server-side smarts? That’s Pro territory. As DEV.to’s 2025 React grids roundup points out, Pro unlocks virtualization for partial group loads, plus getRowChildren for on-demand fetching.

Install it: npm install @mui/x-data-grid-pro. Then wrap your grid:

tsx
import { DataGridPro } from '@mui/x-data-grid-pro';

<DataGridPro
 paginationMode="server"
 rowGroupingModel={rowGroupingModel}
 experimentalFeatures={{ rowGrouping: true }}
 // more props...
/>

Without Pro, you’re stuck simulating groups with tree data—which still loads everything. Pro integrates seamlessly with server APIs for paginated children. PureCode’s MUI guide stresses this for complex projects; free tier chokes on real-world scale.

Quick check: Got a license? Production needs it anyway. Sandbox away in StackBlitz for proofs-of-concept.


Basic Server-Side Row Grouping Setup

Start simple. Configure your DataGridPro for server ops:

tsx
const [paginationModel, setPaginationModel] = useState({
 page: 0,
 pageSize: 25,
});

<DataGridPro
 rows={rows}
 columns={columns}
 paginationMode="server"
 paginationModel={paginationModel}
 onPaginationModelChange={setPaginationModel}
 rowGroupingModel={['category']} // e.g., group by 'category'
 groupingMode="server" // Pro magic
 getGroupedRowId={(params) => params.id}
/>

Your backend endpoint—like /api/data?page=0&pageSize=25&groupBy=category—returns grouped rows with aggregate info (counts, sums). Expanded groups? Initially, just placeholders or first-page children.

Reddit threads on MUI X pagination confirm: dataSource prop auto-switches modes to server. But for groups, extend with getRowChildrenExpansion callbacks. Frontend signals expansion; server filters by groupId.

Test it. Groups paginate. Expansion loads some kids. But not paginated ones yet. That’s next.


Paginating Group Sub-Items on the Backend

Here’s the game-changer: Treat group children as a nested query. When a user expands “Category A”, fire an API call like /api/group-children?groupId=catA&page=0&pageSize=50.

Backend logic (Node.js/NestJS example from this Medium perf guide):

  1. Main endpoint: Paginate groups with aggregates.
  2. Children endpoint: WHERE category = ? LIMIT ? OFFSET ?.

Hook it frontend-side with Pro’s getRowChildren:

tsx
const getRowChildren = async (params) => {
 const { groupId } = params;
 const response = await fetch(`/api/group-children?groupId=${groupId}&page=0&pageSize=50`);
 return response.json().children;
};

<DataGridPro getRowChildren={getRowChildren} />

No more full loads. freeCodeCamp’s integration tutorial echoes this: Server dictates slice sizes. Thousands of items? Paginate 'em 50 at a time. Smooth.

But users need controls. Enter custom paginators.


Building Separate Pagination Controls

Yes, you’ll want separate pagination for group items. MUI doesn’t bundle nested paginators out-of-the-box—groups share the main one. Solution? Custom row renderers or modals with sub-grids.

Approach 1: Expandable rows with internal <Pagination>.

tsx
// Custom row for groups
const GroupRow = ({ row }) => (
 <div>
 <div onClick={() => toggleExpansion(row.id)}>
 {row.groupLabel} ({row.childrenCount})
 </div>
 {isExpanded && (
 <>
 <DataGrid rows={subRows} paginationModel={subPagination} />
 <Pagination count={Math.ceil(row.childrenCount / 50)} onChange={handleSubPage} />
 </>
 )}
 </div>
);

PureCode’s pagination deep-dive recommends this for nested data: Filter backend by groupId, apply offset/limit. Expansion fetches page 1; clicks advance it.

Approach 2: Modal on row click. Scales better for huge groups. Sencha’s grid comparison notes MUI shines here with custom UIs.

Pro tip: Sync states with rowGroupingModel changes. Users expect intuitive UX—no hunting for sub-pages.


Performance Optimization and Best Practices

Large groups? Debounce expansions. Cache fetched pages (React Query or SWR). Virtualize deeply with Pro’s built-ins.

Backend must-haves from TechBloat’s table libs review:

  • Indexes on group columns.
  • Aggregate queries for headers (e.g., COUNT(*) OVER (PARTITION BY category)).
  • Streaming responses for 10k+ rows.

Edge cases: Sorting within groups? Pass sortModel to child endpoints. Filtering? Same. Syncfusion’s 2025 grids post praises hierarchical pagination—mimic it.

Monitor with React DevTools. Aim for <100ms renders. Done right, MUI DataGrid handles enterprise scale effortlessly.


Sources

  1. Data Grid in Material-UI
  2. Using MUI Pagination for Easy, Readable Data Grid
  3. Best Free React DataGrids to Use in 2025
  4. How to paginate and sort properly with MUI X DataGrid
  5. How to Integrate the Material UI Data Grid in React
  6. How to Use MUI DataGrid Well
  7. 8 Best React Libraries to Create Awesome Tables
  8. ReExt vs. AG Grid vs. MUI: Best React Grid Library
  9. 5 Best React Data Grid Libraries Every Developer Should Know in 2025
  10. Enhancing Data Grid Performance with Server-Side Filtering

Conclusion

Mastering server-side row grouping pagination in MUI DataGrid boils down to Pro features, smart backend slicing by group ID, and yes—separate controls for sub-items to keep things performant. Fork that StackBlitz, wire in getRowChildren, add a nested paginator, and watch large datasets fly. You’ll dodge render hangs and API meltdowns, delivering snappy UX even for massive groups. Dive in; it’s worth the Pro upgrade.

Authors
Verified by moderation
Moderation
MUI DataGrid Server-Side Row Grouping Pagination