---
title: "ListToolbar"
description: "The controls above a list in the order every list keeps: the search, the filters with “Clear filters” while any is applied, the saved views and the column picker at the end, and a row of chips under it. On a phone the filters fold behind one button."
---

> Documentation Index
> Fetch the complete documentation index at: https://epidermis.getarbol.com/llms.txt
> Use this file to discover all available pages before exploring further.

# ListToolbar

## Search, filters and columns

```tsx
import { useState } from 'react';
import { ColumnPicker, FilterSelect, ListToolbar, SearchField, useColumnState } from '@arbol-ai/ui';

const NO_VALUES: string[] = [];

const COLUMNS = [
  { id: 'title', label: 'Conversation', canHide: false },
  { id: 'contact', label: 'Contact' },
  { id: 'agent', label: 'Agent' },
  { id: 'started', label: 'Started' },
];

const ConversationsToolbar = () => {
  const [q, setQ] = useState('');
  const [channels, setChannels] = useState<readonly string[]>(NO_VALUES);
  const [results, setResults] = useState<readonly string[]>(['missed']);
  const { onOrderChange, onReset, onVisibleChange, state } = useColumnState({ columns: COLUMNS });
  return (
    <ListToolbar
      activeFilterCount={[channels, results].filter((values) => values.length > 0).length}
      columns={
        <ColumnPicker
          columns={COLUMNS}
          onOrderChange={onOrderChange}
          onReset={onReset}
          onVisibleChange={onVisibleChange}
          state={state}
        />
      }
      filters={
        <>
          <FilterSelect
            label="Channel"
            onChange={setChannels}
            options={[
              { value: 'whatsapp', label: 'WhatsApp' },
              { value: 'voice', label: 'Phone' },
              { value: 'web', label: 'Web chat' },
            ]}
            selectionMode="multiple"
            value={channels}
          />
          <FilterSelect
            label="Result"
            onChange={setResults}
            options={[
              { value: 'resolved', label: 'Resolved' },
              { value: 'missed', label: 'Missed' },
            ]}
            value={results}
          />
        </>
      }
      onClearFilters={() => {
        setChannels(NO_VALUES);
        setResults(NO_VALUES);
      }}
      search={<SearchField onChange={setQ} placeholder="Search conversations" value={q} />}
    />
  );
};

<ConversationsToolbar />
```

## Chips, a records filter and saved views

The agent filter lists records with a face and a second line; the views menu is the app’s own, in the views slot.

```tsx
import { BookmarkSimpleIcon, CaretDownIcon } from '@phosphor-icons/react';
import { useState } from 'react';
import { Avatar, Button, FilterChips, FilterSelect, FilterToggle, ListToolbar, Menu, MenuItem, MenuTrigger, SearchField } from '@arbol-ai/ui';

const NO_VALUES: string[] = [];

const AGENTS = [
  { value: 'agt_1', label: 'Front desk', description: 'Answers every call' },
  { value: 'agt_2', label: 'Reminders', description: 'Confirms appointments' },
  { value: 'agt_3', label: 'Collections', description: 'Calls about payments' },
].map((agent) => ({ ...agent, icon: <Avatar name={agent.label} size="xs" /> }));

const PatientsToolbar = () => {
  const [q, setQ] = useState('');
  const [kind, setKind] = useState<'first' | 'returning' | null>(null);
  const [agents, setAgents] = useState<readonly string[]>(NO_VALUES);
  const [archived, setArchived] = useState(false);
  return (
    <ListToolbar
      activeFilterCount={[agents.length > 0, archived].filter(Boolean).length}
      chips={
        <FilterChips
          label="Kind of patient"
          onChange={setKind}
          options={[
            { label: 'First visit', value: 'first', count: 18 },
            { label: 'Returning', value: 'returning', count: 230 },
          ]}
          value={kind}
        />
      }
      filters={
        <>
          <FilterSelect
            allLabel="All agents"
            label="Agent"
            onChange={setAgents}
            options={AGENTS}
            value={agents}
          />
          <FilterToggle count={12} isSelected={archived} label="Archived" onChange={setArchived} />
        </>
      }
      onClearFilters={() => {
        setAgents(NO_VALUES);
        setArchived(false);
      }}
      search={<SearchField onChange={setQ} placeholder="Name, document or phone" value={q} />}
      views={
        <MenuTrigger>
          <Button variant="outline">
            <BookmarkSimpleIcon aria-hidden />
            Views
            <CaretDownIcon aria-hidden />
          </Button>
          <Menu placement="bottom end">
            <MenuItem>Booked by an agent today</MenuItem>
            <MenuItem>No show this week</MenuItem>
          </Menu>
        </MenuTrigger>
      }
    />
  );
};

<PatientsToolbar />
```

## Source

The examples above are [`src/blocks/list-toolbar/list-toolbar.examples.tsx`](https://github.com/getarbol/project-arbol-ui/blob/main/src/blocks/list-toolbar/list-toolbar.examples.tsx), which the library's tests render too.

Source: https://epidermis.getarbol.com/components/blocks/list-toolbar/index.mdx
