---
title: Filters
description: Declare the controls that narrow the data of a view or a zone.
---

A filter describes one query value and the control that changes it. The user changes
the control, the value goes into the URL, and your view reads the typed value and
applies it to its query.

A filter is not a field. A field describes a piece of data in a record. A filter
describes a question about the records, and one filter can search many columns or
none. Thus filters have their own builders, and they do not accept field
declarations.

This introduction explains how a filter goes from a declaration to a query. The page
for each filter type has the full options.

## The URL is the state

The current value of each filter is in the query string and nowhere else. The
protocol response describes the controls and does not contain their values. This has
three results. A filtered list has a URL that a user can share or bookmark. The back
button of the browser restores the previous filters. And a reload shows the same
records.

## The four steps of a filter

**Declare.** `defineFilters` creates a filter bar from an array of filters. The
order of the array is the order of the controls.

```ts
const listFilters = defineFilters([
  filters.text('search').setLabel('Search'),
  filters.select('status', statusOptions).setLabel('Status'),
])
```

**Bind.** A filter bar has no place in the URL until you register it on a view or a
zone. The owner gives the filters their scope. A view filter uses `f[name]` in the
query string, and a zone filter uses `f[zone][name]`, so two zones on one page can
each have a `status` filter.

```ts
orders.view('list', { filters: listFilters }, async (ctx, view) => {
  // ...
})
```

**Read.** `ctx.parse(view).filters` returns one typed property for each filter. A
value that is missing or not valid is `undefined`. Backlit parses the values and
does not apply them. Your callback applies them to your query, so you decide what
"search" means for your data.

```ts
const { filters: applied } = ctx.parse(view)

const rows = await db.orders.list({
  search: applied.search,
  status: applied.status,
})
```

**Place.** A filter bar is not a content block. A `section` and a `panel` each have a
separate slot for it, which you set with `setFilters`. The controls then render in
the header of that block.

```ts
return section('Orders')
  .setFilters(view.state.filters)
  .setContent([panel([table(rows).setFields(columns)])])
```

When the owner of the filter bar is a zone, a change to a control requests only that
zone. See [Binding and placement](/reference/filters/binding).

## Select a filter type

| Your need                                                 | Filter                                          |
| --------------------------------------------------------- | ----------------------------------------------- |
| Search with free text                                     | [Text](/reference/filters/text)                  |
| Select yes, no, or no selection                           | [Toggle](/reference/filters/toggle)              |
| Select one or more values from a fixed set                | [Select](/reference/filters/select)              |
| Select the records between two calendar dates             | [Date range](/reference/filters/date-range)      |
| Select the records between two numbers                    | [Number range](/reference/filters/number-range)  |
| Select the grouping unit of a chart, such as day or month | [Period](/reference/filters/period)              |

A select filter with one value can also render as a row of buttons above a list. See
the [segments block](/reference/blocks/segments).

Filters are one of three types of search state. A paginator and a sorter use the
same steps: declare, bind, read, and place. See [Search state](/reference/state).

## API

```ts
defineFilters(filters: FilterBuilderContract[]): FilterBar
```

Filter names can contain letters, numbers, underscores, and hyphens. Each name
must be unique in one filter bar.

## Declaration

Declare related filters together. Their array order is their control order.

<BacklitExample path="filters/declaration" view="list" />

## Label

Every filter supports `setLabel`. If you do not set a label, the SDK sends an
empty label.

<BacklitExample path="filters/label" view="list" />

## All filter types

One filter bar can contain each built-in filter type. A select filter can also
accept several selected values.

<BacklitExample path="filters/all_options" view="list" />
