Unify Applications
Logo
Filters, Sorting & Pagination

Filters, Sorting & Pagination

Logo

11 mins READ

Controlling which records show, in what order, and how many at a time. Filtering and sorting can be configured in two places — on the data source (narrowing what's fetched) and on the component (letting users explore what's shown). The Filters block is the primary UI component for exposing filter, sort, search, and date controls to the app user.

filters_sorting_and_pagination.png
filters_sorting_and_pagination.png

Filtering in Two Places

Where

What it does

Use when

On the data source

Narrows the data before it ever reaches the component — fewer records are fetched, reducing payload size and server load.

You always want a fixed subset (e.g. only active records, only this user's records). Use data source filters to limit what's possible, not just what's shown.

On the component

Lets users filter, sort and search from the component's toolbar. The component never filters rows in the browser. On a table bound to a platform object, the filter is sent to the server automatically. On a mapped table, nothing changes until you bind {{ table1.filters.filter }} and {{ table1.filters.search }} into the data source's inputs.

Providing a user-facing interactive filter over the component's data.

table1.filters.* is the Table's own toolbar state, set when users filter, search or sort from the Table's toolbar. filters1.state.* is the state of a separate Filters block, described below. See Table State and Bindings for the Table's state.

The Filters Block

The Filters block is a toolbar of filter controls that together publish one shared filter state. The block never queries data itself — it reads field metadata from your data sources, lets the user compose criteria, and exposes the result as block state. You bind that state into your data source's inputs to drive re-fetching.

Note: Arrives pre-configured with three controls: a full condition-builder filter, a date range filter (preset to last 30 days), and a search box. You can add or remove controls via Standard Actions in the inspector.

Connecting the Filters Block to a Data Source

Before you start: the data source must use Automatic run behavior, so it re-runs when its bound inputs change.

  1. Select Data Sources: In the Filters block inspector under Data Sources, pick the data sources the toolbar should understand. The block reads each source's field metadata to populate the condition builder, field chips, and sort menu. It reads metadata only — it never triggers a fetch on its own.

  2. Bind exposed state to the data source inputs: In the data source's Input configuration, bind filter to {{ filters1.state.filter }}, search to {{ filters1.state.search }}, sortBy to {{ filters1.state.sort }}, startTime to {{ filters1.state.dateTimeFilter.epoch.startTime }} and endTime to {{ filters1.state.dateTimeFilter.epoch.endTime }}.

  3. The data source re-runs automatically: Because the data source's inputs are bound to the Filters block state, it re-fetches whenever the user changes any filter, sort, or search control. No event handler is needed.

Filters Block Properties

PropertyKeyTypeDefaultDescription
Data SourcesdataSourceIdsstring[][]The data sources whose field metadata drives the condition builder and sort menu.
Standard ActionsprimaryActionsarraydata filter + date filter + searchThe list of filter controls in the toolbar. Each entry has a type from the 7 available types.
Remember applied changespersistFiltersboolean—Keep the user's applied filter, sort, search, and date range when they leave and return to the page in the same browser tab.

The 7 Filter Control Types

Each filter control is one "action" in the Standard Actions list. Add or remove them — with limits noted below. The toolbar order is fixed; only multiple quick filters or multiple field filters can be ordered relative to each other.

TypeWhat it addsMultiple per block?State key
Data filterFull condition builder — users compose AND/OR groups over any filterable field.No — last one winsstate.filter
Date filterDate or date range picker with optional named presets (Last 30 days, This month, etc.).No — last one winsstate.dateTimeFilter
SortSort menu over the data source's sortable fields. Button label shows the current sort.No — last one winsstate.sort
SearchKeyword search box. Result is a plain string — the data source handles the matching.No — last one winsstate.search
Quick filterOne-click chip carrying a pre-configured filter or date range.Yes — every one rendersstate.filter (same slice as data filter)
Field filterA chip scoped to one field of the data source.Yes — every one rendersstate.filter (merged into combined filter)
AI filterA prompt popover — the user describes the filter in plain language and the block applies it.No — last one winsstate.filter

Filters Block Exposed State

Every control in the toolbar publishes its result under the block's name. Bind these into data source inputs to drive re-querying.

State keyTypeDescription
{{ filters1.state.filter }}objectThe combined filter tree — the condition builder output plus all field chip conditions merged under one AND group.
{{ filters1.state.search }}stringThe search box text. Empty string after the user clears the box.
{{ filters1.state.sort }}arraySort entries, each a field plus ASC or DESC.
{{ filters1.state.dateRangePreset }}stringThe active named preset (e.g. last30days); empty when the range is custom or fixed.
{{ filters1.state.dateTimeFilter.iso.startTime }}stringRange start as an ISO string.
{{ filters1.state.dateTimeFilter.iso.endTime }}stringRange end as an ISO string.
{{ filters1.state.dateTimeFilter.epoch.startTime }}numberRange start as epoch milliseconds.
{{ filters1.state.dateTimeFilter.epoch.endTime }}numberRange end as epoch milliseconds.
{{ filters1.state.dateTimeFilter.epoch.previousStartTime }}numberStart of the equal-length period immediately before the selected range (for period-over-period comparisons).

Data Filter (Condition Builder)

The data filter control adds a Filter button that opens a full condition builder. Users compose AND/OR filter groups over any filterable field of the selected data sources.

Condition Operators by Field Type

Field type

Operators available

Text

Contains, Does not contain, Contains case sensitive, Does not contain case sensitive, Equals to, Does not equal, Starts with, Does not start with, Ends with, Does not end with, Is present, Is not present, Matches regex, Does not match regex

Number

Less than, Less than or equal to, Greater than, Greater than or equal to, Equals to, Does not equal, Is present, Is not present

Date/time

Equals to, Does not equal, Before, Before or equal to, After, After or equal to, Between, Is present, Is not present

Time duration

Less than, Less than or equal to, Greater than, Greater than or equal to

Boolean

Is, Is not, Is present, Is not present

Lookup

Is, Is not, Is present, Is not present

Data Filter Properties

PropertyTypeDefaultDescription
labelstring—The button text.
valueSourceenumcustomcustom: author an initial filter in the inspector. mapped: bind the filter from data.
initialValuefilter config—Conditions applied on page load (when Source = Custom).
allowedFilterFieldsstring[]—When set, only these fields are offered in the builder. Restricts the entire Filters block, not just this control.

Sort Control

The sort control adds a sort menu. The button label derives from the currently applied order — no configurable label. The exposed state is {{ filters1.state.sort }}: an array of entries, each with a field and ASC or DESC direction.

PropertyTypeDescription
valueSourceenumcustom or mapped. Custom: set the initial order in the inspector. Mapped: bind from data.
initialValuesort configField-and-direction entries applied on load.

Search Control

The search control adds a keyword search box. The user's input is published as {{ filters1.state.search }} — a plain string. Bind it to the data source's search parameter. The data source handles the actual matching.

Note: On desktop the search box sits at the opposite end of the toolbar from the other controls. On mobile it moves above the other controls and stretches to full width.

Pagination

Data-bound components such as Table and Repeatable support two pagination modes, set under Content → Add-ons → Pagination → Type. The examples below use a Repeatable named repeatable1.

Type

Behavior

Configuration

Page based

Numbered pages with a page size. Viewers move between pages with prev/next controls. Total records count can be shown. A newly added Pagination add-on starts as Page based with a page size of 30.

Page Size Mode: Fixed (one size) or User Selectable (you provide options list and default). Initial Page. Pages are fetched from the server only if you wire them: bind the data source's limit and offset inputs to {{ repeatable1.pagination.pageSize }} and {{ repeatable1.pagination.offset }}, and bind Total Records to the same source's total count. The pager bar doesn't appear until Total Records is bound.

Scroll based

Loads more as the viewer scrolls. Appends new records below existing ones.

Needs the same two wires, or the data source is fetched once and never pages: bind the data source's limit and offset inputs to the block's page size and offset ({{ repeatable1.pagination.pageSize }} and {{ repeatable1.pagination.offset }}), and bind Total Records from the same data source. See Infinite Loading.

A Table bound to a platform object pages automatically. A Table with mapped data needs the same wiring, including Total Records.

Warning: A table inside another table's row won't accept an automatic data source. Use a manual data source triggered per row, or bind the inner table to the current row's data using the row context: {{ outer_table.context.currentRow.nestedItems }}.

Filters Block Methods

Callable programmatically from any event via the Control Block Method action or from a binding expression.

MethodParametersWhat it does
Resetfields: any of filter, sort, search (default: all three)Clears the chosen slices of state. Resetting filter also clears the date range, quick-filter selections, and all field chips.
Set Valuefilter, sort, search, dateTimeFilter, dateRangePresetApplies given values programmatically. Invalid-shape keys are silently dropped; valid siblings still apply.

Filters Block Events

Event

When it fires

On Filter Change

When the user changes any toolbar control, and on page load for controls with an initial value. It doesn't fire for changes made by the AI filter, the Set Value or Reset methods, or restored remembered state. Read the new values from filters1.state.

Patterns

Pattern 1 — Filter a table with a Filters block

// 1. Drop a Filters block above the table. Select the table's data source. // 2. In the data source's Input configuration, bind: filter: {{ filters1.state.filter }} search: {{ filters1.state.search }} sortBy: {{ filters1.state.sort }} startTime: {{ filters1.state.dateTimeFilter.epoch.startTime }} endTime: {{ filters1.state.dateTimeFilter.epoch.endTime }} // The query re-runs automatically whenever the user changes any control.

Pattern 2 — One-click status views with Quick Filters

// Add three Quick Filter actions to the Filters block: // "Open" — data filter: status Equals "open", Apply by default: ON // "In Progress" — data filter: status Equals "in_progress" // "Closed" — data filter: status Equals "closed" // Users switch views with one click. Picking a chip replaces the previous chip's filter. // The page loads pre-filtered to "Open" because it's set as the default.

Pattern 3 — Persist filters across navigation

// In the Filters block inspector, enable "Remember applied changes". // Now a user who filters a list, opens a detail page, and returns // finds their filter, sort, and search still applied for the tab session. // Closing the tab clears persisted state.

Gotchas

GotchaDetails
Resetting filter clears more than conditionsThe Reset method's filter option wipes the condition builder, the date range, active quick filters, and all field chips in one go. Sort and search reset independently.
Set Value silently drops invalid shapesEach key passed to Set Value is shape-checked. A key that fails the check is ignored — the error is silent, so a bad binding can cause the control to seem unresponsive.
Only one data/date/sort/search/AI control per blockAdding a second of any of these in Standard Actions is silently ignored — the last one in the list wins. Quick filters and field filters can repeat freely.
On Filter Change fires on load for controls with initial valuesA control with an initial value configured fires On Filter Change on page load, so a bound query may run twice (once bare, once filtered). Plan for the double run in analytics or pagination resets attached to this event.
Remembered state overrides initial valuesWith Remember applied changes on, a returning user's stored state is restored and every configured initial value — including quick filters marked Apply by default — is skipped for that visit.
Mapped values overwrite user changes when the binding changesFor a control with Source = Mapped, the bound value re-applies whenever it changes after load — replacing whatever the user had composed. Bind mapped values only when the binding, not the user, should own that slice of state.
No filter fields = disabled controlsIf the Filters block has no Data Sources selected, or the selected sources yield no filterable fields, all filter controls are disabled in the builder. Always configure Data Sources first.
FAQs

How do I set a default filter that loads pre-applied when the page opens?

On the data filter control, set Source to Custom and configure your conditions under Value. Those conditions are applied on page load before the first query runs. For quick-filter chips, enable Apply by default on the chip you want pre-selected. Both approaches fire the On Filter Change event on load, so a bound automatic data source re-fetches once with the initial filter applied.

The filters panel shows no fields to filter by. What's wrong?

The Filters block reads field metadata from the data sources you select under Data Sources in the inspector. If no sources are selected, or the selected sources have no filterable fields, the condition builder shows empty. Add the relevant data source to the block's Data Sources list. Note that the block reads metadata — selecting a source here does not trigger that source to run.

Can I filter and sort at the data source level instead of using the Filters block?

Yes — set fixed filters, sort and record count in the data source operation's own inputs (for example Storage → Fetch Records). Use this when the filter is fixed (always the same criteria) and doesn't need to change based on user input. Use the Filters block when users need to interactively compose their own criteria. Both can be combined: a fixed data-source filter narrows the dataset, and the Filters block lets users explore within that narrowed set.

How do I reset all filters when the user navigates to the page?

Use the Filters block's Reset method in the page's On Load event, or in a button action. When you call it from an expression, always pass the fields to reset: {{ filters1.reset({ fields: ['filter', 'sort', 'search'] }) }}. Calling reset() with no arguments in an expression fails. (In the Control Block Method action form, the fields default to all three.) To reset only specific slices — for example just the search without clearing filters — pass only that field: {{ filters1.reset({ fields: ['search'] }) }}.

Why does On Filter Change fire twice on page load?

A filter control with a configured initial value fires On Filter Change when the initial value is applied on load — which is intentional so bound queries can use the initial filter. However, if the data source is also set to run on page load (or runs automatically when the block mounts), the query runs once bare and once with the filter, appearing as a double fetch. This is normal behavior. If you want only one run, keep the data source Automatic and turn it off until the filter is set: in the data source's Advanced tab, under Trigger Conditions, set Disable query to a condition that stays true until the initial filter has been applied. Don't switch the source to Manual — a Manual source doesn't re-run when its bound inputs change, so later filter changes would stop refreshing the data.