Skip to Content
Documentation
Getting startedComponentsChartsTheming
Get Pro
Overview
Concepts

Filters

Used to filter data with typed fields, operators and value editors

Source
Status

Northstar Labs

Enterprise

Active

$12,400

Arcwell Health

Enterprise

Active

$9,800

Copperline

Growth

Active

$4,700

Daybreak Energy

Enterprise

Active

$15,100

Usage

The Filters component is built on Saas.js Conditions: a headless engine for typed condition queries with validation, evaluation and serialization. You describe your fields once with defineConditions, and the same definition drives the filter bar, the query state and the filtering itself.

import { createFilters } from '#components/ui/filters'

Define the fields, then create the filters hook at module scope. Every instance returned by useConditions carries the filter parts alongside the full conditions API.

import { z } from 'zod'

const accountFilters = createFilters({
  fields: {
    status: {
      type: 'enum',
      label: 'Status',
      schema: z.enum(['Active', 'Trial', 'Paused']),
      operators: ['equals', 'not', 'in'],
      defaultOperator: 'equals',
      options: [
        { value: 'Active', label: 'Active' },
        { value: 'Trial', label: 'Trial' },
        { value: 'Paused', label: 'Paused' },
      ],
    },
    mrr: {
      type: 'number',
      label: 'MRR',
      schema: z.coerce.number().min(0),
      operators: ['equals', 'gte', 'lte', 'between'],
    },
  },
})

The built definition is available as accountFilters.definition, for adapters and server-side evaluation. When the definition is shared with non-UI code — a Drizzle query on the server, a serialized saved view — define it separately with defineConditions and pass it as definition instead:

import { defineConditions } from '@saas-js/conditions'

import { accountConditions } from '~/shared/account-conditions'

const accountFilters = createFilters({ definition: accountConditions })

Fields with options (like enum and boolean fields) open a searchable submenu straight from the add-filter menu. Clicking an option commits it with the field's default operator ("Status is Active") and closes the menu; when the field supports a multi-select operator like in, each option also carries a checkbox that toggles values while the menu stays open, and the operator follows the selection ("is" for one value, "is any of" for several). Other field types open their value editor after picking the field.

Add icons through meta on a field or option: a field icon shows in the add-filter menu and on the chip's label, an option icon shows in the option lists and on the chip's value. When two or more values are selected the chip collapses to the value icons plus a count — set pluralLabel on the field's meta to name it ("2 statuses" instead of "2 selected").

status: {
  type: 'enum',
  label: 'Status',
  meta: { icon: <StatusIcon />, pluralLabel: 'statuses' },
  options: [
    { value: 'active', label: 'Active', meta: { icon: <ActiveDot /> } },
  ],
  // ...
}

Render the bar inside conditions.Root and filter your rows with useFilter.

function Accounts() {
  const conditions = accountFilters.useConditions()
  const matches = conditions.useFilter(accounts)

  return (
    <conditions.Root>
      <conditions.FilterBar />
      <AccountList accounts={matches} />
    </conditions.Root>
  )
}
info
useConditions supports uncontrolled (defaultValue), controlled (value + onValueChange) and external store usage. The committed query is a plain serializable tree — see the Conditions documentation for validation, serialization and server-side evaluation.

Examples

Async Options

Pass a function as a field's options to load them on demand. The value editor gets a search input, debounced queries and a loading state without extra wiring.

Northstar Labs

Maya Chen

Kite & Harbor

Jon Bell

Arcwell Health

Priya Shah

Fieldnote Studio

Alex Moreno

Copperline

Noor Aziz

Daybreak Energy

Maya Chen

Plainspoken

Sam Whitfield

Orbit Commerce

Jon Bell

Composed Bar

Pass children to FilterBar to compose the bar yourself from conditions.FilterChips, conditions.AddFilterButton and conditions.ClearFiltersButton, or your own components. Use conditions.draft.beginAddCondition to build a fully custom add button.

Northstar Labs

Active

Kite & Harbor

Trial

Arcwell Health

Active

Fieldnote Studio

Paused

Copperline

Active

Data Table

Pair the filter bar with the Data Table through @saas-js/conditions-tanstack-table: the committed query becomes the table's global filter, and every row is evaluated against it. Pass undefined while the query is empty so the empty state doesn't count it as an active filter, and wire table.NoResults to clear the conditions.

Northstar Labs

Enterprise

Active

$12,400

Kite & Harbor

Growth

Trial

$3,200

Arcwell Health

Enterprise

Active

$9,800

Fieldnote Studio

Starter

Paused

$890

Copperline

Growth

Active

$4,700

Daybreak Energy

Enterprise

Active

$15,100

Value Formatting

Use formatValue to control how committed values appear in the chips, and operatorLabels to override the compact operator labels. Register custom value editors per field type, field or operator with the editor registries.

const accountFilters = createFilters({
  fields: accountFields,
  formatValue: ({ fieldId, value }) => {
    if (fieldId === 'mrr' && typeof value === 'number') {
      return currencyFormat.format(value)
    }
    return undefined
  },
  operatorLabels: {
    gte: 'at least',
    lte: 'at most',
  },
})

Props

createFilters

PropDefaultType
operators 'defaultOperators'
ConditionOperators

Custom operator registry for the inline `fields` form.

fields
ConditionFields

The filterable fields: type, label, validation schema, operators and options. Builds the conditions definition inline; it is exposed as `filters.definition`. Provide either `fields` or `definition`.

definition
ConditionsDefinition

A conditions definition created with `defineConditions`, as an alternative to `fields` — use this when the definition is shared with non-UI code.

valueEditors
Record<string, ValueEditorComponent>

Value editors by field type, merged over the built-in string, number, date and option editors.

fieldValueEditors
Record<string, ValueEditorComponent>

Value editors for a specific field, taking precedence over the type editors.

operatorValueEditors
Record<string, ValueEditorComponent>

Value editors for a specific operator.

operatorLabels
Record<string, string>

Compact operator labels shown in the chips, merged over the built-in overrides (`gt` `>`, `gte` `≥`, `lt` `<`, `lte` `≤`). Unmapped operators use the operator label from the definition.

formatValue
(context: { fieldId, field, value }) => string | undefined

Formats a committed value for display in a chip. Return `undefined` to fall back to the default formatting (option labels, localized dates, Yes/No booleans).

FilterBar

PropDefaultType
children
React.ReactNode

Replaces the default composition (chips, add button, clear button). Compose your own bar from `conditions.FilterChips`, `conditions.AddFilterButton` and `conditions.ClearFiltersButton`.

AddFilterButton

PropDefaultType
parentId 'the root group'
string

The condition group to add the condition to.

children 'Filter'
React.ReactNode

The button content.

ClearFiltersButton

PropDefaultType
children 'Clear'
React.ReactNode

The button content. The button only renders while at least one filter is active.

Previous

File Upload

Next

GridList