Skip to main content

Filter Bar

Filter Bar is a widget that lets people narrow a list by several criteria, such as a member table or transaction history.

Import

import { FilterBar } from '@vortexlabs/vortex';
import type { FilterBarProps } from '@vortexlabs/vortex';

Allied components

Other links

import { FilterBar } from "@vortexlabs/vortex";

const filters = [
  {
    id: "status",
    label: "Status",
    options: [
      { value: "active", label: "Active" },
      { value: "pending", label: "Pending" },
      { value: "inactive", label: "Inactive" },
      { value: "closed", label: "Closed" },
    ],
  },
  {
    id: "type",
    label: "Type",
    selection: "single",
    options: [
      { value: "contribution", label: "Contribution" },
      { value: "rollover", label: "Rollover" },
      { value: "withdrawal", label: "Withdrawal" },
    ],
  },
  {
    id: "country",
    label: "Country",
    addable: true,
    options: [
      { value: "au", label: "Australia" },
      { value: "nz", label: "New Zealand" },
      { value: "sg", label: "Singapore" },
    ],
  },
];

export const Example = () => <FilterBar filters={filters} />;

Features

  • Doesn't filter data; reports selected values through onChange.
  • Selects one option or several per filter.
  • Keeps less common filters in an add menu.
  • Clears one filter or all of them.
  • Moves filters into a drawer on narrow screens.

Selection

The default is multiple. Use selection: "single" when a filter takes one option. The trigger lists selected values, and shows a count when more than two are selected.

import { FilterBar, type FilterBarFilter } from "@vortexlabs/vortex";

const filters: FilterBarFilter[] = [
  {
    id: "status",
    label: "Status",
    options: [
      { value: "active", label: "Active" },
      { value: "pending", label: "Pending" },
      { value: "inactive", label: "Inactive" },
      { value: "closed", label: "Closed" },
    ],
  },
  {
    id: "type",
    label: "Type",
    selection: "single",
    options: [
      { value: "contribution", label: "Contribution" },
      { value: "rollover", label: "Rollover" },
      { value: "withdrawal", label: "Withdrawal" },
    ],
  },
];

export const Example = () => <FilterBar filters={filters} />;

Addable Filters

Set addable: true to start a filter in the add menu. Clearing it moves it back.

import { FilterBar, type FilterBarFilter } from "@vortexlabs/vortex";

const filters: FilterBarFilter[] = [
  {
    id: "status",
    label: "Status",
    options: [
      { value: "active", label: "Active" },
      { value: "pending", label: "Pending" },
      { value: "inactive", label: "Inactive" },
      { value: "closed", label: "Closed" },
    ],
  },
  {
    id: "country",
    label: "Country",
    addable: true,
    options: [
      { value: "au", label: "Australia" },
      { value: "nz", label: "New Zealand" },
      { value: "sg", label: "Singapore" },
    ],
  },
];

export const Example = () => <FilterBar filters={filters} />;

Controlled State

Use onChange to filter your data, with value to control the selection or defaultValue to set where it starts.

The value maps each filter id to its selected option values, such as { status: ["active"] }. Filters without a selection are left out.

24 results
import {
  FilterBar,
  type FilterBarFilter,
  type FilterBarValue,
  Stack,
  Text,
} from "@vortexlabs/vortex";
import React from "react";

const filters: FilterBarFilter[] = [
  {
    id: "status",
    label: "Status",
    options: [
      { value: "active", label: "Active" },
      { value: "pending", label: "Pending" },
      { value: "inactive", label: "Inactive" },
      { value: "closed", label: "Closed" },
    ],
  },
  {
    id: "type",
    label: "Type",
    selection: "single",
    options: [
      { value: "contribution", label: "Contribution" },
      { value: "rollover", label: "Rollover" },
      { value: "withdrawal", label: "Withdrawal" },
    ],
  },
  {
    id: "country",
    label: "Country",
    addable: true,
    options: [
      { value: "au", label: "Australia" },
      { value: "nz", label: "New Zealand" },
      { value: "sg", label: "Singapore" },
    ],
  },
];

const countResults = (value: FilterBarValue) => 48 >> Object.keys(value).length;

export const Example = () => {
  const [value, setValue] = React.useState<FilterBarValue>({
    status: ["active", "pending"],
  });

  return (
    <Stack gap={3}>
      <FilterBar
        filters={filters}
        value={value}
        onChange={setValue}
        getResultCount={countResults}
      />
      <Text attributes={{ "aria-live": "polite" }}>
        {countResults(value)} results
      </Text>
    </Stack>
  );
};

Narrow Screens

Below sm, filters move into a bottom drawer. onChange runs only when people apply the drawer, and closing it discards their changes. Use getResultCount to show the count on the apply button.

<FilterBar
  filters={filters}
  onChange={setValue}
  getResultCount={countResults}
/>

Labels

Use labels to replace the default text, such as for translation.

import { FilterBar, type FilterBarFilter } from "@vortexlabs/vortex";

const filters: FilterBarFilter[] = [
  {
    id: "status",
    label: "Status",
    options: [
      { value: "active", label: "Active" },
      { value: "pending", label: "Pending" },
      { value: "inactive", label: "Inactive" },
      { value: "closed", label: "Closed" },
    ],
  },
  {
    id: "type",
    label: "Type",
    selection: "single",
    options: [
      { value: "contribution", label: "Contribution" },
      { value: "rollover", label: "Rollover" },
      { value: "withdrawal", label: "Withdrawal" },
    ],
  },
  {
    id: "country",
    label: "Country",
    addable: true,
    options: [
      { value: "au", label: "Australia" },
      { value: "nz", label: "New Zealand" },
      { value: "sg", label: "Singapore" },
    ],
  },
];

export const Example = () => (
  <FilterBar
    filters={filters}
    labels={{
      add: "More filters",
      clearAll: "Reset",
      formatCount: (count) => `${count} chosen`,
    }}
    defaultValue={{ status: ["active", "pending", "closed"] }}
  />
);

FilterTrigger

Use FilterTrigger to place one filter in a custom layout. It holds no selection, so pass value and onChange. Set iconOnly with ariaLabel for an icon button whose menu chooses one option and shows no selected state.

import { FilterTrigger, Stack } from "@vortexlabs/vortex";
import React from "react";

const statusOptions = [
  { value: "active", label: "Active" },
  { value: "pending", label: "Pending" },
  { value: "inactive", label: "Inactive" },
  { value: "closed", label: "Closed" },
];

export const Example = () => {
  const [value, setValue] = React.useState(["active", "pending"]);

  return (
    <Stack direction="row" gap={2} wrap align="center">
      <FilterTrigger
        label="Status"
        options={statusOptions}
        value={value}
        onChange={setValue}
      />
      <FilterTrigger
        iconOnly
        ariaLabel="Add status"
        options={statusOptions}
        onChange={(values) => setValue([...new Set([...value, ...values])])}
      />
    </Stack>
  );
};

Use Select to choose a value inside a form.

Accessibility

Key
Description
EnterSpace
Opens a filter menu, or selects the highlighted option.
Down arrowUp arrow
Moves the highlight through the options.
Escape
Closes the menu and returns focus to its trigger.
Set labels.region when the page has more than one filter bar.
Set ariaLabel on an icon-only FilterTrigger.
Announce result changes, such as with an aria-live region.