Server-Side Data
Integrate the React grid with server-side sorting, filtering, and windowed row loading by implementing a remote data source that streams chunks on-demand efficiently.
For very large datasets or when data must remain on the server, use server-side data operations.
When to Use Server-Side
- Dataset too large to load into browser memory
- Data requires real-time updates from server
- Complex filtering/sorting logic on server
- Security requirements to keep data server-side
Basic Setup
import { Grid, createServerDataSource } from "@gp-grid/react";
const dataSource = createServerDataSource(async (request) => {
const response = await fetch("/api/grid-data", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(request),
});
return response.json();
});
function ServerGrid() {
return (
<Grid
columns={columns}
dataSource={dataSource}
rowHeight={36}
/>
);
}Request Format
The server receives a request describing the absolute row range, plus sort/filter state. endRow is exclusive, so endRow - startRow is the number of rows to return.
interface DataSourceRequest {
range: {
startRow: number; // First row index to fetch (0-indexed, inclusive)
endRow: number; // First row index after the range (exclusive)
};
sort?: SortModel[]; // Sort configuration
filter?: FilterModel; // Filter configuration
}
// Example request — the grid asks for rows 0..99 sorted by name asc
{
range: { startRow: 0, endRow: 100 },
sort: [{ colId: "name", direction: "asc" }],
filter: {
salary: {
conditions: [{ type: "number", operator: ">", value: 50000 }],
combination: "and"
}
}
}Response Format
Return rows and total count:
interface DataSourceResponse<TData> {
rows: TData[]; // Current page of data
totalRows: number; // Total rows matching filters
}
// Example response
{
rows: [
{ id: 1, name: "Giovanni", salary: 75000 },
{ id: 2, name: "Luca", salary: 82000 },
// ...
],
totalRows: 10000
}Express.js Example
// server.ts
import express from "express";
app.post("/api/grid-data", async (req, res) => {
const { range, sort, filter } = req.body;
// Build database query
let query = db.select().from(employees);
// Apply filters
if (filter) {
for (const [field, model] of Object.entries(filter)) {
for (const condition of model.conditions) {
if (condition.type === "number") {
switch (condition.operator) {
case ">":
query = query.where(field, ">", condition.value);
break;
case "<":
query = query.where(field, "<", condition.value);
break;
// ... other operators
}
}
// ... handle other types
}
}
}
// Apply sorting
if (sort?.length) {
for (const s of sort) {
query = query.orderBy(s.colId, s.direction);
}
}
// Get total count
const totalRows = await query.clone().count();
// Apply range (endRow is exclusive)
const { startRow, endRow } = range;
query = query.offset(startRow).limit(endRow - startRow);
const rows = await query;
res.json({ rows, totalRows });
});Handling Sort Models
// SortModel structure
type SortModel = {
colId: string;
direction: "asc" | "desc";
};
// Multiple columns (for multi-sort)
[
{ colId: "department", direction: "asc" },
{ colId: "salary", direction: "desc" }
]Handling Filter Models
// FilterModel structure
type FilterModel = Record<string, ColumnFilterModel>;
// ColumnFilterModel
{
conditions: FilterCondition[];
combination: "and" | "or";
}
// Example: salary > 50000 AND salary < 100000
{
salary: {
conditions: [
{ type: "number", operator: ">", value: 50000 },
{ type: "number", operator: "<", value: 100000 }
],
combination: "and"
}
}Error Handling
const dataSource = createServerDataSource(async (request) => {
try {
const response = await fetch("/api/grid-data", {
method: "POST",
body: JSON.stringify(request),
});
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
return response.json();
} catch (error) {
console.error("Failed to fetch grid data:", error);
// Return empty result on error
return { rows: [], totalRows: 0 };
}
});Caching Considerations
For better UX, consider caching pages:
const cache = new Map<string, DataSourceResponse>();
const dataSource = createServerDataSource(async (request) => {
const cacheKey = JSON.stringify(request);
if (cache.has(cacheKey)) {
return cache.get(cacheKey)!;
}
const result = await fetchFromServer(request);
cache.set(cacheKey, result);
return result;
});Clear cache when data changes on the server.
Styling
Customize the appearance of the React grid with CSS variables, theme tokens, and class overrides to match your application's design system seamlessly.
FAQ
Browse frequently asked questions about using gp-grid with React, covering common pitfalls, integration tips, and answers gathered from the community.