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

# List View - Web

> ListViews display a selectable list that can be customized, including displaying secondary actions and information.

> [!NOTE]
>
> For development, import `ListView` from
> `@servicetitan/anvil2`. For design, find it in the
> [Anvil2 file](https://www.figma.com/design/xaNQsvRKczoxu7pj0M6QT8/Anvil2-v3?node-id=2262-35823).

![](https://mintcdn.com/servicetitan/uz2PQSvO75TRhQ38/images/docs/web/components/shared/listview-main-image.png?fit=max\&auto=format\&n=uz2PQSvO75TRhQ38\&q=85\&s=883e5949d1e37902cd422d2730066e23)

## Anatomy

The List View consists of five primary elements that work together to display a selectable list with customization options.

![ListView
Anatomy](https://mintcdn.com/servicetitan/aip5_7K1pHSn1Axn/images/docs/web/components/list-view/design/listview-anatomy.png?fit=max\&auto=format\&n=aip5_7K1pHSn1Axn\&q=85\&s=9c54d65543a6b2f7c8187adfd8154e9f)

1. Unselected
2. Label
3. Selected
4. Secondary Actions
5. Description

## Options

The List View supports selection, secondary actions, disabled states, menus, and custom content configurations to accommodate various list scenarios.

### Selection

listview-defaultselected

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/listview-defaultselected?file=src/App.tsx)

```tsx lines theme={null}
import { ListView, Text } from "@servicetitan/anvil2";

function App() {
  return (
    <ListView onSelectionChange={console.log} defaultSelected={["Option2"]}>
      <ListView.Option label="Option">
        <Text flexGrow={1}>Option</Text>
      </ListView.Option>
      <ListView.Option label="Option2">
        <Text flexGrow={1}>Option 2</Text>
        <ListView.OptionCell></ListView.OptionCell>
      </ListView.Option>
    </ListView>
  );
}

export default App;
```

### Secondary action

listview-optioncell-button

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/listview-optioncell-button?file=src/App.tsx)

```tsx lines theme={null}
import { ListView, Text, Button } from "@servicetitan/anvil2";

function App() {
  return (
    <ListView onSelectionChange={console.log} defaultSelected={["Option2"]}>
      <ListView.Option label="Option">
        <Text flexGrow={1}>Option</Text>
        <Button onClick={console.log}>Button</Button>
      </ListView.Option>
      <ListView.Option label="Option2">
        <Text flexGrow={1}>Option 2</Text>
        <ListView.OptionCell>
          <Button onClick={console.log}>Button</Button>
        </ListView.OptionCell>
      </ListView.Option>
    </ListView>
  );
}

export default App;
```

### Disabled

listview-disabled

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/listview-disabled?file=src/App.tsx)

```tsx lines theme={null}
import { ListView, Text, Button } from "@servicetitan/anvil2";

function App() {
  return (
    <ListView onSelectionChange={console.log} defaultSelected={["Option2"]}>
      <ListView.Option label="Option">
        <Text flexGrow={1}>Option</Text>
        <Button onClick={console.log}>Button</Button>
      </ListView.Option>
      <ListView.Option label="Option2" disabled>
        <Text flexGrow={1}>Option 2</Text>
        <ListView.OptionCell>
          <Button onClick={console.log}>Button</Button>
        </ListView.OptionCell>
      </ListView.Option>
      <ListView.Option label="Option3">
        <Text flexGrow={1}>Option 3</Text>
        <ListView.OptionCell>
          <Button onClick={console.log}>Button</Button>
        </ListView.OptionCell>
      </ListView.Option>
    </ListView>
  );
}

export default App;
```

### Menu

listview-optioncell-menu

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/listview-optioncell-menu?file=src/App.tsx)

```tsx lines theme={null}
import { ListView, Flex, Menu } from "@servicetitan/anvil2";
import MenuIcon from "@servicetitan/anvil2/assets/icons/material/round/menu.svg";

function App() {
  return (
    <ListView>
      <ListView.Option label="Luke Skywalker">
        <Flex direction="column" grow="1">
          Luke Skywalker
        </Flex>
        <ListView.OptionCell>
          <Menu icon={MenuIcon} aria-label="menu">
            <Menu.Item label="Edit" onClick={() => console.log("Edit")} />
            <Menu.Item label="Delete" onClick={() => console.log("Delete")} />
          </Menu>
        </ListView.OptionCell>
      </ListView.Option>
      <ListView.Option label="Obi-wan Kenobi">
        <Flex direction="column" grow="1">
          Obi-wan Kenobi
        </Flex>
        <ListView.OptionCell>
          <Menu icon={MenuIcon} aria-label="menu">
            <Menu.Item label="Edit" onClick={() => console.log("Edit")} />
            <Menu.Item label="Delete" onClick={() => console.log("Delete")} />
          </Menu>
        </ListView.OptionCell>
      </ListView.Option>
      <ListView.Option label="Din Djarin">
        <Flex direction="column" grow="1">
          Din Djarin
        </Flex>
        <ListView.OptionCell>
          <Menu icon={MenuIcon} aria-label="menu">
            <Menu.Item label="Edit" onClick={() => console.log("Edit")} />
            <Menu.Item label="Delete" onClick={() => console.log("Delete")} />
          </Menu>
        </ListView.OptionCell>
      </ListView.Option>
    </ListView>
  );
}

export default App;
```

### Custom content

#### Avatar

listview-option-avatar

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/listview-option-avatar?file=src/App.tsx)

```tsx lines theme={null}
import { ListView, Avatar } from "@servicetitan/anvil2";

function App() {
  return (
    <ListView onSelectionChange={console.log}>
      <ListView.Option label="Luke Skywalker">
        <Avatar name="Luke Skywalker" />
        Luke Skywalker
      </ListView.Option>
      <ListView.Option label="Obi-wan Kenobi">
        <Avatar name="Obi-wan Kenobi" />
        Obi-wan Kenobi
      </ListView.Option>
      <ListView.Option label="Din Djarin">
        <Avatar name="Din Djarin" />
        Din Djarin
      </ListView.Option>
    </ListView>
  );
}

export default App;
```

#### Description

listview-option-description

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/listview-option-description?file=src/App.tsx)

```tsx lines theme={null}
import { ListView, Flex, Text } from "@servicetitan/anvil2";

function App() {
  return (
    <ListView onSelectionChange={console.log}>
      <ListView.Option label="Luke Skywalker">
        <Flex direction="column" grow="1">
          Luke Skywalker
          <Text size="small" subdued>
            luke@servicetitan.com
          </Text>
        </Flex>
      </ListView.Option>
      <ListView.Option label="Obi-wan Kenobi">
        <Flex direction="column" grow="1">
          Obi-wan Kenobi
          <Text size="small" subdued>
            kenobi@servicetitan.com
          </Text>
        </Flex>
      </ListView.Option>
      <ListView.Option label="Din Djarin">
        <Flex direction="column" grow="1">
          Din Djarin
          <Text size="small" subdued>
            mando@servicetitan.com
          </Text>
        </Flex>
      </ListView.Option>
    </ListView>
  );
}

export default App;
```

## Behavior

The List View responds to content overflow by wrapping labels to new lines while maintaining consistent list structure.

### Overflow

listview-option-overflow

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/listview-option-overflow?file=src/App.tsx)

```tsx lines theme={null}
import { ListView, Text } from "@servicetitan/anvil2";

function App() {
  return (
    <div style={{ width: "23rem" }}>
      <ListView onSelectionChange={console.log} defaultSelected={["Option2"]}>
        <ListView.Option label="Option">
          <Text flexGrow={1}>
            Here is an example of how overflow is handled for ListView.
          </Text>
        </ListView.Option>
        <ListView.Option label="Option2">
          <Text flexGrow={1}>
            Here is another example of how overflow is handled for ListView.
          </Text>
          <ListView.OptionCell></ListView.OptionCell>
        </ListView.Option>
      </ListView>
    </div>
  );
}

export default App;
```

When a List View label is too long for the available space, it wraps to a new line.

## Usage Guidelines

| Standard | Requirement |
| - | - |
| DoBuild lists of selectable items | List Views are used to construct lists of selectable items — for actions, navigation, and selections. |
| DoEmphasize multi-select with checkboxes | List Views are useful for emphasizing multiple selection with checkboxes. |
| DoAdd secondary actions to list items | Use List View when your list requires secondary actions, in addition to selecting multiple items or interacting with checkboxes. Unlike other options such as Listboxes, Comboboxes, or Side Nav menus, List View provides the functionality for these additional actions. |

### Alternatives

#### List View vs Listbox

Use Listbox when using simple selections that do not require any customization. Use List View when requiring customized styling or secondary actions. List View may also be used when emphasizing to users that multi-selection is possible.

### How to Use

#### Choosing between secondary actions and menus

Choose secondary actions or menus based on the number of actions per item and their frequency of use.

listview-optioncell-button

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/listview-optioncell-button?file=src/App.tsx)

```tsx lines theme={null}
import { ListView, Text, Button } from "@servicetitan/anvil2";

function App() {
  return (
    <ListView onSelectionChange={console.log} defaultSelected={["Option2"]}>
      <ListView.Option label="Option">
        <Text flexGrow={1}>Option</Text>
        <Button onClick={console.log}>Button</Button>
      </ListView.Option>
      <ListView.Option label="Option2">
        <Text flexGrow={1}>Option 2</Text>
        <ListView.OptionCell>
          <Button onClick={console.log}>Button</Button>
        </ListView.OptionCell>
      </ListView.Option>
    </ListView>
  );
}

export default App;
```

Use secondary actions when each item has one or two common actions that users access frequently. Secondary actions provide immediate access without additional clicks, making them ideal for primary workflows like editing or deleting items.

listview-optioncell-menu

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/listview-optioncell-menu?file=src/App.tsx)

```tsx lines theme={null}
import { ListView, Flex, Menu } from "@servicetitan/anvil2";
import MenuIcon from "@servicetitan/anvil2/assets/icons/material/round/menu.svg";

function App() {
  return (
    <ListView>
      <ListView.Option label="Luke Skywalker">
        <Flex direction="column" grow="1">
          Luke Skywalker
        </Flex>
        <ListView.OptionCell>
          <Menu icon={MenuIcon} aria-label="menu">
            <Menu.Item label="Edit" onClick={() => console.log("Edit")} />
            <Menu.Item label="Delete" onClick={() => console.log("Delete")} />
          </Menu>
        </ListView.OptionCell>
      </ListView.Option>
      <ListView.Option label="Obi-wan Kenobi">
        <Flex direction="column" grow="1">
          Obi-wan Kenobi
        </Flex>
        <ListView.OptionCell>
          <Menu icon={MenuIcon} aria-label="menu">
            <Menu.Item label="Edit" onClick={() => console.log("Edit")} />
            <Menu.Item label="Delete" onClick={() => console.log("Delete")} />
          </Menu>
        </ListView.OptionCell>
      </ListView.Option>
      <ListView.Option label="Din Djarin">
        <Flex direction="column" grow="1">
          Din Djarin
        </Flex>
        <ListView.OptionCell>
          <Menu icon={MenuIcon} aria-label="menu">
            <Menu.Item label="Edit" onClick={() => console.log("Edit")} />
            <Menu.Item label="Delete" onClick={() => console.log("Delete")} />
          </Menu>
        </ListView.OptionCell>
      </ListView.Option>
    </ListView>
  );
}

export default App;
```

Use menus when items have multiple actions or when actions are less frequently used. Menus reduce visual clutter and organize actions hierarchically, making them suitable for complex item management scenarios.

#### Organizing lists with custom content

listview-option-avatar

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/listview-option-avatar?file=src/App.tsx)

```tsx lines theme={null}
import { ListView, Avatar } from "@servicetitan/anvil2";

function App() {
  return (
    <ListView onSelectionChange={console.log}>
      <ListView.Option label="Luke Skywalker">
        <Avatar name="Luke Skywalker" />
        Luke Skywalker
      </ListView.Option>
      <ListView.Option label="Obi-wan Kenobi">
        <Avatar name="Obi-wan Kenobi" />
        Obi-wan Kenobi
      </ListView.Option>
      <ListView.Option label="Din Djarin">
        <Avatar name="Din Djarin" />
        Din Djarin
      </ListView.Option>
    </ListView>
  );
}

export default App;
```

Use custom content to provide visual context and additional information within list items. Avatars, descriptions, and other custom elements help users quickly identify and understand each option. Keep custom content focused and scannable to maintain list clarity and usability.

## Content

Content within the List View should clearly communicate each option and provide necessary context through labels and descriptions.

## Keyboard Interaction

Users can navigate the List View using standard keyboard controls.

## Code

**Implementation**

listview-playground

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/listview-playground?file=src/App.tsx)

```tsx lines expandable theme={null}
import { ListView, Avatar } from "@servicetitan/anvil2";

function App() {
  return (
    <ListView onSelectionChange={console.log}>
      <ListView.Option label="Luke Skywalker">
        <Avatar name="Luke Skywalker" />
        Luke Skywalker
      </ListView.Option>
      <ListView.Option label="Obi-wan Kenobi">
        <Avatar name="Obi-wan Kenobi" />
        Obi-wan Kenobi
      </ListView.Option>
      <ListView.Option label="Din Djarin">
        <Avatar name="Din Djarin" />
        Din Djarin
      </ListView.Option>
    </ListView>
  );
}

export default App;
```

## Common Examples

```tsx theme={null}
import { ListView, Text } from "@servicetitan/anvil2";

function ExampleComponent() {
  return (
    <ListView>
      <ListView.Option label="first">
        <Text>First Value</Text>
      </ListView.Option>
      <ListView.Option label="second">
        <Text>Second Value</Text>
      </ListView.Option>
      <ListView.Option label="third">
        <Text>Third Value</Text>
      </ListView.Option>
    </ListView>
  );
}
```

### Creating selectable list views

The `ListView` component can be used to easily create selectable lists using the `label` prop and custom content as children of each `ListView.Option`.

listview-label

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/listview-label?file=src/App.tsx)

```tsx lines expandable theme={null}
import { ListView, Text } from "@servicetitan/anvil2";

function App() {
  return (
    <ListView>
      <ListView.Option label="first">
        <Text>First Value</Text>
      </ListView.Option>
      <ListView.Option label="second">
        <Text>Second Value</Text>
      </ListView.Option>
      <ListView.Option label="third">
        <Text>Third Value</Text>
      </ListView.Option>
    </ListView>
  );
}

export default App;
```

### Indeterminate list views

The `indeterminate` prop can be used when the selection of items is controlled to render the checkbox as indeterminate, instead of checked or not checked. This is useful when building a nested structure.

**Note:** the logic to determine which items are indeterminate is not included, since there are various use cases. Simply pass the items that should be indeterminate to the prop.

listview-indeterminate

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/listview-indeterminate?file=src/App.tsx)

```tsx lines expandable theme={null}
import { ListView, Text } from "@servicetitan/anvil2";

type Item = {
  id: string;
  label: string;
  disabled?: boolean;
  parentId?: string;
};

function App() {
  const listItems: Item[] = [
    { id: "dt", label: "Design Technologists" },
    { id: "ben", label: "Ben Ho", parentId: "dt" },
    { id: "helen", label: "Helen Holmes", parentId: "dt" },
    { id: "james", label: "James Coyle", parentId: "dt" },
    { id: "nick", label: "Nick Sands", parentId: "dt" },
    { id: "uie", label: "UI Engineers" },
    { id: "adam", label: "Adam Lantz", parentId: "uie" },
    { id: "derek", label: "Derek Watson", parentId: "uie" },
    { id: "erica", label: "Erica Gugliemella", parentId: "uie" },
    { id: "patrick", label: "Patrick Buckingham", parentId: "uie" },
    { id: "ryan", label: "Ryan De La Torre", parentId: "uie" },
  ];

  return (
    <ListView
      items={listItems}
      // in reality, these should be managed by separate logic/state
      selected={[listItems[1], listItems[3]]}
      indeterminate={[listItems[0]]}
    >
      {(_items) =>
        listItems.map((item) => (
          <ListView.Option
            key={item.label}
            item={item}
            disabled={item.disabled}
            style={{
              marginLeft: item.parentId ? "1rem" : undefined,
            }}
          >
            <Text flexGrow={1}>{item.label}</Text>
          </ListView.Option>
        ))
      }
    </ListView>
  );
}

export default App;
```

### Using items objects with list views

The `items` prop of a `ListView` can also be passed an array of objects which are passed to the children of the list view. Each item object should have a `label`, but any other arbitrary attributes are allowed.

```tsx theme={null}
const Items = [
  { label: "Item 1", imgUrl: "#" },
  { label: "Item 2", imgUrl: "#", disabled: true },
  { label: "Item 3" },
];

export const ListViewExample = () => (
  <ListView items={Items}>
    {(items) =>
      items.map((item) => (
        <ListView.Option key={item.label} item={item} disabled={item.disabled}>
          <Avatar image={item.imgUrl} name={item.label} />
          <Text>{item.label}</Text>
        </ListView.Option>
      ))
    }
  </ListView>
);
```

listview-children

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/listview-children?file=src/App.tsx)

```tsx lines expandable theme={null}
import { ListView, Text } from "@servicetitan/anvil2";

type Item = {
  email: string;
  label: string;
  disabled?: boolean;
};

function App() {
  const listItems: Item[] = [
    { label: "Ben Ho", email: "ben@servicetitan" },
    { label: "Derek Watson", email: "derek@servicetitan" },
    { label: "James Coyle", email: "james@servicetitan" },
    {
      label: "Jess Paris",
      email: "jess@servicetitan",
      disabled: true,
    },
    { label: "Nick Sands", email: "nick@servicetitan" },
    { label: "Patrick Buckingham", email: "patrick@servicetitan" },
    { label: "Ryan De La Torre", email: "ryan@servicetitan" },
  ];

  return (
    <ListView items={listItems}>
      {(_items) =>
        listItems.map((item) => (
          <ListView.Option
            key={item.label}
            item={item}
            disabled={item.disabled}
          >
            <Text flexGrow={1}>{item.label}</Text>
          </ListView.Option>
        ))
      }
    </ListView>
  );
}

export default App;
```

**ListView Props**

```tsx theme={null}
<ListView
  items={itemsArray}
  defaultSelected={[]}
  selected={[]}
  indeterminate={[]}
  onSelectionChange={(value) => console.log(value)}
>
  {(items) =>
    items.map((item) => (
      <ListView.Option key={item.label} item={item}>
        {item.label}
      </ListView.Option>
    ))
  }
</ListView>
```

## `ListView` Props

In addition to the props listed below, the `ListView` component can accept any valid HTML `div` props.

**onSelectionChange** `(value: unknown[]) =\> void` *required*

Callback when the selection changes.

**children** `(items) =\> ReactNode (items prop only)  ReactNode (without items prop)`

If the `items` prop is used, `ListView`'s children will be a function that
passes the items.

**defaultSelected** `ItemType[] (items prop only)  string[] (without items prop)`

**indeterminate** `ItemType[]`

Items passed to this prop will render with an indeterminate checkbox
(controlled)

**items** `ItemType[]`

`ListView` can be used with or without an `items` prop to manage your list of
items

**selected** `ItemType[]`

Items passed to this prop will render with an checked checkbox (controlled)

**ListView\.Option Props**

```tsx theme={null}
<ListView.Option
  item={item}
  label="Option 1"
  disabled={false}
  onChange={(e) => console.log("Changed")}
>
  Option content
</ListView.Option>
```

## `ListView.Option` Props

In addition to the props listed below, the `ListView.Option` component can accept any valid HTML `div` props.

**disabled** `boolean`

**item** `\{ label: string, defaultSelected?: boolean, [keys]: values \}`

Required if no `label` prop is supplied.

**label** `string`

Required if no `item` prop is supplied.

**onChange** `(e: ChangeEvent) =\> void`


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.