Query Builder
Filters that read the schema — and run live.
Quick chips for simple cases, nested AND/OR canvas for advanced. Every edit is evaluated instantly against 300 seeded faker records — the same shapes as the DataTable demo.
Quick filters
No quick filters active — chips turn forest when filled.
Live results
All 300 seeded records — add a filter to narrow
| Employee | Role | Department | Status | Salary |
|---|---|---|---|---|
Ross Abbott kirk78@gmail.com | Manager | Support | Inactive | $46,540 |
Felicia Brekke miriam.crooks33@yahoo.com | Analyst | Design | Inactive | $146,219 |
Horacio Braun wallace_casper@yahoo.com | Manager | Support | Active | $208,020 |
Alia Jerde PhD ole_volkman90@gmail.com | Designer | Design | Inactive | $59,071 |
Peggy Schroeder sydnie_tromp@hotmail.com | Developer | Support | Inactive | $116,298 |
Justyn Larson nicolas12@yahoo.com | Manager | Support | Active | $88,365 |
Showing 6 of 300 matches · evaluated live on every edit · payload below is what you’d send as queryParams.filters
Filter payload
no filters{
"logic": "AND",
"conditions": [],
"search": ""
}fetchData to a real API and this exact payload goes out with the request — the DataTable demo already speaks this contract.Quick filters: chips turn forest when filled, X clears. Good for 1-3 filters.
Advanced: AND/OR rail per group, add condition or inner group (max depth 2). Field change resets the operator.
Live engine: every operator from the schema runs client-side against seeded rows — deterministic between server and browser.
Usage
TSXimport { z } from 'zod'
import { QueryBuilder } from '@/components/queryBuilder/QueryBuilder'
const schema = z.object({
name: z.string().describe(JSON.stringify({ label: 'Name' })),
status: z.enum(['Active','Inactive']).describe(JSON.stringify({
label: 'Status', inputType: 'select',
options: [{ label: 'Active', value: 'Active' }, { label: 'Inactive', value: 'Inactive' }]
})),
salary: z.coerce.number().describe(JSON.stringify({ label: 'Salary' })),
joinedAt: z.string().describe(JSON.stringify({ label: 'Joined', inputType: 'date' })),
})
export default function FilterPanel() {
return (
<QueryBuilder
schema={schema}
onQueryChange={(filters) => console.log(filters)}
defaultOpen
ui={{ showModeToggle: true, persistMode: false }}
/>
)
}What is a schema-driven QueryBuilder?
A schema-driven QueryBuilder is a visual filter composer whose fields, input types, and operators are derived from a Zod schema instead of being hand-written per screen. The schema is the single source of truth: add a field to the schema and the builder picks it up with the right input control and operator set — no filter UI code to maintain.
This playground is a working demonstration of that pattern. Both demo schemas (employee and product) are plain Zod objects with field metadata; the builder turns them into typed filter fields, and a small evaluation engine runs the resulting query against seeded faker records in real time.
How it works
zodToFilterFields maps each schema key to a FilterField — type, label, and allowed operators. Quick mode renders one chip per field with a fixed operator; advanced mode renders a tree of conditions and nested AND/OR groups (max depth 2). Every edit emits a JSON payload shaped like { logic, conditions, search } — the same contract the DataTable demo accepts as queryParams.filters.
The payload feeds a pure evaluation engine (evaluateFilters.ts) that implements every operator — text contains, number comparisons, boolean flags, multiselect includes, date before/after/between — and recurses through groups. The 300 demo rows are generated with per-row faker seeds, so server and browser evaluate against identical data.
Features
- Zod schema mapped to typed filter fields
- Quick chip filters and advanced AND/OR groups
- Type-aware operators per field
- Live filtering over 300 seeded faker records
- Live results table with match percentage
- JSON payload preview matching the DataTable contract
When to use a component like this
A QueryBuilder earns its place when users need to compose their own filters — admin panels, ERP list screens, CRM segments, support queues, audit logs. Deriving the builder from a schema keeps it in sync with the API contract: the JSON it produces is the JSON your backend already speaks, and new fields appear in the UI the moment they are added to the schema.
Built with React, TypeScript and Zod. Related demos: the React DataTable playground, which consumes this filter payload, and the SchemaForm builder, which uses the same schema-driven pattern for data entry.