Skip to main content

Sorting

Click a column header to sort. Click again to reverse. Click a third time to clear. An arrow shows the current direction. That's the whole interaction — no configuration beyond sortable: true.

Click any column header to sort
Live
Try it in your framework

The demo uses Radix UI styling. Your framework's design system (Fluent, Material, Vuetify, PrimeNG) will render the sort indicator in its own style.

Make a column sortable

Add sortable: true to any column definition:

import { OGrid } from '@alaarab/ogrid-react-radix';

const columns = [
{ columnId: 'name', name: 'Name', sortable: true },
{ columnId: 'age', name: 'Age', sortable: true, type: 'numeric' as const },
{ columnId: 'department', name: 'Department', sortable: true },
{
columnId: 'salary',
name: 'Salary',
sortable: true,
type: 'numeric' as const,
valueFormatter: (v) => `$${Number(v).toLocaleString()}`,
},
];

function App() {
return (
<OGrid
columns={columns}
data={people}
getRowId={(item) => item.id}
defaultPageSize={10}
/>
);
}
Switching UI libraries

Same props across all React packages — 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>

Set a default sort

Use defaultSortBy and defaultSortDirection to pre-sort the grid on mount — useful when there's an obvious "right" order for your data, like newest-first for a log or alphabetical for a directory:

<OGrid
columns={columns}
data={employees}
getRowId={(item) => item.id}
defaultSortBy="name"
defaultSortDirection="asc"
/>

Custom sort order

By default, text columns sort alphabetically and numeric columns sort numerically. When that isn't right — priority levels, custom status orderings, anything domain-specific — provide a compare function:

const columns = [
{
columnId: 'priority',
name: 'Priority',
sortable: true,
// "High" should sort before "Medium" which sorts before "Low"
compare: (a, b) => {
const order = { High: 0, Medium: 1, Low: 2 };
return order[a.priority] - order[b.priority];
},
},
{
columnId: 'status',
name: 'Status',
sortable: true,
// Custom business logic: "In Progress" first, then "Open", then "Closed"
compare: (a, b) => {
const order = { 'In Progress': 0, Open: 1, Closed: 2 };
return (order[a.status] ?? 99) - (order[b.status] ?? 99);
},
},
];
Pro tip

Custom compare functions receive the full row objects — not just the cell values. That means you can sort by computed values, nested fields, or anything else you can derive from the row.

Keep sort state in your app

If you need to sync sort state to the URL, read it from an API, or share it between components, use controlled mode:

function App() {
const [sort, setSort] = useState({ field: 'name', direction: 'asc' as const });

// Persist to URL:
// useEffect(() => { setSearchParam('sort', sort.field); }, [sort]);

return (
<OGrid
columns={columns}
data={people}
getRowId={(item) => item.id}
sort={sort}
onSortChange={setSort}
/>
);
}

When sort and onSortChange are both present, the grid is fully controlled — it never manages sort state internally.

Server-side sorting

Sending sort state to your API is straightforward. The grid passes sort info to fetchPage() — your server does the actual ordering:

const dataSource: IDataSource<Project> = {
fetchPage: async ({ sort, page, pageSize, filters }) => {
const params = new URLSearchParams({
page: String(page),
pageSize: String(pageSize),
});

// Wire sort directly to your query params or request body
if (sort) {
params.set('sortBy', sort.field);
params.set('sortDir', sort.direction); // 'asc' | 'desc'
}

const res = await fetch(`/api/projects?${params}`);
const json = await res.json();
return { items: json.data, totalCount: json.total };
},
};
Pro tip

Server-side sorting disables client-side sort entirely — the grid just re-fetches when the user clicks a header. This pairs perfectly with large datasets where you never want all rows in the browser at once. See Server-Side Data for the full setup.

Props reference

PropTypeDefaultDescription
sortablebooleanfalseSet on IColumnDef. Enables click-to-sort on that column header.
compare(a: T, b: T) => numberCustom sort comparator on IColumnDef. Receives full row objects.
sort{ field: string; direction: 'asc' | 'desc' }Controlled sort state on OGrid.
onSortChange(sort) => voidCalled when the user changes the sort. Required for controlled mode.
defaultSortBystringInitial sort column (uncontrolled only).
defaultSortDirection'asc' | 'desc''asc'Initial sort direction (uncontrolled only).

Next steps