Svelte Server-Side Table
When the dataset is too large to send, the grid stops owning the rows and starts asking for them. You implement one getRows function that receives the current sort, filters and row range and returns the rows plus a total count; the grid handles caching, request cancellation, race safety and the loading state.
The same contract covers offset paging, keyset or cursor paging and infinite scroll, so moving from one to another is a change of options rather than a rewrite. Lazy grouping per level, tree data, pivot, transactions and select-all across rows the grid never loaded run on the same contract through the Enterprise server-side row model.
Turn it on: createServerDataSource(source, { pageSize: 50 })
Install
npm i @svgrid/gridFree and MIT-licensed in @svgrid/grid: no license key, no row cap, no watermark.
The code
import { createServerDataSource, type ServerDataSource } from '@svgrid/grid'
const source: ServerDataSource<Row> = {
async getRows({ startRow, endRow, sortModel, filterModel }) {
const res = await fetch('/api/rows', {
method: 'POST',
body: JSON.stringify({ startRow, endRow, sortModel, filterModel }),
})
const { rows, total } = await res.json()
return { rows, rowCount: total } // rowCount = total AFTER filtering
},
}
const ctl = createServerDataSource(source, { pageSize: 50, onChange: (s) => (view = s) })
ctl.refresh()What you get
- One getRows contract - A single function receives the row range, the sort model and the filter model, and returns rows plus the filtered total. Everything else is handled.
- Debounce and cancellation - Typing in a filter does not fire a request per keystroke, and a superseded request is cancelled rather than racing the one after it.
- Keyset paging - Cursor-based paging for tables where OFFSET gets slow, with the same grid props as offset paging.
- Infinite scroll - Fetch the next block as the user scrolls, keeping a sliding window of rows in memory instead of the whole result.
- Server-side grouping - Group nodes expand one level at a time through the same contract, so the grid holds only the groups you opened. Lazy grouping, tree data, pivot, transactions and select-all across unloaded rows are the Enterprise server-side row model; the hand-wired pattern stays free.
- Free and MIT - The datasource contract, the paged and block-cached infinite controller and cursor paging are all in @svgrid/grid, MIT.
Live examples
- Server-Side Row Model: paged and infinite - The free half of the row model: implement one async getRows({ startRow, endRow, sortModel, filterModel }) and createServerDataSource owns the sort/filter/page lifecycle and races stale responses away. Paged, the grid holds one 50-row page; infinite, a block cache under the scrollbar with placeholders, retry and an LRU, drawn live as you scroll. Here a 100,000-row in-memory server behind 250ms latency. Grouping, tree, pivot and transactions on top of the same contract are the Enterprise row model (demo 467).
- Server-side data - Sort/filter/page round-tripped to a mock endpoint with debounce + cancel.
- Server-side infinite scroll - 100k-event audit log behind a mock API. Sparse chunked load on scroll; sort + filter + search pushed to the server.
- Cursor (keyset) pagination - Modern alternative to offset paging: prev / next cursor tokens, stable under writes, O(log N) deep pages.
- Server-side grouping + aggregates - GROUP BY + SUM/AVG pushed to the server; pre-aggregated buckets with on-demand drill-in.
- Loading from REST - Fetches rows from a public REST API with loading skeleton, retry, error surface, and a Reload button.
Documentation
- Server-side data - The patterns for moving sort, filter, group, and pagination off the client and onto your API. Three flavours, ranked by complexity:
- Server-Side Row Model (SSRM) - When the data lives on the server - millions of rows in a database - the grid should hold only the page on screen and push sorting, filtering, and paging to…
- Server-side infinite scroll - One long list, a million rows on the server, and a scrollbar that covers all of them: the grid asks for the block under the viewport, shows placeholder rows…
- Server grouping - Enterprise - Grouping a hundred thousand rows in the browser means shipping all hundred thousand rows first. Server grouping flips that: the backend runs the GROUP BY,…
- Server-side data (load on demand) - When the rows live behind an API and there are too many to ship to the browser, the server does the work: sorting, filtering, and paging become query…
Related articles
- Server-Side Data - Pagination, Sorting, and Filtering on the Backend - Keep 100,000+ rows on the server. SvGrid owns the UI state for sort, filter, and pagination controls - your API owns the data.
- Client-Side vs Server-Side Data for Tables - The architectural fork that determines your grid's performance ceiling - how to pick the right data mode and wire it correctly in SvGrid.
- Inside SvGrid: Server-Side Data and the Headless Core - How SvGrid separates UI state from row processing, and what that means for building grids driven entirely by a backend.
Frequently asked questions
How do I do server-side pagination in a Svelte table?
Create a ServerDataSource with a getRows function that receives startRow, endRow, sortModel and filterModel, and return the rows with the total count after filtering. Pass the controller's state to the grid.
What does the grid send to my endpoint?
The requested row range plus the current sort model and filter model. You decide how to turn those into SQL or an API call; the grid never assumes a backend.
Does it support infinite scroll?
Yes. Turn pagination off and the grid requests the next block as the user scrolls, keeping a window of rows rather than the entire result set.
Is server-side data free?
The contract, paged and infinite loading and cursor paging are MIT-licensed in @svgrid/grid. Lazy server-side grouping, tree data, pivot, transactions and select-all across rows the grid never loaded are the server-side row model in @svgrid/enterprise.