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
Try it in your framework
The demo above uses Radix UI for styling. To see this feature with the Fluent UI implementation, click "Open in online demo" below the demo.
Quick Example
- React
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>
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>;
}
| Method | Required | Description |
|---|---|---|
fetchPage | Yes | Fetches a page of data. Called on page/sort/filter changes. |
fetchFilterOptions | No | Returns options for multiSelect filters with optionsSource: 'api'. |
searchPeople | No | Searches people for people-type column filters. |
getUserByEmail | No | Resolves 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
}
Filter Options & People Search
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 location | All rows in memory | Fetched per page |
| Sorting / Filtering | In-memory by the grid | Your server handles it |
| Best for | < 10,000 rows | Large datasets, real-time data |