Filtering
Each column's ⋮ menu holds a filter that matches its type: text (contains, equals, starts/ends with, blank…), number (=, ≠, <, ≤, >, ≥, between, blank),…
Live demoFiltering
Text, number, date and set filters plus a quick search box.
Code for this demo
import { createGrid } from '@freegridjs/core';
import { demoColumns, makeRows } from '../data';
import { button, search, separator, text, tr, type DemoContext } from '../ui';
export default function mount(ctx: DemoContext) {
const { lang } = ctx;
const grid = createGrid(ctx.host, { columns: demoColumns(lang), data: makeRows(20_000), locale: lang });
const count = text(ctx, '');
const update = () => (count.textContent = tr(lang, {
en: `${grid.getDisplayedRowCount().toLocaleString()} of 20,000 rows`,
ko: `20,000행 중 ${grid.getDisplayedRowCount().toLocaleString()}행`,
}));
grid.on('filterChange', e => {
ctx.log(`filterChange → ${JSON.stringify(e.filters)}`);
update();
});
search(ctx, tr(lang, { en: 'Quick search, e.g. "kim seoul"', ko: '빠른 검색 예: "kim japan"' }), q => {
grid.setQuickFilter(q);
update();
});
separator(ctx);
button(ctx, tr(lang, { en: 'Q1 ≥ 80,000', ko: '1분기 ≥ 80,000' }), () => grid.setFilter('q1', { type: 'number', op: 'gte', value: 80000 }));
button(ctx, tr(lang, { en: 'Joined in 2024', ko: '2024년 가입' }), () =>
grid.setFilter('joined', { type: 'date', op: 'between', value: '2024-01-01', valueTo: '2024-12-31' }));
button(ctx, tr(lang, { en: 'Japan or Korea', ko: '일본 또는 한국' }), () =>
grid.setFilter('country', { type: 'set', values: ['Japan', 'South Korea'] }));
button(ctx, tr(lang, { en: 'Clear filters', ko: '필터 모두 해제' }), () => {
for (const id of Object.keys(grid.getFilterModel())) grid.setFilter(id, null);
});
update();
return grid;
}Live demoAdvanced filter
Two conditions per column with And / Or, and conditions across columns from a builder.
Code for this demo
import { createGrid } from '@freegridjs/core';
import { demoColumns, makeRows } from '../data';
import { button, separator, text, tr, type DemoContext } from '../ui';
export default function mount(ctx: DemoContext) {
const { lang } = ctx;
const h = (en: string, ko: string) => tr(lang, { en, ko });
const grid = createGrid(ctx.host, { columns: demoColumns(lang), data: makeRows(2000), locale: lang, statusBar: true });
grid.on('advancedFilterChange', e => ctx.log(`advancedFilterChange → ${JSON.stringify(e.advancedFilter)}`));
grid.on('filterChange', e => ctx.log(`filterChange → ${JSON.stringify(e.filters)}`));
text(ctx, h('Open a column menu: it takes two conditions joined with And / Or. Conditions across columns: right-click → Advanced filter…',
'컬럼 메뉴에서 조건 두 개를 그리고/또는으로 묶을 수 있습니다. 여러 컬럼에 걸친 조건은 우클릭 → 고급 필터…'));
separator(ctx);
button(ctx, h('Q1 between 20,000 and 40,000', '1분기 2만~4만'), () =>
grid.setFilter('q1', { type: 'combined', operator: 'and', conditions: [{ type: 'number', op: 'gte', value: 20000 }, { type: 'number', op: 'lte', value: 40000 }] }));
button(ctx, h('(Japan and Q4 > 80,000) or name starts with "Kim"', '(일본 그리고 4분기 > 8만) 또는 이름이 Kim으로 시작'), () =>
grid.setAdvancedFilter({ operator: 'or', conditions: [
{ operator: 'and', conditions: [
{ colId: 'country', model: { type: 'text', op: 'equals', value: 'Japan' } },
{ colId: 'q4', model: { type: 'number', op: 'gt', value: 80000 } },
] },
{ colId: 'name', model: { type: 'text', op: 'startsWith', value: 'kim' } },
] }));
button(ctx, h('Open the builder', '조건 작성 창 열기'), () => grid.openFilterBuilder());
button(ctx, h('Clear', '모두 해제'), () => {
grid.setAdvancedFilter(null);
grid.setFilter('q1', null);
});
return grid;
}Each column's ⋮ menu holds a filter that matches its type: text (contains, equals, starts/ends with, blank…), number (=, ≠, <, ≤, >, ≥, between, blank), date (equals, before, after, between) and set (a searchable checklist of the column's values). Text matching ignores case. A filled filter icon marks filtered columns.
grid.setFilter('name', { type: 'text', op: 'contains', value: 'kim' });
grid.setFilter('total', { type: 'number', op: 'between', value: 1000, valueTo: 5000 });
grid.setFilter('joined', { type: 'date', op: 'after', value: '2025-01-01' });
grid.setFilter('country', { type: 'set', values: ['Japan', 'South Korea'] });
grid.setFilter('name', null); // clear one
grid.setQuickFilter('kim seoul'); // every word must match some visible column
grid.on('filterChange', e => console.log(e.filters));The column menu takes up to two conditions joined with and / or. Conditions across columns come from setAdvancedFilter or "Advanced filter…" in the right-click menu; they nest and apply together with the column filters.
// two conditions on one column
grid.setFilter('qty', { type: 'combined', operator: 'and',
conditions: [{ type: 'number', op: 'gte', value: 10 }, { type: 'number', op: 'lte', value: 100 }] });
// (status = done AND amount > 100) OR customer starts with "Kim"
grid.setAdvancedFilter({ operator: 'or', conditions: [
{ operator: 'and', conditions: [
{ colId: 'status', model: { type: 'text', op: 'equals', value: 'done' } },
{ colId: 'amount', model: { type: 'number', op: 'gt', value: 100 } },
] },
{ colId: 'customer', model: { type: 'text', op: 'startsWith', value: 'kim' } },
] });
grid.openFilterBuilder(); // or right-click → Advanced filter…Filter row
filterRow: true puts a box under every header that filters as you type (applied after 0.2 s; Enter applies at once, Escape clears). Type what fits the column, as below. Text the grid cannot read turns the box red and leaves the filter as it was.
| Column | Type | Means |
|---|---|---|
| Text | kim | Values containing "kim" (any case) |
| Number | 15 · >10 · >=10 · <5 · <=5 · <>3 | Equal, greater, at least, less, at most, not equal (1,000 and currency signs are read too) |
| Number range | 10~20 · 10..20 | From 10 to 20 |
| Date | 2026-03-05 · >2026-01-01 · <2026-12-31 | That day, after, before (dots or slashes work too: 2026.3.5) |
| Date range | 2026-01-01~2026-03-31 | Between the two days, both included |
| Set and true/false | A drop-down | Columns with filter: 'set' and boolean columns pick one value |
The filter row, the column menu and setFilter() share one filter, so a change in one shows in the others. Leave a column out with filter: false. What you are typing stays put when live updates change the rows.
const grid = createGrid(el, {
columns: [
{ field: 'name', header: 'Name' }, // text: contains
{ field: 'qty', header: 'Qty', type: 'number' }, // >10, <=5, 10~20
{ field: 'joined', header: 'Joined', type: 'date' }, // >2026-01-01, 2026-01-01~2026-03-31
{ field: 'status', header: 'Status', filter: 'set' }, // a list of the column's values
{ field: 'memo', header: 'Memo', filter: false }, // no box
],
data,
filterRow: true,
});
// the boxes and the column menu share one filter: setting one updates the other
grid.setFilter('qty', { type: 'number', op: 'between', value: 10, valueTo: 20 }); // the box shows 10~20Questions about this page
What filters does FreeGrid have?
Text, number, date and set (list of values) filters in the column menu, a filter row under the header, a quick search box, and an advanced filter that combines conditions across columns with AND and OR.