Skip to main content

Server-Side Data

Pass a dataSource instead of a data array to delegate sorting, filtering, and pagination to your server.

Live Demo

Simulated 300ms server latency — watch the loading state
Live
Name
Age
Department
Salary
Loading…
Try it in your framework

The demo above uses Radix UI for styling. To see this feature with your framework's design system (Fluent UI, Material UI, Vuetify, PrimeNG, etc.), click "Open in online demo" below the demo.

Quick Example

import { OGrid } from '@alaarab/ogrid-react-radix';
import type { IDataSource, IFetchParams, IPageResult } from '@alaarab/ogrid-react-radix';

interface Employee {
id: number;
name: string;
department: string;
salary: number;
}

const dataSource: IDataSource<Employee> = {
async fetchPage(params: IFetchParams): Promise<IPageResult<Employee>> {
const query = new URLSearchParams({
page: String(params.page),
pageSize: String(params.pageSize),
...(params.sort && {
sortField: params.sort.field,
sortDir: params.sort.direction,
}),
});
const res = await fetch(`/api/employees?${query}`);
return res.json(); // { items: Employee[], totalCount: number }
},
};

function App() {
return (
<OGrid
columns={columns}
dataSource={dataSource}
getRowId={(e) => e.id}
defaultPageSize={25}
/>
);
}
Switching UI libraries

The OGrid component has the same props across all React UI packages. To switch, just change the import:

  • Radix (lightweight, default): from '@alaarab/ogrid-react-radix'
  • Fluent UI (Microsoft 365 / SPFx): from '@alaarab/ogrid-react-fluent' — wrap in <FluentProvider>
  • Material UI (MUI v7): from '@alaarab/ogrid-react-material' — wrap in <ThemeProvider>

The grid fetches the first page on mount and re-fetches whenever page, sort, or filters change.

IDataSource<T> Reference

interface IDataSource<T> {
fetchPage(params: IFetchParams): Promise<IPageResult<T>>;
fetchFilterOptions?(field: string): Promise<string[]>;
searchPeople?(query: string): Promise<UserLike[]>;
getUserByEmail?(email: string): Promise<UserLike | undefined>;
}
MethodRequiredDescription
fetchPageYesFetches a page of data. Called on page/sort/filter changes.
fetchFilterOptionsNoReturns options for multiSelect filters with optionsSource: 'api'.
searchPeopleNoSearches people for people-type column filters.
getUserByEmailNoResolves a user by email (for restoring saved people filters).

IFetchParams

interface IFetchParams {
page: number; // 1-indexed
pageSize: number;
sort?: { field: string; direction: 'asc' | 'desc' };
filters: IFilters; // See Types reference for FilterValue shapes
}

IPageResult<T>

interface IPageResult<T> {
items: T[];
totalCount: number; // drives pagination controls
}
const dataSource: IDataSource<Employee> = {
async fetchPage(params) { /* ... */ },

async fetchFilterOptions(field: string): Promise<string[]> {
const res = await fetch(`/api/employees/filter-options/${field}`);
return res.json(); // ['Engineering', 'Marketing', 'Sales']
},

async searchPeople(query: string): Promise<UserLike[]> {
const res = await fetch(`/api/people/search?q=${query}`);
return res.json();
},

async getUserByEmail(email: string): Promise<UserLike | undefined> {
const res = await fetch(`/api/people/${email}`);
if (!res.ok) return undefined;
return res.json();
},
};

Error Handling & Loading

<OGrid
columns={columns}
dataSource={dataSource}
getRowId={(e) => e.id}
onError={(error) => {
console.error('Grid fetch failed:', error);
showToast('Failed to load data.');
}}
/>

The grid manages loading state automatically. You can also control it via gridRef.current?.setLoading(true).

Client-Side vs. Server-Side

Client-side (data)Server-side (dataSource)
Data locationAll rows in memoryFetched per page
Sorting / FilteringIn-memory by the gridYour server handles it
Best for< 10,000 rowsLarge datasets, real-time data
  • Filtering -- filter types and configuration
  • Grid API -- setFilterModel() for programmatic filtering
  • Types -- IFilters, FilterValue, UserLike shapes