Handling Large Datasets in Ant Design: Implementing Server-Side Table Logic
Stop overloading your browser with massive arrays. Learn how to implement server-side pagination, sorting, and filtering in Ant Design's Table component for high-performance data grids.
16 Oct 2025, 07:16 UTC

The Client-Side Bottleneck
Many developers start with Ant Design's Table component by passing a full array of data to the dataSource prop. While this works for a few hundred rows, it quickly becomes a performance liability. Loading thousands of records into the browser causes memory spikes, slows down initial page loads, and makes the UI unresponsive during sorting or filtering operations.
The solution is to shift the heavy lifting—sorting, filtering, and pagination—to the backend. Instead of the Table component managing the data slice, it becomes a "controlled" interface that tells your API exactly which subset of data to return.
Connecting the Table to Your API
To implement server-side logic, you must move away from the Table's internal state management. The key is the onChange prop. This callback triggers whenever a user changes the page, clicks a sort header, or applies a filter. It provides four critical arguments: pagination, filters, sorter, and extra.
When onChange is triggered, you should update your local state (e.g., using useState) and trigger a new API fetch. To prevent the UI from feeling "frozen" during this transition, use the loading prop to show a spinner over the table body.
Implementing Sorting and Filtering
For server-side sorting to work, the sorter property in your column definition should be set to true. This tells Ant Design not to attempt a client-side sort and instead pass the sort parameters to the onChange handler.
Filters work similarly. By defining a filters array in the column configuration, the Table provides a UI for selection. When a user selects a value, the filters object in onChange will contain the column key and the selected values, which you can then map to query parameters for your backend request.
Practical Implementation Example
Below is a pattern for managing a server-side Table using React hooks. This example assumes a backend that accepts page, pageSize, sortField, and sortOrder.
import React, { useState, useEffect } from 'react';
import { Table } from 'antd';
import axios from 'axios';
const UserTable = () => {
const [data, setData] = useState([]);
const [loading, setLoading] = useState(false);
const [total, setTotal] = useState(0);
const [params, setParams] = useState({
page: 1,
pageSize: 10,
sortField: null,
sortOrder: null,
filters: {},
});
const fetchData = async (currentParams) => {
setLoading(true);
try {
// Map Ant Design params to API query strings
const response = await axios.get('/api/users', {
params: {
page: currentParams.page,
limit: currentParams.pageSize,
sortBy: currentParams.sortField,
order: currentParams.sortOrder,
...currentParams.filters,
},
});
setData(response.data.items);
setTotal(response.data.total);
} catch (error) {
console.error('Fetch failed:', error);
} finally {
setLoading(false);
}
};
useEffect(() => {
fetchData(params);
}, [params]);
const handleTableChange = (pagination, filters, sorter) => {
setParams({
page: pagination.current,
pageSize: pagination.pageSize,
sortField: sorter.field,
sortOrder: sorter.order,
filters,
});
};
const columns = [
{ title: 'Name', dataIndex: 'name', key: 'name', sorter: true },
{
title: 'Role',
dataIndex: 'role',
key: 'role',
filters: [
{ text: 'Admin', value: 'admin' },
{ text: 'User', value: 'user' },
],
filterMultiple: true
},
];
return (
);
};
Trade-offs and Performance Risks
While server-side processing solves memory issues, it introduces new challenges:
- Network Latency: Every page change or sort now requires a round-trip to the server. To mitigate this, ensure your backend uses indexed columns for sorting and filtering.
- Race Conditions: If a user clicks the "Next Page" button rapidly, multiple requests are sent. If the second request returns before the first, the UI may display the wrong page of data. To fix this, use an
AbortControllerto cancel previous requests or a tracking variable to ignore stale promises. - State Sync: If you use row selection across pages, remember that Ant Design's default selection state is tied to the current
dataSource. You must manage selected keys in a separate state array to persist selections when the user navigates to another page.
Verification Checklist
To ensure your implementation is robust, perform these checks:
- Network Inspection: Open the Browser Network tab. Verify that clicking a column header triggers a request with the correct
sortByandorderparameters. - Empty States: Test a filter combination that returns zero results to ensure the Table displays the "No Data" state rather than crashing.
- Loading State: Throttling your network to "Slow 3G" in DevTools to confirm the
loadingprop provides visual feedback during the fetch.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.