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

# Drawer - Web

> Drawers are flyouts that slide onto the page. All drawers slide in from the right.

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

![](https://mintcdn.com/servicetitan/uz2PQSvO75TRhQ38/images/docs/web/components/shared/example-of-a-drawer.png?fit=max\&auto=format\&n=uz2PQSvO75TRhQ38\&q=85\&s=cd0b735286e9497cafc5b94de6a88b89)

## Anatomy

The Drawer consists of six primary elements that work together to slide content onto the page from the right.

![Anatomy of a
Drawer](https://mintcdn.com/servicetitan/c3TemHZmf0Azs2_z/images/docs/web/components/drawer/design/anatomy-of-a-drawer.png?fit=max\&auto=format\&n=c3TemHZmf0Azs2_z\&q=85\&s=2e611c0075c827740d3b04502ca3292c)

1. Header
2. Header actions (Optional)
3. Close action
4. Body content
5. Backdrop
6. Footer actions

## Options

The Drawer supports flexible content, header actions, footer actions, and sizing configurations to accommodate various flyout scenarios.

### Header actions

Drawer supports an optional overflow menu in the header for secondary operations related to the drawer content. Header actions appear to the left of the close action.

drawer-header-actions-full-page

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

```tsx lines theme={null}
import { Drawer, Flex, TextField, Button } from "@servicetitan/anvil2";

function App() {
  return (
    <Drawer open>
      <Drawer.Header
        actions={[
          { label: "Edit", onClick: () => {} },
          { label: "Duplicate", onClick: () => {} },
          { label: "Delete", onClick: () => {} },
        ]}
      >
        Header text
      </Drawer.Header>
      <Drawer.Content>
        <Flex direction="column" gap="6">
          <TextField label="First Name" />
          <TextField label="Last Name" />
          <TextField label="Email Address" />
        </Flex>
      </Drawer.Content>
      <Drawer.Footer sticky>
        <Drawer.CancelButton>Cancel</Drawer.CancelButton>
        <Button appearance="primary">Primary Action</Button>
      </Drawer.Footer>
    </Drawer>
  );
}

export default App;
```

### Body content

Drawer supports any type of content in the body.

drawer-content-full-page

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

```tsx lines theme={null}
import { Drawer, Flex, TextField, Radio, Button } from "@servicetitan/anvil2";

function App() {
  return (
    <Drawer open>
      <Drawer.Header>Header text</Drawer.Header>
      <Drawer.Content>
        <Flex direction="column" gap="6">
          <TextField label="First Name" />
          <TextField label="Last Name" />
          <TextField label="Email Address" />
          <Radio.Group legend="My function is">
            <Radio name="role" value="designer" label="Designer" />
            <Radio name="role" value="developer" label="Developer" />
            <Radio name="role" value="pm" label="Product Manager" />
          </Radio.Group>
        </Flex>
      </Drawer.Content>
      <Drawer.Footer sticky>
        <Drawer.CancelButton>Cancel</Drawer.CancelButton>
        <Button appearance="primary">Primary Action</Button>
      </Drawer.Footer>
    </Drawer>
  );
}

export default App;
```

### Footer actions

Drawer supports several types of action groupings. These typically include one primary action and may include elements such as links or destructive actions.

drawer-footer-full-page

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

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

function App() {
  return (
    <Drawer open>
      <Drawer.Content></Drawer.Content>
      <Drawer.Footer sticky>
        <Drawer.CancelButton>Cancel</Drawer.CancelButton>
        <Button appearance="primary">Primary Action</Button>
      </Drawer.Footer>
    </Drawer>
  );
}

export default App;
```

drawer-footer-button-left-full-page

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

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

function App() {
  return (
    <Drawer open>
      <Drawer.Content></Drawer.Content>
      <Drawer.Footer sticky>
        <Button>Back</Button>
        {/* spacer */}
        <Flex grow="1" />
        <Drawer.CancelButton>Cancel</Drawer.CancelButton>
        <Button appearance="primary">Primary Action</Button>
      </Drawer.Footer>
    </Drawer>
  );
}

export default App;
```

drawer-footer-link-left-full-page

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

```tsx lines theme={null}
import { Drawer, Link, Flex, Button } from "@servicetitan/anvil2";

function App() {
  return (
    <Drawer open>
      <Drawer.Content></Drawer.Content>
      <Drawer.Footer sticky>
        <Link style={{ alignSelf: "center" }}>Learn more</Link>
        {/* spacer */}
        <Flex grow="1" />
        <Drawer.CancelButton>Cancel</Drawer.CancelButton>
        <Button appearance="primary">Primary Action</Button>
      </Drawer.Footer>
    </Drawer>
  );
}

export default App;
```

drawer-footer-button-danger-full-page

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

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

function App() {
  return (
    <Drawer open>
      <Drawer.Content></Drawer.Content>
      <Drawer.Footer sticky>
        <Drawer.CancelButton>Cancel</Drawer.CancelButton>
        <Button appearance="danger">Destructive Action</Button>
      </Drawer.Footer>
    </Drawer>
  );
}

export default App;
```

### Sizing

Drawer supports 4 sizing options: small, medium (default), large, and extra large.

drawer-size-small-full-page

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

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

function App() {
  return (
    <Drawer open size="small">
      <Drawer.Header>Header text</Drawer.Header>
      <Drawer.Content>Body text in the Drawer.</Drawer.Content>
      <Drawer.Footer sticky>
        <Drawer.CancelButton>Cancel</Drawer.CancelButton>
        <Button appearance="primary">Primary Action</Button>
      </Drawer.Footer>
    </Drawer>
  );
}

export default App;
```

drawer-size-medium-full-page

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

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

function App() {
  return (
    <Drawer open>
      <Drawer.Header>Header text</Drawer.Header>
      <Drawer.Content>Body text in the Drawer.</Drawer.Content>
      <Drawer.Footer sticky>
        <Drawer.CancelButton>Cancel</Drawer.CancelButton>
        <Button appearance="primary">Primary Action</Button>
      </Drawer.Footer>
    </Drawer>
  );
}

export default App;
```

drawer-size-large-full-page

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

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

function App() {
  return (
    <Drawer open size="large">
      <Drawer.Header>Header text</Drawer.Header>
      <Drawer.Content>Body text in the Drawer.</Drawer.Content>
      <Drawer.Footer sticky>
        <Drawer.CancelButton>Cancel</Drawer.CancelButton>
        <Button appearance="primary">Primary Action</Button>
      </Drawer.Footer>
    </Drawer>
  );
}

export default App;
```

drawer-size-xlarge-full-page

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

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

function App() {
  return (
    <Drawer open size="xlarge">
      <Drawer.Header>Header text</Drawer.Header>
      <Drawer.Content>Body text in the Drawer.</Drawer.Content>
      <Drawer.Footer sticky>
        <Drawer.CancelButton>Cancel</Drawer.CancelButton>
        <Button appearance="primary">Primary Action</Button>
      </Drawer.Footer>
    </Drawer>
  );
}

export default App;
```

| Sizing | Width | Height |
| - | - | - |
| Small | 22.5rem / 360px | 100% |
| Medium (default) | 35rem / 560px | 100% |
| Large | 48rem / 768px | 100% |
| Extra Large | 70rem / 1120px | 100% |

## Behavior

The Drawer responds to content overflow and scrolling requirements while maintaining focus management.

### Overflow handling

Header and footer content in Drawer wraps rather than truncates. Footer actions support custom overflow behavior when needed.

drawer-overflow-full-page

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

```tsx lines theme={null}
import { Drawer, Link, Flex, Button } from "@servicetitan/anvil2";
import { core } from "@servicetitan/anvil2/token";

function App() {
  return (
    <Drawer open>
      <Drawer.Header>
        Drawer Title that Will Overflow to Another Line with Enough Words
      </Drawer.Header>
      <Drawer.Content>Body text in the Drawer.</Drawer.Content>
      <Drawer.Footer
        style={{
          flexWrap: "wrap",
          alignItems: "center",
        }}
        sticky
      >
        <Link
          style={{
            marginTop: core.primitive?.Size2?.value,
            marginBottom: core.primitive?.Size2?.value,
          }}
        >
          Learn more
        </Link>
        <Flex wrap="wrap" justifyContent="flex-end" gap="2">
          <Drawer.CancelButton>
            A Very Long Secondary Action that Wraps
          </Drawer.CancelButton>
          <Button appearance="primary">A Longer Primary Action</Button>
        </Flex>
      </Drawer.Footer>
    </Drawer>
  );
}

export default App;
```

### Scrolling

The body region of Drawer scrolls when space is not available. The footer can be configured to scroll with the body but is typically sticky.

drawer-scrolling-full-page

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

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

function App() {
  return (
    <Drawer open>
      <Drawer.Header>Header text</Drawer.Header>
      <Drawer.Content>
        Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed interdum
        justo id eros interdum, quis malesuada diam malesuada. Etiam auctor
        pharetra nisl vitae aliquet. Curabitur gravida diam eros, nec pharetra
        augue convallis non. Maecenas nibh felis, blandit ac congue at, cursus
        sed odio. Cras quam risus, suscipit eget velit id, varius faucibus est.
        Integer in quam eget lacus consectetur posuere consectetur mollis quam.
        Morbi sapien felis, venenatis ac dui a, laoreet placerat velit. Nam
        luctus eu lectus sed consequat. Nam mi elit, auctor ut magna et,
        accumsan facilisis sapien.
        <br />
        <br />
        Donec a mi eu mauris dignissim dictum non at erat. Sed hendrerit metus
        dolor, in congue ligula vestibulum nec. Quisque ac velit sollicitudin,
        laoreet mauris quis, rutrum urna. Nam euismod nisi vitae mi commodo, id
        dapibus arcu convallis. Quisque tincidunt aliquam elit. Donec porttitor
        lacus eget tellus finibus, luctus dapibus mi auctor. Praesent sed
        tincidunt tortor. Aliquam non orci at nisl tincidunt dapibus. Mauris a
        interdum augue. Nunc ac augue ornare, mattis orci eu, tempus tortor. Ut
        dignissim velit sit amet lorem tempus, a dictum mi commodo.
        <br />
        <br />
        Maecenas neque nulla, fringilla vel risus sit amet, placerat accumsan
        tortor. Nam consequat massa non laoreet posuere. Cras at massa in leo
        varius tempus. Nam sed convallis sapien. Integer pretium risus vel
        fermentum consequat. Integer convallis in massa sit amet posuere. Cras
        gravida ut odio eget laoreet. Pellentesque lobortis enim nec orci
        venenatis, in auctor enim molestie. Nullam felis lectus, dictum ut magna
        vel, dignissim ultricies erat. Suspendisse potenti. Morbi non lacinia
        dui, at fermentum velit. Aenean sodales ullamcorper lectus, in mollis
        lacus vulputate vel. Vivamus accumsan lorem odio, et lacinia nunc
        rhoncus sed. Quisque dapibus nisl at orci sodales, vitae volutpat nulla
        tincidunt. Fusce tristique sed erat in ullamcorper. In porttitor lectus
        nec est porttitor, eget dignissim ex finibus.
        <br />
        <br />
        Nulla facilisi. Phasellus mollis iaculis nisi sed pulvinar. Ut id
        vehicula enim, eget euismod nibh. Vestibulum egestas facilisis mauris
        sit amet porttitor. Morbi at laoreet sem. Fusce eu mauris a dui
        efficitur tristique. Integer ultrices, leo nec bibendum sodales, neque
        sem congue ipsum, non molestie metus tortor ac ligula. Sed cursus diam
        sed odio cursus commodo eu nec ex. Donec sed lacus nec felis vehicula
        hendrerit. Curabitur consectetur ultricies odio eu suscipit.
        <br />
        <br />
        Cras eu blandit tortor, a semper nibh. Proin vel felis tellus.
        Vestibulum dignissim diam eget sem tincidunt, sit amet vehicula justo
        bibendum. Interdum et malesuada fames ac ante ipsum primis in faucibus.
        Phasellus tortor diam, viverra in mattis ac, lobortis id tellus.
        Praesent laoreet condimentum magna ac venenatis. Etiam massa libero,
        volutpat in erat a, venenatis varius lorem.
      </Drawer.Content>
      <Drawer.Footer sticky>
        <Drawer.CancelButton>Cancel</Drawer.CancelButton>
        <Button appearance="primary">Primary Action</Button>
      </Drawer.Footer>
    </Drawer>
  );
}

export default App;
```

## Usage Guidelines

Use Drawer to show information or a task without navigating to another page.

| Standard | Requirement |
| - | - |
| DoPosition on the right | Drawers always appear on the right side of the page; see [Position the Drawer on the right](#position-the-drawer-on-the-right). |
| DoAlways use a backdrop | The backdrop reinforces the focused nature of Drawer and prevents de-synced states; see [Always use a backdrop](#always-use-a-backdrop). |
| DoExtend to full height | Drawers always extend to 100% the height of the browser window; see [Always extend the Drawer to the screen's full height](#always-extend-the-drawer-to-the-screens-full-height). |
| Don'tUse for exclusive, essential flows | Drawers should not be used for steps essential to a flow. The exception is if the Drawer is acting as a supplemental, redundant place for those steps. |
| Don'tUse to communicate page errors | Using a Drawer to convey errors is an anti-pattern. It requires a user to actively remember its contents once the Drawer is closed. See the error pattern for alternatives. |
| Don'tUse to confirm actions | Use the [Dialog](https://anvil.servicetitan.com/docs/components/dialog/web.md) for confirmations instead. |
| Don'tPosition on the left | Don't position a Drawer on the left side of the page; see [Position the Drawer on the right](#position-the-drawer-on-the-right). |
| Don'tExcluding the backdrop | Don't exclude the backdrop; if side content must not have a backdrop, integrate the content directly on the page instead; see [Always use a backdrop](#always-use-a-backdrop). |
| Don'tOffsetting Drawer height | Don't offset the Drawer to fit below other elements on the page; see [Always extend the Drawer to the screen's full height](#always-extend-the-drawer-to-the-screens-full-height). |

### Alternatives

Consider integrating the content directly on the page instead of using a Drawer. This allows users to continue to both see and interact with the rest of the content on the page.

#### Drawer vs Dialog

| | Dialog | Drawer |
| - | - | - |
| Complete a secondary task | Simple, one step tasks | 1-3 steps at most. Tasks with more steps should be on a separate page |
| Display additional information related to the context in the main page | Yes, ideal for short content | Yes, ideal for longer, more complex information |
| Confirm user actions | Yes | No |

### How to Use

#### Position the Drawer on the right

Drawers always appear on the right side of the page.

![Drawer How Position Right
Do](https://mintcdn.com/servicetitan/Q18b5kAebhFUEruT/images/docs/web/components/drawer/shared/drawer-how-position-right-do.png?fit=max\&auto=format\&n=Q18b5kAebhFUEruT\&q=85\&s=b81d60357d8a83faaed3896b224f7ba7)

> [!TIP]
>
> **Do**

![Drawer How Position Left Don't](https://mintlify.s3.us-west-1.amazonaws.com/servicetitan/images/docs/web/components/drawer/web/drawer-how-position-left-dont.png)

> [!CAUTION]
>
> **Don't**

#### Always use a backdrop

The backdrop reinforces the focused nature of Drawer and prevents de-synced states. If side content must not have a backdrop, integrate the content directly on the page instead.

![Drawer use a
backdrop](https://mintlify.s3.us-west-1.amazonaws.com/servicetitan/images/docs/web/components/drawer/web/drawer-use-a-backdrop.png)

> [!TIP]
>
> **Do**

![Drawer don't exclude the
backdrop](https://mintlify.s3.us-west-1.amazonaws.com/servicetitan/images/docs/web/components/drawer/web/drawer-dont-exclude-the-backdrop.png)

> [!CAUTION]
>
> **Don't**

#### Always extend the Drawer to the screen's full height

Drawers will always extend to 100% the height of the browser window. Don't offset the Drawer to fit below other elements on the page.

![Drawer How Position Right
Do](https://mintcdn.com/servicetitan/Q18b5kAebhFUEruT/images/docs/web/components/drawer/shared/drawer-how-position-right-do.png?fit=max\&auto=format\&n=Q18b5kAebhFUEruT\&q=85\&s=b81d60357d8a83faaed3896b224f7ba7)

> [!TIP]
>
> **Do**

![Drawer How Height
Don't](https://mintlify.s3.us-west-1.amazonaws.com/servicetitan/images/docs/web/components/drawer/web/drawer-how-height-dont.png)

> [!CAUTION]
>
> **Don't**

#### Dialogs on top of Drawers

Avoid stacking overlay elements. A confirmation message through a Dialog is an exception. If a confirmation Dialog is needed within a Drawer, use an alternative to Drawer.

## Content

Content within the Drawer should be clear, organized, and support the task or information being displayed.

## Keyboard Interaction

Users can navigate the Drawer using standard keyboard controls.

| Key | Interaction |
| - | - |
| Tab | Cycles through tab-able elements within the Drawer |
| Escape | Closes the Drawer |

### Accessibility

#### Focus management

There are a few different things to keep in mind with the Drawer's focus behavior.

##### Trap focus within the Drawer

By default, the Drawer will trap focus within itself. Elements outside of the Drawer would not be accessible by keyboard.

##### First focus management

What elements Drawer focuses on open depends on a few factors:

* If the body content has a focusable element, Drawer focuses on the first of these elements.
* If the body does not have a focusable element, the following is the preferred first-focus element:
  * With no actions at all, focus first on the close action.
  * When a primary action exists, focus first on the primary action.
  * When a primary destructive action exists, focus first on the secondary cancel action.

##### Return focus on close

When a Drawer is closed and the original context that triggered the Drawer is still present, focus should return to the element that triggered the Drawer.

For more guidance on focus management with dynamic content, see [changing content best practices](https://anvil.servicetitan.com/docs/accessibility/changing-content.md).

## Code

**Implementation**

drawer-playground-full-page

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

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

function App() {
  return (
    <Drawer open>
      <Drawer.Header>Header text</Drawer.Header>
      <Drawer.Content>Body text</Drawer.Content>
      <Drawer.Footer>
        <Drawer.CancelButton>Cancel</Drawer.CancelButton>
        <Button appearance="primary">Footer Button</Button>
      </Drawer.Footer>
    </Drawer>
  );
}

export default App;
```

## Common Examples

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

function ExampleComponent() {
  return (
    <Drawer open>
      <Drawer.Header>Header text</Drawer.Header>
      <Drawer.Content>Body text</Drawer.Content>
      <Drawer.Footer>
        <Button appearance="primary">Footer Button</Button>
      </Drawer.Footer>
    </Drawer>
  );
}
```

### Closing Drawers

Clicking a `Drawer.CancelButton` or the close button in the `Drawer.Header` will trigger the `onClose` handler. A callback can be added to a `Drawer.CancelButton` or any `Button` in the `Drawer.Footer` to further control the open state of the `Drawer`.

#### Closing callbacks

In addition to `onClose`–which indicates a user has chosen to close a drawer–the component also offers two animation callbacks `onCloseAnimationStart` and `onCloseAnimationComplete`.

You may use these callbacks to perform additional actions related to the presentation of the `Drawer`. For example, if you have a drawer containing a form, you may wish to reset the form state after the close animation has played so that the user doesn't see an empty form briefly when closing the drawer.

```tsx theme={null}
const [open, setOpen] = useState(false);
const [field, setField] = useState("");

const handleSubmit = () => {
  setOpen(false);
};
const handleDrawerAnimationComplete = () => {
  setField("");
};

return (
  <>
    <Button onClick={() => setOpen(true)}>Open</Button>
    <Drawer
      open={open}
      onClose={() => setOpen(false)}
      onCloseAnimationComplete={handleDrawerAnimationComplete}
    >
      <Drawer.Header>What is your favorite color?</Drawer.Header>
      <Drawer.Content>
        <TextField
          label="Favorite color"
          value={field}
          onChange={(e) => setField(e.target.value)}
          style={{ width: "100%" }}
          autoComplete="off"
        />
      </Drawer.Content>
      <Drawer.Footer>
        <Button onClick={() => setOpen(false)}>Cancel</Button>
        <Button appearance="primary" onClick={handleSubmit}>
          Submit
        </Button>
      </Drawer.Footer>
    </Drawer>
  </>
);
```

### Sticky Content

Use the `sticky` prop on `Drawer.Content` to keep important UI elements (like search fields or filters) visible while other content scrolls. This is useful for drawers with long, scrollable content.

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

function ExampleComponent() {
  const [isOpen, setIsOpen] = useState(true);
  return (
    <Drawer open={isOpen} onClose={() => setIsOpen(false)}>
      <Drawer.Header>Filter Items</Drawer.Header>
      <Drawer.Content sticky>
        <TextField placeholder="Search items..." />
      </Drawer.Content>
      <Drawer.Content>
        <Flex direction="column" gap="2">
          {Array.from({ length: 50 }, (_, i) => (
            <Text key={i}>Item {i + 1}</Text>
          ))}
        </Flex>
      </Drawer.Content>
      <Drawer.Footer sticky>
        <Flex justifyContent="space-between" grow="1">
          <Button>Reset</Button>
          <Flex gap="3">
            <Drawer.CancelButton>Cancel</Drawer.CancelButton>
            <Button appearance="primary">Apply</Button>
          </Flex>
        </Flex>
      </Drawer.Footer>
    </Drawer>
  );
}
```

The search field will remain visible at the top while the list of items scrolls below it.

### Drawers and Toasts

Due to the way the HTML `dialog` element renders in the browser's top layer, the `Drawer` component includes an internal `Toaster` for rendering toast messages. This should be unnoticeable to implementors and users, but there may be edge cases the result in toasts not rendering as expected.

Please reach out to us in the [#ask-designsystem](https://servicetitan.enterprise.slack.com/archives/CBSRGHTRS) channel on Slack if any edge cases related to drawers and toasts are found!

## Anti-Patterns

### Conditional rendering

The openness should be controlled by the `open` prop and not by conditional rendering -- this is an anti-pattern for Drawer.

```tsx theme={null}
❌
{condition && <Drawer open>...</Drawer>}

✅
<Drawer open={condition}>...</Drawer>
```

#### Resetting content

HTML Dialog show/hide content but it doesn't remove from the DOM which means the reset doesn't happen automatically. To do the reset, use `key` on `<Drawer.Content>` . Since `<Drawer.Content>` is always present in DOM, you can add conditional to, or in, the `<Drawer.Content>` as well.

#### More details

Drawer uses HTML Dialog which is powered by HTML top-layers. This puts them on top of EVERYTHING regardless z-index and we use the HTML top-layer to avoid stacking context and z-index issues, ensuring Drawer always appear above all content without needing manual z-index adjustments. Drawer internally has custom mechanism to ensure Toasts to be on top for ServiceTitan app and requires Drawer to be present on page load.

**Drawer Props**

```tsx theme={null}
<Drawer
  open={true}
  onClose={() => setIsOpen(false)}
  disableCloseOnClickOutside={false}
  disableCloseOnEscape={false}
  size="medium"
  onCloseAnimationComplete={() => {}}
  onCloseAnimationStart={() => {}}
  onOpenAnimationComplete={() => {}}
  onOpenAnimationStart={() => {}}
>
  <Drawer.Header>Header</Drawer.Header>
  <Drawer.Content>Content</Drawer.Content>
  <Drawer.Footer>
    <Button>Action</Button>
  </Drawer.Footer>
</Drawer>
```

## `Drawer` Props

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

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

Screen the first `next()` opens from a closed [DrillDown](https://anvil.servicetitan.com/docs/components/drilldown/web.md) stack. The drawer starts with no active screen. Closing the drawer restores this index.

**disableCloseOnClickOutside** `boolean` (default: false)

When `true`, clicking outside the drawer will not close it.

**disableCloseOnEscape** `boolean` (default: false)

When `true`, pressing the escape key will not close the drawer.

**enableScrollChaining** `boolean`

Enables scroll chaining behavior.

**initialFocusResolver** `(focusables: (HTMLElement | SVGElement)[]) =\> (HTMLElement | SVGElement)`

Given an array of focusable elements, returns the element that should receive initial focus.

**onClickOutside** `(e: MouseEvent) =\> void`

Callback when clicking outside the drawer.

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

Indicates the user has closed the drawer. Use this to update your state.

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

Callback indicating the drawer has animated out. Use this to clean up views as needed.

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

Callback indicating the drawer is beginning to animate out.

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

Callback indicating the drawer has animated in.

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

Callback indicating the drawer is beginning to animate in.

**open** `boolean` (default: false)

**size** `"small" | "medium" | "large" | "xlarge"` (default: medium)

**Drawer.Header Props**

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

## `Drawer.Header` Props

In addition to the props listed below, the `Drawer.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`.

**Drawer.Content Props**

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

## `Drawer.Content` Props

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

**sticky** `boolean` (default: false)

When `true`, the content will stick below the header during scroll.

**Drawer.Footer Props**

```tsx theme={null}
<Drawer.Footer sticky={false}>
  <Button>Action</Button>
</Drawer.Footer>
```

## `Drawer.Footer` Props

In addition to the props listed below, the `Drawer.Footer` component can accept any valid HTML `footer` props.

**sticky** `boolean` (default: false)


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