> ## 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.

# Drilldown - Web

> A Drilldown is a navigation pattern that allows users to view additional information within the same container. Drilldown is a helper that works with the Drawer, Dialog, and Page Panel components.

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

## Anatomy

![Anatomy of a
Drilldown](https://mintcdn.com/servicetitan/U8oLKbN3oa-wwaq2/images/docs/web/components/drilldown/drilldown_anatomy.png?fit=max\&auto=format\&n=U8oLKbN3oa-wwaq2\&q=85\&s=8cad538b0f90aa72a5c4b0c1be636627)

1. Back action
2. Drilldown header
3. Header actions (optional, Drawer and Page Panel only)
4. Close action (optional)
5. Drilldown content
6. Drilldown footer (optional)
7. Close action (optional)
8. Drilldown content
9. Drilldown footer (optional)

## Options

### In a Dialog

drilldown-options-dialog

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

### In a Drawer

drilldown-options-drawer

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

### In a Page Panel

drilldown-options-panel

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

### Header actions

Drilldown supports an optional overflow menu in the header for secondary operations related to the Drilldown content. Header actions appear to the left of the close action, or on their own when the Drilldown has no close action.

Each Drilldown defines its own header actions. A Drilldown does not inherit the header actions of the Drawer it opens from.

drilldown-header-actions-drawer

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

## Behaviors

### How the Drilldown gets opened

Drilldowns are opened by clicking on a trigger element within the parent UI.

### Dismissal

If the container is dismissed (e.g., clicking the backdrop or a close control), the entire UI closes. It does **not** step back; going back is reserved for the back action.

### Focus Management

Focus should mimic the corresponding UI which the Drilldown is present in. Refer to focus management documentation for Dialogs and Drawers for more information.

## Keyboard Interactions

Drilldown does not have any specific keyboard interactions. It should follow the keyboard conventions of the UI it is in.

## Usage Guidelines

| Standard | Requirement |
| - | - |
| DoUse as progressive disclosure | Use as progressive disclosure in constrained layouts or smaller UIs. |
| DoUse for less critical, less complex tasks | Use for less critical, less complex tasks. |
| DoPreserve parent view context | Use when preserving the context of the parent view adds value. |
| DoNavigate to a child item | Use when wanting to navigate to a child item from a parent view. |
| Don'tUnrelated content | Avoid when content is unrelated to the parent UI. |
| Don'tNew parent UI in the flow | Avoid when needing to use a new parent UI in the flow; see [Keep Drilldown in its parent UI](#keep-drilldown-in-its-parent-ui). |

[drilldown-usage-drawer](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/drilldown-usage-drawer?embed=1\&hideNavigation=1\&hideExplorer=1\&terminalHeight=0\&file=src/App.tsx)

An example of a Drilldown within a Drawer that allows the user to quickly edit individual values from the parent view.

### Where Drilldown can be used

Drilldown is allowed in the following UIs:

* [Drawer](https://anvil.servicetitan.com/docs/components/drawer)
* [Dialog](https://anvil.servicetitan.com/docs/components/dialog)
* [Page Panel](https://anvil.servicetitan.com/docs/components/page)

#### Where Drilldown cannot be used

Drilldown is explicitly not allowed in the following UIs:

* [Popover](https://anvil.servicetitan.com/docs/components/popover)
* [Combobox](https://anvil.servicetitan.com/docs/components/combobox)

These are not allowed for a few reasons:

* They are not easily accessible to implement in the component.
* They are an unstable place to interact with for users (e.g., accidental Popover closing, uncontrollable resizes that get cut off).

In place of the Drilldown, use a Dialog instead.

#### Fullscreen Drilldown + Dialog vs A1 Takeover

The A1 Takeover contained in its header a "back to" element. Open that reference from [Visit Anvil1](https://anvil.servicetitan.com/docs/resources/visit-anvil1.md) after you request Vercel access. While on the surface this seems the same as a Drilldown in a fullscreen Dialog, there is a notable difference. An A1 Takeover can be triggered from any UI interaction. The Drilldown + Fullscreen Dialog can only be triggered from another fullscreen Dialog, consistent with how Drilldown works in all other scenarios.

If looking for the closest approximation to the A1 Takeover, use the normal Fullscreen Dialog without a Drilldown.

### Keep Drilldown in its parent UI

Use the same container the Drilldown came from (e.g., a Drawer-triggered Drilldown remains in that Drawer).

Dialog $\rightarrow$ User clicks on item $\rightarrow$ Drilldown in same Dialog

**Do**
Example of a properly triggered Drilldown

Any UI in the app $\rightarrow$ User clicks on item $\rightarrow$ Drilldown in a new Dialog

**Don’t**
Drilldown must come from the same parent UI

## How to use

* Use the same container the Drilldown came from (e.g., a Drawer-triggered Drilldown remains in that Drawer).
* Avoid using the [confirmation pattern](https://anvil.servicetitan.com/docs/patterns/confirmation/web.md) when using the Drilldown. If the actions being taken are important enough to warrant a confirmation, consider using a different UI pattern besides the Drilldown.
* If the Drilldown context is closed in some way, such as clicking on an x or a Backdrop, the whole UI is closed. It should not go back, that action is reserved for the back action. You may optionally remove these close actions in a Drilldown.
* Do not use [Breadcrumbs](https://anvil.servicetitan.com/docs/components/breadcrumbs) within a Drilldown. Breadcrumbs create a competing hierarchy with the Drilldown's back action. Breadcrumbs are not traditionally used in Panels, Drawers, or Dialogs either.

Dialog $\rightarrow$ User clicks on item $\rightarrow$ Drilldown in same Dialog

**Do**
Example of a properly triggered Drilldown

Any UI in the app $\rightarrow$ User clicks on item $\rightarrow$ Drilldown in a new Dialog

**Don’t**
Drilldown must come from the same parent UI

### Actions within a Drilldown

When using a Drilldown, it is recommended to adjust the actions to the following:

**Header**

* Always include the back action provided by Drilldown.
* Optionally remove the close action to reduce ambiguity.
* Optionally add header actions for secondary operations that apply to the current Drilldown, in a Drawer or Page Panel.

**Footer**

* Use **Apply** or similarly a named action when users modify content within the Drilldown.
* Omit **Cancel**, as its scope is ambiguous in this context.

## Code

**Implementation**

[drilldown-options-drawer](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/drilldown-options-drawer?embed=1\&hideNavigation=1\&hideExplorer=1\&terminalHeight=0\&file=src/App.tsx)

## Common Examples

```tsx theme={null}
import { Button, Dialog, DrillDown, Flex } from "@servicetitan/anvil2";

function ExampleComponent() {
  return (
    <Dialog>
      <Dialog.Header>Dialog Header</Dialog.Header>
      <Dialog.Content>This is the dialog body content.</Dialog.Content>
      <Dialog.Footer>
        <Flex gap="3" justifyContent="flex-end">
          <Dialog.CancelButton>Cancel</Dialog.CancelButton>
          <Button appearance="primary">Continue</Button>
        </Flex>
      </Dialog.Footer>
      <DrillDown index={0}>
        <DrillDown.Header>DrillDown Index 0</DrillDown.Header>
        <DrillDown.Content>This is the drilldown body content.</DrillDown.Content>
        <DrillDown.Footer>
          <Flex gap="3" justifyContent="flex-end">
            <DrillDown.PrevButton>Cancel</DrillDown.PrevButton>
            <DrillDown.NextButton appearance="primary">
              Continue
            </DrillDown.NextButton>
          </Flex>
        </DrillDown.Footer>
      </DrillDown>
    </Dialog>
  );
}
```

### Use as children of `Dialog`, `Drawer`, or `Page.Panel`

Any number of `DrillDown` components can be added as children of the `Dialog`, `Drawer`, or `Page.Panel` component. When a `DrillDown` is visible, it will cover the entire contents of the `Dialog` or `Drawer`.

```tsx theme={null}
<Dialog>
  <Dialog.Header>{...}</Dialog.Header>
  <Dialog.Content>{...}</Dialog.Content>
  <Dialog.Footer>{...}</Dialog.Footer>

  <DrillDown index={0}>{...}</DrillDown>
  <DrillDown index={1}>{...}</DrillDown>
  <DrillDown index={2}>{...}</DrillDown>
  <DrillDown index={3}>{...}</DrillDown>
</Dialog>
```

```tsx theme={null}
<Drawer>
  <Drawer.Header>{...}</Drawer.Header>
  <Drawer.Content>{...}</Drawer.Content>
  <Drawer.Footer>{...}</Drawer.Footer>

  <DrillDown index={0}>{...}</DrillDown>
  <DrillDown index={1}>{...}</DrillDown>
  <DrillDown index={2}>{...}</DrillDown>
  <DrillDown index={3}>{...}</DrillDown>
</Drawer>
```

> [!NOTE]
>
> **Each `DrillDown` requires a unique `index`**
>
> `DrillDown` sibling components are identified by their `index` prop. Numbers used for the `index` prop are zero-based, and calling `next` or clicking the `DrillDown.NextButton` will display the next index in numerical order.

### Uncontrolled usage

Set `defaultDrillDownIndex` on the host (`Dialog`, `Drawer`, or `Page.Panel`) to choose which `DrillDown` screen the first `next()` opens from a closed stack. The prop type is `number` and defaults to `0`. The host starts with no active screen. Closing the host restores this index.

Use `defaultDrillDownIndex` when that first `next()` should not open index `0`—for example, skipping an intro screen.

```tsx theme={null}
import { useState } from "react";
import { Drawer, DrillDown, Flex } from "@servicetitan/anvil2";

function ExampleComponent() {
  const [isOpen, setIsOpen] = useState(true);

  return (
    <Drawer
      open={isOpen}
      onClose={() => setIsOpen(false)}
      defaultDrillDownIndex={1}
    >
      <Drawer.Header>Drawer Header</Drawer.Header>
      <Drawer.Content>This is the drawer body content.</Drawer.Content>
      <Drawer.Footer>
        <Flex gap="3" justifyContent="flex-end">
          <Drawer.CancelButton>Cancel</Drawer.CancelButton>
          <DrillDown.NextButton appearance="primary">Continue</DrillDown.NextButton>
        </Flex>
      </Drawer.Footer>

      <DrillDown index={0} onClose={() => setIsOpen(false)}>
        <DrillDown.Header>Intro</DrillDown.Header>
        <DrillDown.Content>Optional intro screen.</DrillDown.Content>
        <DrillDown.Footer>
          <Flex gap="3" justifyContent="flex-end">
            <DrillDown.PrevButton>Cancel</DrillDown.PrevButton>
            <DrillDown.NextButton appearance="primary">
              Continue
            </DrillDown.NextButton>
          </Flex>
        </DrillDown.Footer>
      </DrillDown>

      <DrillDown index={1} onClose={() => setIsOpen(false)}>
        <DrillDown.Header>Main step</DrillDown.Header>
        <DrillDown.Content>
          Screen the first `next()` opens (`defaultDrillDownIndex={1}`).
        </DrillDown.Content>
        <DrillDown.Footer>
          <Flex gap="3" justifyContent="flex-end">
            <DrillDown.PrevButton>Back</DrillDown.PrevButton>
            <DrillDown.NextButton appearance="primary">
              Continue
            </DrillDown.NextButton>
          </Flex>
        </DrillDown.Footer>
      </DrillDown>
    </Drawer>
  );
}
```

> [!NOTE]
>
> With `defaultDrillDownIndex={1}`, the first `next` opens index `1`. Index `0` stays in the tree for later `setIndex(0)` jumps but is not reached by `Next`/`Prev` alone—`back` from the start index closes the flow.

#### Using `DrillDown.NextButton` and `DrillDown.PrevButton`

The `DrillDown` can be used with uncontrolled state by using the `DrillDown.NextButton` to move forward to each `DrillDown` `index` in ascending numerical order. The first `next` call opens `defaultDrillDownIndex` on the host. The `DrillDown.PrevButton` will similarly move backward in descending numerical order, and will close the `DrillDown` if there is no previous screen.

### Controlled usage

Controlled navigation still uses `defaultDrillDownIndex` on the host for the first `next()` from a closed stack and for restore-on-close. Spread `useDrillDown().drillDownProps` onto the same host, then call `next`, `back`, or `setIndex` for programmatic moves. Omit the prop to restore to `0` (shown below), or pass a non-zero value when the first `next()` should open a later screen.

```tsx theme={null}
import { useState } from "react";
import { Button, Drawer, DrillDown, Flex, useDrillDown } from "@servicetitan/anvil2";

function ExampleComponent() {
  const { drillDownProps, back, next } = useDrillDown();
  const [isOpen, setIsOpen] = useState(true);

  return (
    <Drawer {...drillDownProps} open={isOpen} onClose={() => setIsOpen(false)}>
      <Drawer.Header>Drawer Header</Drawer.Header>
      <Drawer.Content>This is the drawer body content.</Drawer.Content>
      <Drawer.Footer>
        <Flex gap="3" justifyContent="flex-end">
          <Drawer.CancelButton>Cancel</Drawer.CancelButton>
          <Button appearance="primary" onClick={next}>Continue</Button>
        </Flex>
      </Drawer.Footer>

      <DrillDown index={0} onClose={() => setIsOpen(false)}>
        <DrillDown.Header>DrillDown Index 0</DrillDown.Header>
        <DrillDown.Content>This is the drilldown body content.</DrillDown.Content>
        <DrillDown.Footer>
          <Flex gap="3" justifyContent="flex-end">
            <Button onClick={back}>Cancel</Button>
            <Button appearance="primary" onClick={next}>
              Continue
            </Button>
          </Flex>
        </DrillDown.Footer>
      </DrillDown>
    </Drawer>
  );
}
```

#### Using the `useDrillDown` hook

The `useDrillDown` hook returns a `next` and `back` function to allow for programmatically navigating between `DrillDown` components in numerical `index` order. (The hook also returns a `setIndex` function to allow navigating to arbitrary `index` values.)

To allow this hook to work, the `drillDownProps` return value also needs to be spread onto the parent component.

```tsx theme={null}
import { useState } from "react";
import { Drawer, useDrillDown } from "@servicetitan/anvil2";

function ExampleComponent() {
  const { drillDownProps, back, next } = useDrillDown();
  const [isOpen, setIsOpen] = useState(true);

  return (
    <Drawer {...drillDownProps} open={isOpen} onClose={() => setIsOpen(false)}>
      {...}
    </Drawer>
  );
}
```

### Closing from within a `DrillDown`

By default, the `DrillDown.Header` does not display a close button. To display a close button in the `DrillDown.Header`, add an `onClose` handler to the `DrillDown`.

```tsx theme={null}
<DrillDown index={0} onClose={() => setOpen(false)}>
  <DrillDown.Header>DrillDown Index 0</DrillDown.Header>
  <DrillDown.Content>This is the drilldown body content.</DrillDown.Content>
  <DrillDown.Footer>
    <Flex gap="3" justifyContent="flex-end">
      <DrillDown.PrevButton>Cancel</DrillDown.PrevButton>
      <DrillDown.NextButton appearance="primary">
        Continue
      </DrillDown.NextButton>
    </Flex>
  </DrillDown.Footer>
</DrillDown>
```

### Header actions

To add an overflow menu to the `DrillDown.Header`, pass an `actions` array. The menu appears to the left of the close button, or on its own when the `DrillDown` has no `onClose`.

Actions belong to the screen that defines them. A `DrillDown` does not inherit the actions from `Drawer.Header`, so pass the commands that apply to each screen.

```tsx theme={null}
<DrillDown index={0} onClose={() => setOpen(false)}>
  <DrillDown.Header
    actions={[
      { label: "Edit", onClick: () => {} },
      { label: "Duplicate", onClick: () => {} },
      { label: "Delete", onClick: () => {} },
    ]}
  >
    DrillDown Index 0
  </DrillDown.Header>
  <DrillDown.Content>This is the drilldown body content.</DrillDown.Content>
</DrillDown>
```

**DrillDown Props**

```tsx theme={null}
<DrillDown index={0} onClose={() => setOpen(false)}>
  <DrillDown.Header>DrillDown Index 0</DrillDown.Header>
  <DrillDown.Content>This is the drilldown body content.</DrillDown.Content>
  <DrillDown.Footer>
    <Flex gap="3" justifyContent="flex-end">
      <DrillDown.PrevButton>Cancel</DrillDown.PrevButton>
      <DrillDown.NextButton appearance="primary">
        Continue
      </DrillDown.NextButton>
    </Flex>
  </DrillDown.Footer>
</DrillDown>
```

## `DrillDown` Props

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

**index** `number` *required*

The index of the `DrillDown` screen in the navigation sequence. This index is zero-based.

**onClose** `() =\> void`

Called when the close button in the header is clicked. The close button is only displayed in the `DrillDown.Header` if there is an `onClose` provided.

## Host overlay props

`defaultDrillDownIndex` is not a prop on `DrillDown`. Set it on the host overlay that owns the drill flow:

* [`Dialog`](https://anvil.servicetitan.com/docs/components/dialog/web)
* [`Drawer`](https://anvil.servicetitan.com/docs/components/drawer/web)
* [`Page.Panel`](https://anvil.servicetitan.com/docs/components/page/web)

**defaultDrillDownIndex** `number` (default: 0)

Screen the first `next()` opens from a closed stack. The host starts with no active screen. Closing the host restores this index. Available on `Dialog`, `Drawer`, and `Page.Panel`.

**DrillDown.Header Props**

```tsx theme={null}
<DrillDown.Header
  actions={[
    { label: "Edit", onClick: () => {} },
    { label: "Duplicate", onClick: () => {} },
    { label: "Delete", onClick: () => {} },
  ]}
>
  DrillDown Header
</DrillDown.Header>
```

## `DrillDown.Header` Props

In addition to the props listed below, the `DrillDown.Header` component can accept any valid HTML`header` props.

**actions** `\{ label: string; onClick: () =\> void; disabled?: boolean; icon?: Svg \}[]`

Overflow menu items rendered to the left of the close button. Each item maps to a `Menu.Item`. The menu appears when the array is non-empty.

**el** `"h1" | "h2" | "h3" | "h4" | "h5" | "h6"` (default: h1)

Customizes the heading level of the `DrillDown.Header`

**DrillDown.Content Props**

```tsx theme={null}
<DrillDown.Content>DrillDown content</DrillDown.Content>
```

## `DrillDown.Content` Props

The `DrillDown.Content` component can accept any valid HTML `div` props.

**DrillDown.Footer Props**

```tsx theme={null}
<DrillDown.Footer>
  <Flex gap="3" justifyContent="flex-end">
    <Button onClick={back}>Back</Button>
    <Button appearance="primary" onClick={next}>
      Continue
    </Button>
  </Flex>
</DrillDown.Footer>
```

## `DrillDown.Footer` Props

The `DrillDown.Footer` component can accept any valid HTML `footer` props.

**DrillDown.NextButton Props**

```tsx theme={null}
<DrillDown.NextButton appearance="primary">
  Continue
</DrillDown.NextButton>
```

## `DrillDown.NextButton` Props

The `DrillDown.NextButton` component can accept the same props as the `Button` component. This button takes the user to the next DrillDown index (e.g. if the `index` `1` `DrillDown` is visible, it will move to `index` `2`).

**DrillDown.PrevButton Props**

```tsx theme={null}
<DrillDown.PrevButton>
  Back
</DrillDown.PrevButton>
```

## `DrillDown.PrevButton` Props

The `DrillDown.PrevButton` component can accept the same props as the `Button` component. This button takes the user to the previous DrillDown. If there is no previous `DrillDown`, it will close the `DrillDown`.

**useDrillDown hook**

```tsx theme={null}
const { drillDownProps, setIndex, back, next } = useDrillDown();
```

## `useDrillDown` hook

The `useDrillDown` hook returns four values:

**drillDownProps** `object`

This object is required to be spread on the parent component (`Drawer`, `Dialog`) for the `DrillDown` to work in a controlled manner using the `useDrillDown` hook.

**next** `() =\> void`

Calling this function takes the user to the next DrillDown index (e.g. if the `index` `1` `DrillDown` is visible, it will move to `index` `2`).

**back** `() =\> void`

Calling this function takes the user to the previous DrillDown. If there is no previous `DrillDown`, it will close the `DrillDown`.

**setIndex** `(index: number | undefined) =\> void`

Calling this function takes the user to the DrillDown index that is passed into the function. If `setIndex` is called with `undefined`, it will close the `DrillDown`.


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