Server-Side Pagination & Sorting with Ant Design Table
Implement server‑side pagination and sorting in Ant Design Table to handle large datasets efficiently, avoiding DOM bloat and empty‑page bugs.
15 Jul 2025, 18:12 UTC

Problem: Table UI stalls when rendering thousands of rows
Binding a large dataset directly to Ant Design’s <Table /> forces the browser to create a DOM node for every row. Even a few thousand records cause lag, unresponsive sorting, and pagination that feels broken because the component still performs client‑side slicing.
Thesis: Treat the Table as a view‑only component and drive pagination and sorting from the server
By using the controlled pagination and sorter props and handling the onChange callback, you shift data fetching to the backend. The UI only renders the fetched slice, keeping the DOM light and interactions snappy.
Section 1: Controlled pagination setup
Install Ant Design and create a Table component that receives pagination state and updates it via onChange. The example assumes a REST endpoint accepting page, pageSize, sortField and sortOrder query parameters.
import { Table, Spin } from 'antd';
import { useState, useEffect } from 'react';
function UserTable() {
const [data, setData] = useState([]);
const [loading, setLoading] = useState(true);
const [pagination, setPagination] = useState({ current: 1, pageSize: 20 });
const [sortInfo, setSortInfo] = useState({ field: '', order: '' });
const buildParams = () => {
const params = new URLSearchParams({
page: String(pagination.current),
pageSize: String(pagination.pageSize),
});
if (sortInfo.field) {
params.append('sortField', sortInfo.field);
params.append('sortOrder', sortInfo.order);
}
return params.toString();
};
useEffect(() => {
setLoading(true);
fetch(`/api/users?${buildParams()}`)
.then(res => res.json())
.then(json => {
setData(json.items);
setPagination(prev => ({ ...prev, total: json.total }));
})
.finally(() => setLoading(false));
}, [pagination.current, pagination.pageSize, sortInfo.field, sortInfo.order]);
const handleTableChange = (paginationObj, filters, sorter) => {
setPagination(paginationObj);
if (sorter.field) {
setSortInfo({ field: sorter.field, order: sorter.order });
} else {
setSortInfo({ field: '', order: '' });
}
};
return (
);
Key points:
- The
paginationprop is an object; Ant Design readscurrent,pageSizeand optionallytotalfrom it. - The
onChangehandler receives the updated pagination object and any sorter changes, giving you a single place to trigger a new data fetch. - Sorting is passed back to the Table via the
sorterprop, so the UI shows the correct sort indicator.
Section 2: Avoiding the empty page bug after filtering
When filters reduce the total result set, reset current to 1 so the Table doesn’t request a page index that no longer exists.
const [filters, setFilters] = useState({});
useEffect(() => {
setPagination(prev => ({ ...prev, current: 1 }));
}, [filters]);
Include filters in buildParams so the server applies the search criteria.
Section 3: Trade‑offs and verification
Limitation: Even with server‑side pagination, rendering hundreds of rows per page can bloat the DOM. For page sizes > 100, consider Ant Design’s virtual prop or a windowing library like react-window.
How to verify the implementation works:
- Open Chrome DevTools → Network tab.
- Interact with pagination controls (next, page jump) or click a column header to sort.
- Confirm a new request appears with expected query parameters (page, pageSize, sortField, sortOrder).
- Check the response payload matches the rows displayed.
- Ensure no duplicate or missing rows when rapidly changing pages.
If the request URL updates correctly and the Table reflects new data without a full reload, the controlled pattern is working.
Actionable closing
Start small: replace a client‑side sliced Table with the controlled version above, point it at a paginated endpoint, and watch the network tab. Once stable, add filters and remember to reset the page index when the filtered total shrinks. For very large pages, layer in virtual scroll to keep the DOM lean. This approach gives you a responsive UI while keeping data‑fetching logic in one place.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.