Skip to Content
UI KitAKI ComponentsAkifilter

Overview

Akifilter component from the @akinon/akifilter package It is a customizable filter component for React applications and provides advanced filtering features.

Example Usage

import { Akifilter, type AkifilterSchema } from '@akinon/akifilter'; const filterSchema: AkifilterSchema = [ { key: 'username', type: 'text', label: 'Username', placeholder: 'Enter username' }, { key: 'status', type: 'select', label: 'Status', placeholder: 'Select status', options: [ { value: 'active', label: 'Active' }, { value: 'inactive', label: 'Inactive' } ] }, { key: 'isActive', type: 'checkbox', label: 'Is Active' } ]; const MyComponent = () => { const handleValuesChange = (values: Record<string, unknown>) => { console.log('Filter values changed:', values); // Use values to fetch data, update URL params, etc. }; return ( <Akifilter filterSchema={filterSchema} storageNamespace="users-filter" onValuesChange={handleValuesChange} defaultValues={{ status: 'active' }} /> ); };

Akifilter Props

Akifilter props provide various options for configuring the filter component:

PropertyDescriptionTypeDefault
filterSchemaDeclarative description of the filter fieldsAkifilterSchema[]
storageNamespaceOptional namespace for local storage persistence. Filters are saved and restored automatically.string-
defaultValuesDefault values supplied by the host applicationPartial<TFieldValues>-
onValuesChangeCallback fired on every filter value change with the normalised payload(values: Partial<T>) => void-
onVisibleFieldsChangeCallback fired whenever visible field keys change(keys: string[]) => void-
onImportCsvCallback triggered when user clicks the CSV import button() => void-
onImportXlsCallback triggered when user clicks the XLS/XLSX import button() => void-
onClearAllCallback triggered when user requests clearing all filters() => void-
enableImportCsvShows the CSV import button in the toolbarbooleanfalse
enableImportXlsShows the XLS/XLSX import button in the toolbarbooleanfalse
filterActionsRefRef to access imperative filter actions like clearing specific filter values programmaticallyReact.Ref<AkifilterActionsRef>-
filterButtonsQuick-filter toggle buttons rendered to the left of the active filters barAkifilterButton[][]
onFilterButtonsChangeCallback fired whenever the set of active filter buttons changes(active: AkifilterButton[]) => void-

Storage Behavior

When storageNamespace is provided, Akifilter automatically persists filter values and field visibility preferences to local storage:

  • Filter values are saved on change (debounced)
  • Visible field selections are saved when toggled
  • Selectable sections persist their visible-field selection under a dedicated per-section storage key (see Section Fields)
  • Values are restored on component mount
  • Storage key is generated based on schema structure and namespace

This enables users to maintain their filter preferences across sessions.

Filter Buttons

The filterButtons prop renders a row of icon-only quick-filter toggle buttons to the left of the active filters bar. They are useful for one-click filters such as filtering products by type. Buttons live outside the filterSchema form, so they can be used with or without form fields.

import { Akifilter, type AkifilterButton, type AkifilterSchema } from '@akinon/akifilter'; const filterSchema: AkifilterSchema = [ { key: 'base_code', type: 'text', label: 'Base Code', config: { visible: true } }, { key: 'sku', type: 'text', label: 'SKU', config: { visible: true } } ]; const filterButtons: AkifilterButton[] = [ { key: 'product_type', value: '01', label: 'Simple', icon: 'simple_product', appliedPrefix: 'Product Type' }, { key: 'product_type', value: '02', label: 'Meta', icon: 'variant_product', appliedPrefix: 'Product Type' } ]; const MyComponent = () => { return ( <Akifilter filterSchema={filterSchema} filterButtons={filterButtons} storageNamespace="product-pool" onValuesChange={values => console.log('Values:', values)} onFilterButtonsChange={active => console.log('Active buttons:', active)} /> ); }; // Activating Simple + Meta emits: { product_type: ['01', '02'] }

AkifilterButton API

PropertyDescriptionTypeRequired
keyFilter key the button contributes to when active (e.g. 'product_type'). Must be distinct from your filterSchema field keysstringYes
valueValue applied to key when active. Also the button’s identity, so it must be unique across the whole filterButtons arraystring | numberYes
labelHuman-readable name shown in the button tooltip and as the value part of the applied chipstringYes
appliedPrefixPrefix shown before label in the applied chip, rendered as "{appliedPrefix}: {label}"stringYes
iconIcon (from @akinon/icons) rendered inside the buttonIconNameYes
singleChoiceWhen true, activating this button deactivates every other buttonbooleanNo

Behavior

  • Value merging: Active button values are merged into the onValuesChange payload under each button’s key. Buttons that share a key are combined into an array (e.g. { product_type: ['simple', 'variant'] }); buttons with different keys emit under their own keys.
  • Key collisions: Button values are merged after the form values. So if a button key matches a filterSchema field key, the button value wins and overwrites whatever the form field put in the payload. Keep button keys distinct from schema field keys, otherwise a form selection can get dropped with no warning.
  • Applied chips: Each active button appears as a removable chip in the active filters bar ({appliedPrefix}: {label}). Removing the chip deactivates the button, and Clear All deactivates all buttons.
  • Persistence: When storageNamespace is provided, active buttons are persisted to local storage alongside the other filters and restored on mount.
  • Single choice: Set singleChoice: true on buttons that should behave like a radio group — activating one deactivates the others.

Provide filterButtons as a referentially stable array (a module constant or memoized value) that is available on first render. Keep each button key distinct from your filterSchema field keys, and each value unique across all buttons.

Field Configuration

Each field in filterSchema supports an optional config property that enables advanced control over field behavior:

const filterSchema: AkifilterSchema = [ { key: 'status', type: 'select', label: 'Status', placeholder: 'Select status', options: [ { value: 'active', label: 'Active' }, { value: 'inactive', label: 'Inactive' } ], config: { disabled: true, // Field is disabled visible: true // Field is visible } } ];

config.disabled

Controls whether a filter field is disabled. Can be either:

  • Boolean: Static disable state

    config: { disabled: true; } // Field always disabled
  • Function: Dynamic disable state based on other field values

    config: { disabled: formValues => !formValues.status; // Disable when status is empty }

config.visible

Controls whether a filter field is displayed. Can be either:

  • Boolean: Static visibility state

    config: { visible: false; } // Field always hidden
  • Function: Dynamic visibility based on other field values

    config: { visible: formValues => formValues.status === 'admin'; // Show only if admin status }
  • Function with currentVisible: The callback receives an optional second parameter currentVisible (boolean | undefined) indicating whether the field is currently visible (based on the user’s field visibility selection). This allows you to combine dynamic logic with the user’s visibility preference.

    config: { visible: (formValues, currentVisible) => { // Keep the field visible if the user has toggled it on, // otherwise show only when status is 'error' return currentVisible ?? formValues.status === 'error'; }; }

Section Fields

Use type: 'section' to group related filters into a collapsible panel rendered below the main filter grid. A section owns its own set of fields via the fields property.

import { Akifilter, type AkifilterSchema } from '@akinon/akifilter'; const filterSchema: AkifilterSchema = [ { key: 'orderNo', type: 'text', label: 'Order No', config: { visible: true } }, { key: 'advancedFilters', type: 'section', label: 'Advanced Filters', defaultExpanded: false, // Start collapsed fields: [ { key: 'customerName', type: 'text', label: 'Customer Name' }, { key: 'amount', type: 'number', label: 'Amount' } ] } ];

Section API

PropertyDescriptionTypeDefault
typeMust be 'section''section'-
labelSection title rendered in the collapsible headerstring-
fieldsFields belonging to the section (any regular field type)AkifilterField[]-
defaultExpandedWhether the section starts expandedbooleantrue
isExcludeSectionStyles the section’s active chips as exclude filters (see below)booleanfalse
selectableFieldsAdds a funnel icon + visibility modal to the section header (see below)booleanfalse

Section field values are part of the same form payload as regular fields, so they appear in onValuesChange and as active-filter chips just like top-level fields. Section fields are not listed in the top-level “Select filters” modal — their visibility is managed per section via selectableFields.

isExcludeSection

Set isExcludeSection: true to mark a section as exclude filters. The active filter chips of that section’s fields are visually distinguished (red background, red border, and an exclude icon) so users can tell exclusion rules apart from regular filters.

const filterSchema: AkifilterSchema = [ { key: 'sku', type: 'text', label: 'SKU', config: { visible: true } }, { key: 'excludeSection', type: 'section', label: 'Exclude Filters', isExcludeSection: true, fields: [ { key: 'excludeSku', type: 'text', label: 'Exclude SKU' }, { key: 'excludeStatus', type: 'select', label: 'Exclude Status', options: [ { value: 'pending', label: 'Pending' }, { value: 'completed', label: 'Completed' } ] } ] } ];

selectableFields (funnel icon + visibility modal)

Set selectableFields: true to give a section its own funnel icon in the header. Clicking it opens a visibility modal that lets users choose which of the section’s fields are shown — the same field-selection experience as the top-level filters. This is ideal for sections with many fields (for example, attribute-type filters) where showing all of them at once would be overwhelming.

import { Akifilter, type AkifilterSchema } from '@akinon/akifilter'; const attributeFields = Array.from({ length: 20 }, (_, index) => ({ key: `attr_${index + 1}`, type: 'text' as const, label: `Attribute ${index + 1}` })); const filterSchema: AkifilterSchema = [ { key: 'sku', type: 'text', label: 'SKU', config: { visible: true } }, { key: 'attributeSection', type: 'section', label: 'Attribute Filters', selectableFields: true, fields: attributeFields }, { // Combine with isExcludeSection for selectable exclude filters key: 'excludeAttributeSection', type: 'section', label: 'Exclude Attribute Filters', isExcludeSection: true, selectableFields: true, defaultExpanded: false, fields: attributeFields } ];

Behavior of a selectable section:

  • Header layout: the header renders as Title → arrow → funnel button, with the funnel pinned to the far right. Clicking the funnel opens the modal without toggling the collapse; clicking anywhere else in the header expands/collapses the section.
  • Default visibility: fields with config.visible: true are shown; otherwise the first 8 fields are shown by default and the rest are opt-in through the funnel — mirroring the top-level filter behavior.
  • Search & pagination: the modal supports searching fields by name and paginates long lists.
  • Value cleanup: hiding a field clears its value and removes its active chip, exactly like the top-level field selection.
  • Dynamic visibility: config.visible boolean/function values (including the currentVisible argument) are honored inside selectable sections too.
  • Per-section persistence: when storageNamespace is provided, each section’s visible-field selection is persisted under its own storage key and restored on mount, independently of the top-level filters and other sections.

Set a unique storageNamespace per Akifilter instance. Section visibility storage keys are derived from the instance’s storage key + the section.key. The instance storage key is built from the top-level (non-section) fields and the namespace only — section fields do not contribute to it. So two instances that both omit storageNamespace and have no top-level fields (section-only schemas) resolve to the same key, and sections that share a key will leak their visibility selections into each other. Giving each instance a distinct storageNamespace keeps these selections isolated.

Default Values Behavior

When using the filterSchema with defaultValue properties, note that:

  1. defaultValue in schema: Used as the schema default value
  2. defaultValues prop: Passed as initial values to the filter form
  3. Clear All action: Resets fields to schema defaultValue only (not external defaultValues)

This allows distinction between schema-defined defaults and external initial values. When clearing filters, only the schema-defined defaults are applied.

import { Akifilter, type AkifilterSchema } from '@akinon/akifilter'; const filterSchema: AkifilterSchema = [ { key: 'region', type: 'select', label: 'Region', placeholder: 'Select region', defaultValue: 'all', // Schema default - used on clear options: [ { value: 'all', label: 'All Regions' }, { value: 'eu', label: 'Europe' }, { value: 'us', label: 'United States' } ] } ]; const MyComponent = () => { return ( <Akifilter filterSchema={filterSchema} storageNamespace="products-filter" defaultValues={{ region: 'eu' }} // Shows 'Europe' initially onValuesChange={values => console.log('Values:', values)} /> ); }; // When user clicks "Clear All", region resets to 'all' (schema default) // NOT to 'eu' (external defaultValues)

Date Field with Time

Date fields support the showTime property to include time selection:

const filterSchema: AkifilterSchema = [ { key: 'createdDate', type: 'date', label: 'Created Date', showTime: true // Includes time picker } ];

When showTime is enabled, the applied filter displays both date and time in localized format.

Custom Field Options

Custom fields (type: 'custom') can include an optional options array. This array is not used for rendering — the render function handles the UI. Instead, Akifilter uses it to resolve selected values into human-readable labels in the applied filters bar. This works for both single and multi-select values.

const filterSchema: AkifilterSchema = [ { key: 'category', type: 'custom', label: 'Category', options: [ { value: 'electronics', label: 'Electronics' }, { value: 'clothing', label: 'Clothing' }, { value: 'books', label: 'Books' } ], render: ({ field, formValues, control }) => ( <MyCustomSelect field={field} formValues={formValues} control={control} /> ) } ]; // Single value: applied filter shows "Electronics" instead of "electronics" // Multi-select value: applied filter shows "Electronics, Clothing" instead of "electronics,clothing"

Without options, applied filter chips display raw values. With options, labels are resolved automatically for both single values and arrays.

Imperative Actions with filterActionsRef

The filterActionsRef prop provides imperative control over filter values, allowing you to programmatically clear specific fields from outside the component:

import { useRef } from 'react'; import { Akifilter, type AkifilterActionsRef, type AkifilterSchema } from '@akinon/akifilter'; import { Button } from '@akinon/ui-button'; const filterSchema: AkifilterSchema = [ { key: 'orderNo', type: 'text', label: 'Order No', placeholder: 'Enter order number' }, { key: 'customerName', type: 'text', label: 'Customer Name', placeholder: 'Enter customer name' }, { key: 'status', type: 'select', label: 'Status', options: [ { value: 'pending', label: 'Pending' }, { value: 'completed', label: 'Completed' } ] } ]; const MyComponent = () => { const filterActionsRef = useRef<AkifilterActionsRef>(null); const handleClearOrderNo = () => { // Clear a single field filterActionsRef.current?.clearValue('orderNo'); }; const handleClearMultiple = () => { // Clear multiple fields at once filterActionsRef.current?.clearValue(['customerName', 'status']); }; return ( <div> <div style={{ marginBottom: 16, display: 'flex', gap: 8 }}> <Button onClick={handleClearOrderNo}>Clear Order No</Button> <Button onClick={handleClearMultiple}> Clear Customer Name & Status </Button> </div> <Akifilter filterSchema={filterSchema} filterActionsRef={filterActionsRef} onValuesChange={values => console.log('Values changed:', values)} /> </div> ); };

AkifilterActionsRef API

MethodParametersDescription
clearValuekeys: string | string[]Clears the value of one or more filter fields by their key(s)

Import Actions

Akifilter supports optional CSV and XLS/XLSX import buttons in the toolbar. These are hidden by default and can be enabled via props:

import { Akifilter, type AkifilterSchema } from '@akinon/akifilter'; const filterSchema: AkifilterSchema = [ { key: 'productId', type: 'text', label: 'Product ID' } ]; const MyComponent = () => { const handleImportCsv = () => { // Implement CSV import logic console.log('Import from CSV'); }; const handleImportXls = () => { // Implement XLS/XLSX import logic console.log('Import from XLS/XLSX'); }; return ( <Akifilter filterSchema={filterSchema} enableImportCsv={true} enableImportXls={true} onImportCsv={handleImportCsv} onImportXls={handleImportXls} onValuesChange={values => console.log('Values:', values)} /> ); };

When enabled, import buttons appear in the filter toolbar, allowing users to trigger custom import workflows.

Additional Callbacks

onClearAll

Triggered when the user clicks the “Clear All” button:

<Akifilter filterSchema={filterSchema} onClearAll={() => { console.log('All filters cleared'); // Perform additional cleanup if needed }} onValuesChange={values => console.log('Values:', values)} />

onVisibleFieldsChange

Triggered when the user changes which fields are visible in the filter:

<Akifilter filterSchema={filterSchema} onVisibleFieldsChange={visibleKeys => { console.log('Visible fields:', visibleKeys); // Track user preferences or analytics }} onValuesChange={values => console.log('Values:', values)} />