Filtering
OGrid supports four built-in filter types -- text, multiSelect, people, and date -- configured per column. Filters appear as popovers triggered from column headers.
Live Demo
The live demo above shows React Radix UI styling (lightweight default). To see how filter popovers look in your framework's design system, click the framework buttons above the demo to open online demo. Each framework renders filter UI with its native components (dropdowns, inputs, date pickers, etc.), so the styling matches your design system.
Quick Example
- React
- Angular
- Vue
- Vanilla JS
import { OGrid } from '@alaarab/ogrid-react-radix';
const columns = [
{
columnId: 'name',
name: 'Name',
filterable: { type: 'text' },
},
{
columnId: 'department',
name: 'Department',
filterable: { type: 'multiSelect', filterField: 'department' },
},
{
columnId: 'status',
name: 'Status',
filterable: { type: 'multiSelect', filterField: 'status' },
},
{
columnId: 'salary',
name: 'Salary',
type: 'numeric' as const,
valueFormatter: (v) => `$${Number(v).toLocaleString()}`,
},
];
function App() {
return (
<OGrid
columns={columns}
data={people}
getRowId={(item) => item.id}
defaultPageSize={10}
/>
);
}
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>
import { Component } from '@angular/core';
import { OGridComponent, type IColumnDef } from '@alaarab/ogrid-angular-material';
@Component({
standalone: true,
imports: [OGridComponent],
template: `<ogrid [props]="gridProps" />`
})
export class GridComponent {
gridProps = {
columns: [
{ columnId: 'name', name: 'Name', filterable: { type: 'text' } },
{
columnId: 'department', name: 'Department',
filterable: { type: 'multiSelect', filterField: 'department' },
},
{
columnId: 'status', name: 'Status',
filterable: { type: 'multiSelect', filterField: 'status' },
},
{
columnId: 'salary', name: 'Salary', type: 'numeric',
valueFormatter: (v: unknown) => `$${Number(v).toLocaleString()}`,
},
] as IColumnDef<Person>[],
data: people,
getRowId: (item: Person) => item.id,
defaultPageSize: 10,
};
}
Same component API across Angular packages. To switch, just change the import:
- Radix (CDK):
from '@alaarab/ogrid-angular-radix'(default, lightweight) - Angular Material:
from '@alaarab/ogrid-angular-material' - PrimeNG:
from '@alaarab/ogrid-angular-primeng'
All components are standalone — no NgModule required.
<script setup lang="ts">
import { OGrid, type IColumnDef } from '@alaarab/ogrid-vue-vuetify';
const columns: IColumnDef<Person>[] = [
{ columnId: 'name', name: 'Name', filterable: { type: 'text' } },
{
columnId: 'department', name: 'Department',
filterable: { type: 'multiSelect', filterField: 'department' },
},
{
columnId: 'status', name: 'Status',
filterable: { type: 'multiSelect', filterField: 'status' },
},
{
columnId: 'salary', name: 'Salary', type: 'numeric',
valueFormatter: (v) => `$${Number(v).toLocaleString()}`,
},
];
const gridProps = {
columns,
data: people,
getRowId: (item) => item.id,
defaultPageSize: 10,
};
</script>
<template>
<OGrid :gridProps="gridProps" />
</template>
Same component API across Vue packages. To switch, just change the import:
- Radix (Headless UI):
from '@alaarab/ogrid-vue-radix'(default, lightweight) - Vuetify:
from '@alaarab/ogrid-vue-vuetify'— wrap in<v-app>for theming - PrimeVue:
from '@alaarab/ogrid-vue-primevue'
import { OGrid } from '@alaarab/ogrid-js';
import '@alaarab/ogrid-js/styles';
const grid = new OGrid(document.getElementById('grid'), {
columns: [
{
columnId: 'name',
name: 'Name',
filterable: { type: 'text' },
},
{
columnId: 'department',
name: 'Department',
filterable: { type: 'multiSelect', filterField: 'department' },
},
{
columnId: 'status',
name: 'Status',
filterable: { type: 'multiSelect', filterField: 'status' },
},
{
columnId: 'salary',
name: 'Salary',
type: 'numeric',
valueFormatter: (v) => `$${Number(v).toLocaleString()}`,
},
],
data: people,
getRowId: (item) => item.id,
pageSize: 10,
});
How It Works
Set the filterable property on a column definition to an IColumnFilterDef object. The type field determines the filter UI:
Text Filter
A simple text input. Filters rows where the column value contains the search string (case-insensitive).
{ columnId: 'name', name: 'Name', filterable: { type: 'text' } }
Multi-Select Filter
A checkbox list with Select All and Clear All buttons. Users pick one or more values to include.
{
columnId: 'status',
name: 'Status',
filterable: {
type: 'multiSelect',
filterField: 'status', // field to filter on (defaults to columnId)
optionsSource: 'static', // 'static' | 'api' | 'years'
options: ['Active', 'Draft', 'Archived'],
},
}
Options sources:
optionsSource | Behavior |
|---|---|
'static' | Uses the options array you provide. |
'api' | Calls dataSource.fetchFilterOptions(field) to load options dynamically. |
'years' | Auto-generates a year list (current year down, configurable via yearsCount). |
| (omitted) | Client-side: unique values extracted from the data array. Server-side: uses 'api'. |
People Filter
A search-as-you-type input that finds users via dataSource.searchPeople(query). Displays UserLike objects with name, email, and photo.
{
columnId: 'assignee',
name: 'Assignee',
filterable: { type: 'people' },
}
The people filter requires a dataSource with a searchPeople method. It is designed for directory lookups (e.g., Microsoft Graph, LDAP).
Date Filter
A date range picker with From and To date inputs. Filters rows where the column value falls within the specified range.
{
columnId: 'createdDate',
name: 'Created',
type: 'date',
filterable: { type: 'date' },
}
Columns with type: 'date' automatically get date formatting, chronological sorting, and a native date input editor — no additional configuration needed.
Controlled vs. Uncontrolled
- Uncontrolled -- the grid manages filter state internally. Filters reset when the component unmounts.
- Controlled -- pass
filtersandonFiltersChangeto own the filter state externally (e.g., for URL sync or persistence).
function App() {
const [filters, setFilters] = useState<IFilters>({
status: { type: 'multiSelect', value: ['Active'] },
name: { type: 'text', value: 'John' },
});
return (
<OGrid
columns={columns}
data={tasks}
getRowId={(item) => item.id}
filters={filters}
onFiltersChange={setFilters}
/>
);
}
Server-Side Filtering
When using a dataSource, filter values are passed to fetchPage() in the filters param. The grid does not filter client-side.
const dataSource: IDataSource<Task> = {
fetchPage: async ({ filters, page, pageSize, sort }) => {
const params = new URLSearchParams({ page: String(page), pageSize: String(pageSize) });
if (filters.status?.type === 'multiSelect') params.set('status', filters.status.value.join(','));
const res = await fetch(`/api/tasks?${params}`);
return res.json();
},
fetchFilterOptions: async (field) => {
const res = await fetch(`/api/tasks/filter-options/${field}`);
return res.json();
},
searchPeople: async (query) => {
const res = await fetch(`/api/users/search?q=${query}`);
return res.json();
},
};
Props
| Prop | Type | Default | Description |
|---|---|---|---|
filterable | IColumnFilterDef | -- | Set on IColumnDef. Defines filter type and options. |
filterable.type | 'text' | 'multiSelect' | 'people' | 'date' | -- | Filter UI type. |
filterable.filterField | string | columnId | Field name used in the filter model. |
filterable.optionsSource | 'api' | 'static' | 'years' | auto | Where multi-select options come from. |
filterable.options | string[] | -- | Static options for multi-select. |
filterable.yearsCount | number | -- | Number of years to generate for 'years' source. |
filters | IFilters | -- | Controlled filter state on OGrid. |
onFiltersChange | (filters: IFilters) => void | -- | Called when filters change (controlled mode). |
Related
- Sorting -- sort filtered results
- Pagination -- paginate filtered results