iOS Android
Preview DataTableFilter on your device
Download Expo Go, then scan the QR code to preview native components.
The native port of the web data-table-filter engine. The DOM-free core (column model, operators, faceting, useDataTableFilters, createColumnConfigHelper) is copied from @docyrus/ui, so filter state (FiltersState) is interchangeable between platforms. The UI is re-skinned for phones: a Filter button opens an ActionSheet wizard (column → operator → value) and active filters show as pills — tap the operator or value of a pill to edit it, × to remove it.
pnpm dlx @docyrus/cli add @docyrus/rn-data-table-filter
Required Packages (2 packages) pnpm add date-fns @react-native-community/datetimepicker
import { useState } from 'react' ;
import {
DataTableFilter ,
createColumnConfigHelper ,
filterData ,
useDataTableFilters ,
type FiltersState
} from '@/components/docyrus-native/data-table-filter' ;
type Deal = { name : string ; stage : string ; amount : number ; closeDate : Date ; won : boolean };
const dtf = createColumnConfigHelper < Deal >();
const columnsConfig = [
dtf . text (). id ( 'name' ). accessor ( row => row . name ). displayName ( 'Name' ). icon ( 'fal text' ). build (),
dtf . option (). id ( 'stage' ). accessor ( row => row . stage ). displayName ( 'Stage' )
. options ([
{ label : 'Lead' , value : 'lead' , color : '#94a3b8' },
{ label : 'Won' , value : 'won' , color : '#22c55e' }
])
. build (),
dtf . number (). id ( 'amount' ). accessor ( row => row . amount ). displayName ( 'Amount' ). build (),
dtf . date (). id ( 'closeDate' ). accessor ( row => row . closeDate ). displayName ( 'Close date' ). build ()
];
export function DealsFilter ({ deals } : { deals : Deal [] }) {
const [ filters , setFilters ] = useState < FiltersState >([]);
const { columns , actions , strategy } = useDataTableFilters ({
strategy : 'client' ,
data : deals ,
columnsConfig ,
filters ,
onFiltersChange : setFilters
});
const visibleDeals = filterData ( deals , columnsConfig , filters ); // render these rows
return (
<>
< DataTableFilter columns = { columns } filters = { filters } actions = { actions } strategy = { strategy } />
< DealList deals = { visibleDeals } />
</>
);
}
import { filtersToServerFilter } from '@/components/docyrus-native/data-table-filter' ;
const { columns , filters , actions } = useDataTableFilters ({
strategy : 'server' ,
data : [],
columnsConfig ,
calculateFacets : false ,
options : { stage : stageOptions }, // server strategy needs static options…
faceted : { stage : stageCounts } // …and (optionally) counts from the server
});
// Backend-safe payload: `like %x%`, `between`, `not between` → OR group, `contains any` for array columns,
// snake_case relative dates (`last_7_days`, `in_last_x_days` + N). Unsendable rules are dropped.
const params = { filters : filtersToServerFilter ( filters , { columns : columnsConfig }) };
const ownerColumn = {
... dtf . option (). id ( 'owner' ). accessor ( row => row . ownerId ). displayName ( 'Owner' ). build (),
asyncOptions : {
pageSize : 25 ,
debounceMs : 300 ,
load : async ({ search , page , pageSize , signal }) => {
const res = await client . get ( '/v1/users' , { params : { search , offset : page * pageSize , limit : pageSize }, signal });
return { items : res . data . map ( u => ({ value : u . id , label : ` ${ u . firstname } ${ u . lastname } ` , imageUrl : u . photo })), hasMore : res . data . length === pageSize };
}
}
};
Loaded labels are HTML-entity decoded (& → &), loads are aborted when the search changes or the sheet closes, and "Load more" fetches the next page.
Prop Type Default Description columnsColumn<TData>[]— Required. Columns from useDataTableFilters().columns.filtersFiltersState— Required. Current filter state (FilterModel[]).actionsDataTableFilterActions— Required. Actions from useDataTableFilters().actions.strategy'client' | 'server'— Required. client computes options / facets from data; server expects them from the host and filters nothing locally.locale'en' | 'de' | 'fr' | 'nl' | 'zh_CN' | 'zh_TW''en'Bundled string catalog (web parity). An app UiTranslationProvider entry under ui.dataTableFilter.<key> wins. combinator'and' | 'or''and'How the host combines filters — the value shown by the AND / OR toggle. useDataTableFilters and filterData default to AND. onCombinatorChange(combinator: 'and' | 'or') => void— Shows an interactive AND / OR toggle when two or more filters are active. classNamestring— Additional classes for the root view.
Option Type Default Description strategy'client' | 'server'— Required. Filtering strategy.dataTData[]— Required. Rows used for option / facet / min-max derivation (pass [] for server-paged data).columnsConfigColumnConfig<TData>[]— Required. Column definitions (hand-written or built with createColumnConfigHelper).defaultFiltersFiltersState[]Initial state (uncontrolled). filtersFiltersState— Controlled state — must be paired with onFiltersChange. onFiltersChangeDispatch<SetStateAction<FiltersState>>— Controlled setter. calculateFacetsbooleantrueCompute faceted counts from data. Disable when you supply faceted yourself. optionsPartial<Record<optionColumnId, ColumnOption[]>>— Override option lists per option / multiOption column. facetedPartial<Record<columnId, Map<string, number> | [number, number]>>— Faceted counts (option columns) or [min, max] (number columns).
Returns { columns, filters, actions, strategy }.
Action Signature Description addFilterValue(column, values) => voidAdd values to an option / multiOption filter (operator auto-switches is → is any of). removeFilterValue(column, values) => voidRemove values; the filter is dropped when empty. setFilterValue(column, values) => voidReplace the values of any filter type. setFilterOperator(columnId, operator) => voidChange the operator. removeFilter(columnId) => voidRemove one filter. removeAllFilters() => voidClear everything. upsertFilter(filter: FilterModel) => voidNative addition (optional). Insert / replace a whole filter in one step — how the wizard creates value-less filters (is empty, today).
Field Type Description idstringRequired. Column id (becomes FilterModel.columnId).accessor(row: TData) => unknownRequired. Reads the cell value.displayNamestringRequired. Label.typeColumnDataTypeRequired. 'text' | 'number' | 'date' | 'option' | 'multiOption' | 'boolean' | 'uuid'.iconstring | ReactNodeDocyrusIcon name ('fal user') or a node. Optional on native — a type default is used. (Web: LucideIcon, required.) optionsColumnOption[]Static options (option / multiOption). facetedOptionsMap<string, number>Server-supplied counts. min / maxnumberNumber bounds (shown as input hints). transformOptionFn(value) => ColumnOptionDerive options from raw values. orderFn(a, b) => numberCustom option ordering. asyncOptionsAsyncOptionsConfigPaged, debounced, abortable remote option loader. trueLabel / falseLabelstringBoolean choice labels. includeTimebooleanDate columns backed by a timestamp: datetime pickers, minute-precision comparison, day-range is. groupstringSection header in the column picker (e.g. the relation a borrowed column comes through). allowedOperatorsFilterOperators[type][]Restrict the operator list. operatorNotestringOne line shown under a restricted operator list (already translated).
Field Type Description labelstringRequired. Display label.valuestringRequired. Stored value.secondaryLabelstringDimmer second line. iconstring | ReactElement | ElementTypeDocyrusIcon name, element or component. imageUrlstringAvatar image. colorstringColor dot (status / enum options).
Field Type Default Description load({ search, page, pageSize, signal }) => Promise<{ items: ColumnOption[]; hasMore?: boolean }>— Required. Page loader; forward signal to your HTTP client.pageSizenumber25Items per page. debounceMsnumber300Search debounce.
Field Type Description columnIdstringColumn id. typeColumnDataTypeColumn data type. operatorFilterOperators[type]Operator (see below). valuesFilterValues<type>Values: strings (text / option / uuid), numbers (number, or [N] for X-days operators), Dates (date), booleans.
Type Operators textcontains, does not contain, is, is not, starts with, ends with, is empty, is not emptynumberis, is not, is less than, is less than or equal to, is greater than, is greater than or equal to, is between, is not between, is empty, is not emptydate (calendar)is, is not, is before, is on or after, is after, is on or before, is between, is not between, is empty, is not emptydate (relative)today, tomorrow, yesterday, last7/15/30/60/90/120Days, next7/15/30/60/90/120Days, lastWeek, thisWeek, nextWeek, lastMonth, thisMonth, nextMonth, beforeToday, afterToday, lastYear, thisYear, nextYear, first/second/third/fourthQuarter, last3Months, last6Months, and the N-day family xDaysAgo, xDaysLater, beforeLastXDays, inLastXDays, afterLastXDays, inNextXDaysoptionis, is not, is any of, is none of, is empty, is not emptymultiOptioninclude, exclude, include any of, include all of, exclude if any of, exclude if all, is empty, is not emptybooleanis, is not, is empty, is not emptyuuidis, is not, is empty, is not empty (exact match only; a rule is only sent for a complete UUID)
Step Native control Column Searchable list sectioned by group; typing 2+ characters also lists matching option values across option columns, which toggle directly (web "quick search"). Operator Every operator of the type (date: Calendar / Relative sections), honouring allowedOperators + operatorNote. Value-less operators apply immediately. Value — text / uuid Text input (uuid shows an "incomplete UUID" hint). Value — number One input, or Min / Max inputs for is between / is not between. Faceted min / max shown as placeholders. Value — date DateTimePicker (date or datetime by includeTime); From / To pickers (or DateTimeRangePicker with time) for ranges; preset chips (Today, Yesterday, Last 7 / 30 days, This week / month / year) switch is → is between; N input for X-days operators.Value — option / multiOption Searchable check list with faceted counts, color dots, icons, avatars; async columns load pages with "Load more". Several picks under is become is any of. Value — boolean True / False rows (trueLabel / falseLabel) that apply on tap.
Export Description createColumnConfigHelper<TData>()Fluent builder: .text() .number() .date() .option() .multiOption() .uuid() → .id() .accessor() .displayName() .icon() .min() .max() .options() .transformOptionFn() .orderFn() .build(). filterData(data, columns, filters, { combinator?, now?, weekStartsOn? })Native addition. Client-side filtering applying every operator (relative dates resolved against local time). evaluateFilter(cellValue, filter, options?)Native addition. Test one value against one FilterModel. filtersToServerFilter(filters, { columns?, combinator?, getRuleOptions? })Native addition. Backend-safe Docyrus filter group; getRuleOptions(columnId) sets field, arrayColumn, includeTime. filterToServerRule(filter, options?)One rule (or OR group) / null when unsendable. resolveRelativeDateWindow(operator, days?, options?)Inclusive { from?, to? } window for a relative-date operator. decodeHtmlEntities(text) / decodeOptionLabels(items)DOM-free entity decoding. determineNewOperator, filterTypeOperatorDetails, isRelativeDateOperator, isXDaysRelativeOperator, DEFAULT_OPERATORSOperator metadata (web parity). useFilterTranslation(locale)Translation function used by the component (ui.dataTableFilter.<key> → locale catalog → English).
Strings come from the bundled catalogs selected by locale (the web JSON files, verbatim). An app UiTranslationProvider overrides any of them under ui.dataTableFilter.<webKey> — e.g. ui.dataTableFilter.filter, ui.dataTableFilter.filters.date.today. Native-only strings: ui.dataTableFilter.apply (Apply), ui.dataTableFilter.back (Back), ui.dataTableFilter.removeFilter (Remove filter), ui.dataTableFilter.addFilter (Add filter), ui.dataTableFilter.or (or).
The native DataGrid toolbar's filter sheet is built on this engine (web DataGridFilterMenu parity): cell variants map to filter types, FilterModels are written to TanStack columnFilters as { operator, value, endValue } (web grid operator names such as isBetween, includesAny, last7Days), and the grid's default filterFn applies every operator in memory, relative dates included. Array-backed variants (multi-select, tag-select, user-multi-select) use the array operators; duration columns are filtered in hours.
Before Now columns: FilterColumn[] ({ id, label, type, options })columns from useDataTableFilters({ columnsConfig }) (ColumnConfig: id, accessor, displayName, type, options).filters: FilterItem[] ({ id, columnId, operator, value })filters: FiltersState ({ columnId, type, operator, values[] }).onFiltersChangeactions (from the hook) — or pass filters + onFiltersChange to the hook.strategy: 'and' | 'or'combinator + onCombinatorChange. strategy now means 'client' | 'server' (web).type select option (single) / multiOption (multiple).operators equals, contains, gt, lt, between, is_empty, is_not_empty Web operator names (is, contains, is greater than, is less than, is between, is empty, is not empty, …). styleRemoved — use className.
Component Description DataTableFilterFilter button, AND / OR toggle, active-filter pills and the wizard sheet.
Type Description DataTableFilterPropsComponent props. DataTableFilterCombinator'and' | 'or'.DataTableFiltersOptionsuseDataTableFilters options.DataTableFilterActionsFilter actions. Column / ColumnConfig / ColumnConfigHelper / DataTableFilterConfigColumn model. ColumnDataType / OptionBasedColumnDataTypeColumn data types. ColumnOption / ColumnOptionExtendedOption model (extended adds selected, count). AsyncOptionsConfigAsync option loader config. FilterModel / FiltersState / FilterValues / FilterStrategyFilter state. FilterOperators and TextFilterOperator, NumberFilterOperator, DateFilterOperator, OptionFilterOperator, MultiOptionFilterOperator, BooleanFilterOperator, UuidFilterOperatorOperator unions. FilterIconstring | ReactNode.Locale / FilterTranslateFni18n. EvaluateFilterOptions / RelativeDateOptions / RelativeDateWindowClient filtering. DocyrusFilterRule / DocyrusFilterGroup / ServerRuleOptions / ServerFilterOptionsServer serialization.