Deciding when DataTables should process on the server
Move paging, sorting, and searching to the backend for large tables, but expect a network round trip on every interaction.
15 Aug 2025, 11:41 UTC

A table that freezes the browser
You load a page with an HTML table containing 50,000 rows. The browser parses every row into the DOM, DataTables sorts them in JavaScript memory, and clicking the second page feels instant—until the next search locks the main thread for several seconds. At this scale, client-side processing stops being convenient and starts being a performance problem.
The practical takeaway is simple: if your table holds thousands to millions of rows, move paging, filtering, and sorting to the server. If it holds a few hundred rows, keep it in the browser. Server-side processing keeps DOM size low, but every page change, sort, or search requires an HTTP round trip.
What the browser and server exchange
DataTables is a jQuery plugin for enhancing HTML tables. Enabling server-side processing means setting serverSide: true and providing an ajax URL. The client sends standard parameters such as start, length, search[value], order[i][column], and order[i][dir]. The server must reply with JSON containing draw, recordsTotal, recordsFiltered, and data.
This request/response contract is stable across DataTables 1.10.x and 2.x. Older 1.9.x versions use different parameter names, so check your runtime version before adapting examples. You can read the version in the browser console with DataTable.version or $.fn.dataTable.version, depending on the major release.
Worked configuration example
A minimal initialization might look like this, placed in your page’s JavaScript:
$(document).ready(function() {
$('#example').DataTable({
serverSide: true,
ajax: '/api/users',
columns: [
{ data: 'id' },
{ data: 'name' },
{ data: 'email' },
{ data: 'role' }
]
});
});
Run this in your application frontend after including jQuery and DataTables. No special server permissions are required beyond the ability to edit the page’s script. When a user changes pages, the browser sends a request similar to:
GET /api/users?draw=2&start=10&length=10&search%5Bvalue%5D=admin&order%5B0%5D%5Bcolumn%5D=1&order%5B0%5D%5Bdir%5D=asc
The server should respond with JSON shaped like this:
{
"draw": 2,
"recordsTotal": 50000,
"recordsFiltered": 1240,
"data": [
[10, "Ada Lovelace", "[contact removed]", "admin"],
[11, "Grace Hopper", "[contact removed]", "admin"]
]
}
recordsTotal is the count of all rows in the table, while recordsFiltered is the count after applying the current search filter. If you confuse these two, the pager shows the wrong number of pages.
Trade-offs and limitations
- Every interaction triggers a network request. On slow connections, the table feels unresponsive until the AJAX response arrives.
- Some client-only rendering features behave differently or are unavailable in server-side mode.
- You must parameterize every user-controlled sort or filter value. Never interpolate request columns directly into SQL, or you expose the application to injection.
Server-side processing is a good fit for tables with thousands to millions of rows. For small datasets, client-side processing (serverSide: false) is simpler and faster after the initial load because it avoids repeated HTTP requests, but it requires all rows to live in JavaScript memory.
Verify the backend logic
Open browser DevTools, go to the Network tab, trigger a page change or search, and confirm the request includes start, length, and search[value]. Confirm the response includes draw, recordsTotal, recordsFiltered, and data.
Run a sample search, then compare recordsTotal to SELECT COUNT(*) from your source table and recordsFiltered to the count of your filtered query. Throttle the network in DevTools and observe whether the table shows a loading state and recovers after the response.
Closing recommendation
Start with client-side processing. Profile the page when the row count grows beyond a few hundred, and switch to serverSide: true only when sorting, paging, or searching begins to block the browser. Validate the response contract in DevTools before deploying, and keep the backend parameterized to avoid injection risks.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.