# Drawer

URL: https://ark-ui.com/docs/components/drawer
LLM: https://ark-ui.com/llms.txt/components/drawer

A panel that slides in from the edge of the screen, typically used for navigation or forms.

---

<ComponentPreview id="Drawer" />

## Anatomy

<Anatomy id="drawer" />

```tsx
<Drawer.Root>
  <Drawer.Trigger />
  <Drawer.Backdrop />
  <Drawer.Positioner>
    <Drawer.Content>
      <Drawer.Grabber>
        <Drawer.GrabberIndicator />
      </Drawer.Grabber>
      <Drawer.Title />
      <Drawer.Description />
      <Drawer.CloseTrigger />
    </Drawer.Content>
  </Drawer.Positioner>
</Drawer.Root>
```

## Examples

**Example: basic**

#### React

```tsx
import { Drawer } from '@ark-ui/react/drawer'
import { XIcon } from 'lucide-react'
import styles from 'styles/drawer.module.css'

export const Basic = () => (
  <Drawer.Root>
    <Drawer.Trigger className={styles.Trigger}>Open</Drawer.Trigger>
    <Drawer.Backdrop className={styles.Backdrop} />
    <Drawer.Positioner className={styles.Positioner}>
      <Drawer.Content className={styles.Content}>
        <Drawer.Grabber className={styles.Grabber}>
          <Drawer.GrabberIndicator className={styles.GrabberIndicator} />
        </Drawer.Grabber>
        <Drawer.Title className={styles.Title}>Drawer Title</Drawer.Title>
        <p>This is the content of the drawer.</p>
        <Drawer.CloseTrigger className={styles.CloseTrigger}>
          <XIcon />
        </Drawer.CloseTrigger>
      </Drawer.Content>
    </Drawer.Positioner>
  </Drawer.Root>
)

```

#### Solid

```tsx
import { Drawer } from '@ark-ui/solid/drawer'
import { XIcon } from 'lucide-solid'
import styles from 'styles/drawer.module.css'

export const Basic = () => (
  <Drawer.Root>
    <Drawer.Trigger class={styles.Trigger}>Open</Drawer.Trigger>
    <Drawer.Backdrop class={styles.Backdrop} />
    <Drawer.Positioner class={styles.Positioner}>
      <Drawer.Content class={styles.Content}>
        <Drawer.Grabber class={styles.Grabber}>
          <Drawer.GrabberIndicator class={styles.GrabberIndicator} />
        </Drawer.Grabber>
        <Drawer.Title class={styles.Title}>Drawer Title</Drawer.Title>
        <p>This is the content of the drawer.</p>
        <Drawer.CloseTrigger class={styles.CloseTrigger}>
          <XIcon />
        </Drawer.CloseTrigger>
      </Drawer.Content>
    </Drawer.Positioner>
  </Drawer.Root>
)

```

#### Vue

```vue
<script setup lang="ts">
import { Drawer } from '@ark-ui/vue/drawer'
import { XIcon } from 'lucide-vue-next'
import styles from 'styles/drawer.module.css'
</script>

<template>
  <Drawer.Root>
    <Drawer.Trigger :class="styles.Trigger">Open</Drawer.Trigger>
    <Drawer.Backdrop :class="styles.Backdrop" />
    <Drawer.Positioner :class="styles.Positioner">
      <Drawer.Content :class="styles.Content">
        <Drawer.Grabber :class="styles.Grabber">
          <Drawer.GrabberIndicator :class="styles.GrabberIndicator" />
        </Drawer.Grabber>
        <Drawer.Title :class="styles.Title">Drawer Title</Drawer.Title>
        <p>This is the content of the drawer.</p>
        <Drawer.CloseTrigger :class="styles.CloseTrigger">
          <XIcon />
        </Drawer.CloseTrigger>
      </Drawer.Content>
    </Drawer.Positioner>
  </Drawer.Root>
</template>

```

#### Svelte

```svelte
<script lang="ts">
  import { Drawer } from '@ark-ui/svelte/drawer'
  import { XIcon } from 'lucide-svelte'
  import styles from 'styles/drawer.module.css'
</script>

<Drawer.Root>
  <Drawer.Trigger class={styles.Trigger}>Open</Drawer.Trigger>
  <Drawer.Backdrop class={styles.Backdrop} />
  <Drawer.Positioner class={styles.Positioner}>
    <Drawer.Content class={styles.Content}>
      <Drawer.Grabber class={styles.Grabber}>
        <Drawer.GrabberIndicator class={styles.GrabberIndicator} />
      </Drawer.Grabber>
      <Drawer.Title class={styles.Title}>Drawer Title</Drawer.Title>
      <p>This is the content of the drawer.</p>
      <Drawer.CloseTrigger class={styles.CloseTrigger}>
        <XIcon />
      </Drawer.CloseTrigger>
    </Drawer.Content>
  </Drawer.Positioner>
</Drawer.Root>

```

### Swipe Direction

Use the `swipeDirection` prop to control which edge the drawer slides in from.

**Example: swipe-direction**

#### React

```tsx
import { Drawer } from '@ark-ui/react/drawer'
import { XIcon } from 'lucide-react'
import styles from 'styles/drawer.module.css'

export const SwipeDirection = () => (
  <Drawer.Root swipeDirection="end">
    <Drawer.Trigger className={styles.Trigger}>Open Right</Drawer.Trigger>
    <Drawer.Backdrop className={styles.Backdrop} />
    <Drawer.Positioner className={styles.Positioner}>
      <Drawer.Content className={styles.Content}>
        <Drawer.Title className={styles.Title}>Right Drawer</Drawer.Title>
        <p>This drawer slides in from the right side.</p>
        <Drawer.CloseTrigger className={styles.CloseTrigger}>
          <XIcon />
        </Drawer.CloseTrigger>
      </Drawer.Content>
    </Drawer.Positioner>
  </Drawer.Root>
)

```

#### Solid

```tsx
import { Drawer } from '@ark-ui/solid/drawer'
import { XIcon } from 'lucide-solid'
import styles from 'styles/drawer.module.css'

export const SwipeDirection = () => (
  <Drawer.Root swipeDirection="end">
    <Drawer.Trigger class={styles.Trigger}>Open Right</Drawer.Trigger>
    <Drawer.Backdrop class={styles.Backdrop} />
    <Drawer.Positioner class={styles.Positioner}>
      <Drawer.Content class={styles.Content}>
        <Drawer.Title class={styles.Title}>Right Drawer</Drawer.Title>
        <p>This drawer slides in from the right side.</p>
        <Drawer.CloseTrigger class={styles.CloseTrigger}>
          <XIcon />
        </Drawer.CloseTrigger>
      </Drawer.Content>
    </Drawer.Positioner>
  </Drawer.Root>
)

```

#### Vue

```vue
<script setup lang="ts">
import { Drawer } from '@ark-ui/vue/drawer'
import { XIcon } from 'lucide-vue-next'
import styles from 'styles/drawer.module.css'
</script>

<template>
  <Drawer.Root swipeDirection="end">
    <Drawer.Trigger :class="styles.Trigger">Open Right</Drawer.Trigger>
    <Drawer.Backdrop :class="styles.Backdrop" />
    <Drawer.Positioner :class="styles.Positioner">
      <Drawer.Content :class="styles.Content">
        <Drawer.Title :class="styles.Title">Right Drawer</Drawer.Title>
        <p>This drawer slides in from the right side.</p>
        <Drawer.CloseTrigger :class="styles.CloseTrigger">
          <XIcon />
        </Drawer.CloseTrigger>
      </Drawer.Content>
    </Drawer.Positioner>
  </Drawer.Root>
</template>

```

#### Svelte

```svelte
<script lang="ts">
  import { Drawer } from '@ark-ui/svelte/drawer'
  import { XIcon } from 'lucide-svelte'
  import styles from 'styles/drawer.module.css'
</script>

<Drawer.Root swipeDirection="end">
  <Drawer.Trigger class={styles.Trigger}>Open Right</Drawer.Trigger>
  <Drawer.Backdrop class={styles.Backdrop} />
  <Drawer.Positioner class={styles.Positioner}>
    <Drawer.Content class={styles.Content}>
      <Drawer.Title class={styles.Title}>Right Drawer</Drawer.Title>
      <p>This drawer slides in from the right side.</p>
      <Drawer.CloseTrigger class={styles.CloseTrigger}>
        <XIcon />
      </Drawer.CloseTrigger>
    </Drawer.Content>
  </Drawer.Positioner>
</Drawer.Root>

```

### Snap Points

Use the `snapPoints` prop to define intermediate positions the drawer can snap to.

**Example: snap-points**

#### React

```tsx
import { Drawer } from '@ark-ui/react/drawer'
import { XIcon } from 'lucide-react'
import styles from 'styles/drawer.module.css'

export const SnapPoints = () => (
  <Drawer.Root snapPoints={[0.25, 0.5, 1]} defaultSnapPoint={0.5}>
    <Drawer.Trigger className={styles.Trigger}>Open</Drawer.Trigger>
    <Drawer.Backdrop className={styles.Backdrop} />
    <Drawer.Positioner className={styles.Positioner}>
      <Drawer.Content className={styles.Content}>
        <Drawer.Grabber className={styles.Grabber}>
          <Drawer.GrabberIndicator className={styles.GrabberIndicator} />
        </Drawer.Grabber>
        <Drawer.Title className={styles.Title}>Drawer with Snap Points</Drawer.Title>
        <p>This drawer has multiple snap points at 25%, 50%, and 100% of the viewport height.</p>
        <p>Drag the grabber to snap between different heights, or swipe to dismiss.</p>
        <Drawer.CloseTrigger className={styles.CloseTrigger}>
          <XIcon />
        </Drawer.CloseTrigger>
      </Drawer.Content>
    </Drawer.Positioner>
  </Drawer.Root>
)

```

#### Solid

```tsx
import { Drawer } from '@ark-ui/solid/drawer'
import { XIcon } from 'lucide-solid'
import styles from 'styles/drawer.module.css'

export const SnapPoints = () => (
  <Drawer.Root snapPoints={[0.25, 0.5, 1]} defaultSnapPoint={0.5}>
    <Drawer.Trigger class={styles.Trigger}>Open</Drawer.Trigger>
    <Drawer.Backdrop class={styles.Backdrop} />
    <Drawer.Positioner class={styles.Positioner}>
      <Drawer.Content class={styles.Content}>
        <Drawer.Grabber class={styles.Grabber}>
          <Drawer.GrabberIndicator class={styles.GrabberIndicator} />
        </Drawer.Grabber>
        <Drawer.Title class={styles.Title}>Drawer with Snap Points</Drawer.Title>
        <p>This drawer has multiple snap points at 25%, 50%, and 100% of the viewport height.</p>
        <p>Drag the grabber to snap between different heights, or swipe to dismiss.</p>
        <Drawer.CloseTrigger class={styles.CloseTrigger}>
          <XIcon />
        </Drawer.CloseTrigger>
      </Drawer.Content>
    </Drawer.Positioner>
  </Drawer.Root>
)

```

#### Vue

```vue
<script setup lang="ts">
import { Drawer } from '@ark-ui/vue/drawer'
import { XIcon } from 'lucide-vue-next'
import styles from 'styles/drawer.module.css'
</script>

<template>
  <Drawer.Root :snap-points="[0.25, 0.5, 1]" :default-active-snap-point="0.5">
    <Drawer.Trigger :class="styles.Trigger">Open</Drawer.Trigger>
    <Drawer.Backdrop :class="styles.Backdrop" />
    <Drawer.Positioner :class="styles.Positioner">
      <Drawer.Content :class="styles.Content">
        <Drawer.Grabber :class="styles.Grabber">
          <Drawer.GrabberIndicator :class="styles.GrabberIndicator" />
        </Drawer.Grabber>
        <Drawer.Title :class="styles.Title">Drawer with Snap Points</Drawer.Title>
        <p>This drawer has multiple snap points at 25%, 50%, and 100% of the viewport height.</p>
        <p>Drag the grabber to snap between different heights, or swipe to dismiss.</p>
        <Drawer.CloseTrigger :class="styles.CloseTrigger">
          <XIcon />
        </Drawer.CloseTrigger>
      </Drawer.Content>
    </Drawer.Positioner>
  </Drawer.Root>
</template>

```

#### Svelte

```svelte
<script lang="ts">
  import { Drawer } from '@ark-ui/svelte/drawer'
  import { XIcon } from 'lucide-svelte'
  import styles from 'styles/drawer.module.css'
</script>

<Drawer.Root snapPoints={[0.25, 0.5, 1]} defaultSnapPoint={0.5}>
  <Drawer.Trigger class={styles.Trigger}>Open</Drawer.Trigger>
  <Drawer.Backdrop class={styles.Backdrop} />
  <Drawer.Positioner class={styles.Positioner}>
    <Drawer.Content class={styles.Content}>
      <Drawer.Grabber class={styles.Grabber}>
        <Drawer.GrabberIndicator class={styles.GrabberIndicator} />
      </Drawer.Grabber>
      <Drawer.Title class={styles.Title}>Drawer with Snap Points</Drawer.Title>
      <p>This drawer has multiple snap points at 25%, 50%, and 100% of the viewport height.</p>
      <p>Drag the grabber to snap between different heights, or swipe to dismiss.</p>
      <Drawer.CloseTrigger class={styles.CloseTrigger}>
        <XIcon />
      </Drawer.CloseTrigger>
    </Drawer.Content>
  </Drawer.Positioner>
</Drawer.Root>

```

### Modal

Set `modal` to `false` to allow interaction with the rest of the page while the drawer is open.

**Example: modal**

#### React

```tsx
import { Drawer } from '@ark-ui/react/drawer'
import { XIcon } from 'lucide-react'
import styles from 'styles/drawer.module.css'

export const Modal = () => (
  <Drawer.Root modal>
    <Drawer.Trigger className={styles.Trigger}>Open</Drawer.Trigger>
    <Drawer.Backdrop className={styles.Backdrop} />
    <Drawer.Positioner className={styles.Positioner}>
      <Drawer.Content className={styles.Content}>
        <Drawer.Grabber className={styles.Grabber}>
          <Drawer.GrabberIndicator className={styles.GrabberIndicator} />
        </Drawer.Grabber>
        <Drawer.Title className={styles.Title}>Modal Drawer</Drawer.Title>
        <Drawer.CloseTrigger className={styles.CloseTrigger}>
          <XIcon />
        </Drawer.CloseTrigger>
      </Drawer.Content>
    </Drawer.Positioner>
  </Drawer.Root>
)

```

#### Solid

```tsx
import { Drawer } from '@ark-ui/solid/drawer'
import { XIcon } from 'lucide-solid'
import styles from 'styles/drawer.module.css'

export const Modal = () => (
  <Drawer.Root modal>
    <Drawer.Trigger class={styles.Trigger}>Open</Drawer.Trigger>
    <Drawer.Backdrop class={styles.Backdrop} />
    <Drawer.Positioner class={styles.Positioner}>
      <Drawer.Content class={styles.Content}>
        <Drawer.Grabber class={styles.Grabber}>
          <Drawer.GrabberIndicator class={styles.GrabberIndicator} />
        </Drawer.Grabber>
        <Drawer.Title class={styles.Title}>Modal Drawer</Drawer.Title>
        <Drawer.CloseTrigger class={styles.CloseTrigger}>
          <XIcon />
        </Drawer.CloseTrigger>
      </Drawer.Content>
    </Drawer.Positioner>
  </Drawer.Root>
)

```

#### Vue

```vue
<script setup lang="ts">
import { Drawer } from '@ark-ui/vue/drawer'
import { XIcon } from 'lucide-vue-next'
import styles from 'styles/drawer.module.css'
</script>

<template>
  <Drawer.Root modal>
    <Drawer.Trigger :class="styles.Trigger">Open</Drawer.Trigger>
    <Drawer.Backdrop :class="styles.Backdrop" />
    <Drawer.Positioner :class="styles.Positioner">
      <Drawer.Content :class="styles.Content">
        <Drawer.Grabber :class="styles.Grabber">
          <Drawer.GrabberIndicator :class="styles.GrabberIndicator" />
        </Drawer.Grabber>
        <Drawer.Title :class="styles.Title">Modal Drawer</Drawer.Title>
        <Drawer.CloseTrigger :class="styles.CloseTrigger">
          <XIcon />
        </Drawer.CloseTrigger>
      </Drawer.Content>
    </Drawer.Positioner>
  </Drawer.Root>
</template>

```

#### Svelte

```svelte
<script lang="ts">
  import { Drawer } from '@ark-ui/svelte/drawer'
  import { XIcon } from 'lucide-svelte'
  import styles from 'styles/drawer.module.css'
</script>

<Drawer.Root modal>
  <Drawer.Trigger class={styles.Trigger}>Open</Drawer.Trigger>
  <Drawer.Backdrop class={styles.Backdrop} />
  <Drawer.Positioner class={styles.Positioner}>
    <Drawer.Content class={styles.Content}>
      <Drawer.Grabber class={styles.Grabber}>
        <Drawer.GrabberIndicator class={styles.GrabberIndicator} />
      </Drawer.Grabber>
      <Drawer.Title class={styles.Title}>Modal Drawer</Drawer.Title>
      <Drawer.CloseTrigger class={styles.CloseTrigger}>
        <XIcon />
      </Drawer.CloseTrigger>
    </Drawer.Content>
  </Drawer.Positioner>
</Drawer.Root>

```

### Controlled

Use the `open` and `onOpenChange` props to control the drawer state.

**Example: controlled**

#### React

```tsx
import { Drawer, type DrawerOpenChangeDetails } from '@ark-ui/react/drawer'
import { XIcon } from 'lucide-react'
import { useState } from 'react'
import button from 'styles/button.module.css'
import styles from 'styles/drawer.module.css'

export const Controlled = () => {
  const [open, setOpen] = useState(false)

  return (
    <>
      <button className={button.Root} onClick={() => setOpen(!open)}>
        {open ? 'Close' : 'Open'} Drawer
      </button>

      <Drawer.Root open={open} onOpenChange={(details: DrawerOpenChangeDetails) => setOpen(details.open)}>
        <Drawer.Backdrop className={styles.Backdrop} />
        <Drawer.Positioner className={styles.Positioner}>
          <Drawer.Content className={styles.Content}>
            <Drawer.Grabber className={styles.Grabber}>
              <Drawer.GrabberIndicator className={styles.GrabberIndicator} />
            </Drawer.Grabber>
            <Drawer.Title className={styles.Title}>Controlled Drawer</Drawer.Title>
            <p>This drawer is controlled via state.</p>
            <Drawer.CloseTrigger className={styles.CloseTrigger}>
              <XIcon />
            </Drawer.CloseTrigger>
          </Drawer.Content>
        </Drawer.Positioner>
      </Drawer.Root>
    </>
  )
}

```

#### Solid

```tsx
import { Drawer, type DrawerOpenChangeDetails } from '@ark-ui/solid/drawer'
import { XIcon } from 'lucide-solid'
import { createSignal } from 'solid-js'
import button from 'styles/button.module.css'
import styles from 'styles/drawer.module.css'

export const Controlled = () => {
  const [open, setOpen] = createSignal(false)

  return (
    <>
      <button class={button.Root} onClick={() => setOpen(!open())}>
        {open() ? 'Close' : 'Open'} Drawer
      </button>

      <Drawer.Root open={open()} onOpenChange={(details: DrawerOpenChangeDetails) => setOpen(details.open)}>
        <Drawer.Backdrop class={styles.Backdrop} />
        <Drawer.Positioner class={styles.Positioner}>
          <Drawer.Content class={styles.Content}>
            <Drawer.Grabber class={styles.Grabber}>
              <Drawer.GrabberIndicator class={styles.GrabberIndicator} />
            </Drawer.Grabber>
            <Drawer.Title class={styles.Title}>Controlled Drawer</Drawer.Title>
            <p>This drawer is controlled via state.</p>
            <Drawer.CloseTrigger class={styles.CloseTrigger}>
              <XIcon />
            </Drawer.CloseTrigger>
          </Drawer.Content>
        </Drawer.Positioner>
      </Drawer.Root>
    </>
  )
}

```

#### Vue

```vue
<script setup lang="ts">
import { Drawer } from '@ark-ui/vue/drawer'
import { XIcon } from 'lucide-vue-next'
import { ref } from 'vue'
import button from 'styles/button.module.css'
import styles from 'styles/drawer.module.css'

const open = ref(false)
</script>

<template>
  <button :class="button.Root" @click="open = !open">{{ open ? 'Close' : 'Open' }} Drawer</button>

  <Drawer.Root v-model:open="open">
    <Drawer.Backdrop :class="styles.Backdrop" />
    <Drawer.Positioner :class="styles.Positioner">
      <Drawer.Content :class="styles.Content">
        <Drawer.Grabber :class="styles.Grabber">
          <Drawer.GrabberIndicator :class="styles.GrabberIndicator" />
        </Drawer.Grabber>
        <Drawer.Title :class="styles.Title">Controlled Drawer</Drawer.Title>
        <p>This drawer is controlled via state.</p>
        <Drawer.CloseTrigger :class="styles.CloseTrigger">
          <XIcon />
        </Drawer.CloseTrigger>
      </Drawer.Content>
    </Drawer.Positioner>
  </Drawer.Root>
</template>

```

#### Svelte

```svelte
<script lang="ts">
  import { Drawer, type DrawerOpenChangeDetails } from '@ark-ui/svelte/drawer'
  import { XIcon } from 'lucide-svelte'
  import button from 'styles/button.module.css'
  import styles from 'styles/drawer.module.css'

  let open = $state(false)

  const handleOpenChange = (details: DrawerOpenChangeDetails) => {
    open = details.open
  }
</script>

<button class={button.Root} onclick={() => (open = !open)}>
  {open ? 'Close' : 'Open'} Drawer
</button>

<Drawer.Root {open} onOpenChange={handleOpenChange}>
  <Drawer.Backdrop class={styles.Backdrop} />
  <Drawer.Positioner class={styles.Positioner}>
    <Drawer.Content class={styles.Content}>
      <Drawer.Grabber class={styles.Grabber}>
        <Drawer.GrabberIndicator class={styles.GrabberIndicator} />
      </Drawer.Grabber>
      <Drawer.Title class={styles.Title}>Controlled Drawer</Drawer.Title>
      <p>This drawer is controlled via state.</p>
      <Drawer.CloseTrigger class={styles.CloseTrigger}>
        <XIcon />
      </Drawer.CloseTrigger>
    </Drawer.Content>
  </Drawer.Positioner>
</Drawer.Root>

```

### Scrollable

**Example: scrollable**

#### React

```tsx
import { Drawer } from '@ark-ui/react/drawer'
import { XIcon } from 'lucide-react'
import styles from 'styles/drawer.module.css'

export const Scrollable = () => (
  <Drawer.Root>
    <Drawer.Trigger className={styles.Trigger}>Open</Drawer.Trigger>
    <Drawer.Backdrop className={styles.Backdrop} />
    <Drawer.Positioner className={styles.Positioner}>
      <Drawer.Content className={styles.Content}>
        <Drawer.Grabber className={styles.Grabber}>
          <Drawer.GrabberIndicator className={styles.GrabberIndicator} />
        </Drawer.Grabber>
        <Drawer.Title className={styles.Title}>Scrollable Drawer</Drawer.Title>
        <Drawer.CloseTrigger className={styles.CloseTrigger}>
          <XIcon />
        </Drawer.CloseTrigger>
        <div className={styles.Scrollable}>
          {Array.from({ length: 50 }).map((_, index) => (
            <div key={index} className={styles.ScrollableItem}>
              Item {index + 1}
            </div>
          ))}
        </div>
      </Drawer.Content>
    </Drawer.Positioner>
  </Drawer.Root>
)

```

#### Solid

```tsx
import { Drawer } from '@ark-ui/solid/drawer'
import { XIcon } from 'lucide-solid'
import { For } from 'solid-js'
import styles from 'styles/drawer.module.css'

export const Scrollable = () => (
  <Drawer.Root>
    <Drawer.Trigger class={styles.Trigger}>Open</Drawer.Trigger>
    <Drawer.Backdrop class={styles.Backdrop} />
    <Drawer.Positioner class={styles.Positioner}>
      <Drawer.Content class={styles.Content}>
        <Drawer.Grabber class={styles.Grabber}>
          <Drawer.GrabberIndicator class={styles.GrabberIndicator} />
        </Drawer.Grabber>
        <Drawer.Title class={styles.Title}>Scrollable Drawer</Drawer.Title>
        <Drawer.CloseTrigger class={styles.CloseTrigger}>
          <XIcon />
        </Drawer.CloseTrigger>
        <div class={styles.Scrollable}>
          <For each={Array.from({ length: 50 })}>
            {(_, index) => <div class={styles.ScrollableItem}>Item {index() + 1}</div>}
          </For>
        </div>
      </Drawer.Content>
    </Drawer.Positioner>
  </Drawer.Root>
)

```

#### Vue

```vue
<script setup lang="ts">
import { Drawer } from '@ark-ui/vue/drawer'
import { XIcon } from 'lucide-vue-next'
import styles from 'styles/drawer.module.css'
</script>

<template>
  <Drawer.Root>
    <Drawer.Trigger :class="styles.Trigger">Open</Drawer.Trigger>
    <Drawer.Backdrop :class="styles.Backdrop" />
    <Drawer.Positioner :class="styles.Positioner">
      <Drawer.Content :class="styles.Content">
        <Drawer.Grabber :class="styles.Grabber">
          <Drawer.GrabberIndicator :class="styles.GrabberIndicator" />
        </Drawer.Grabber>
        <Drawer.Title :class="styles.Title">Scrollable Drawer</Drawer.Title>
        <Drawer.CloseTrigger :class="styles.CloseTrigger">
          <XIcon />
        </Drawer.CloseTrigger>
        <div :class="styles.Scrollable">
          <div v-for="index in 50" :key="index" :class="styles.ScrollableItem">Item {{ index }}</div>
        </div>
      </Drawer.Content>
    </Drawer.Positioner>
  </Drawer.Root>
</template>

```

#### Svelte

```svelte
<script lang="ts">
  import { Drawer } from '@ark-ui/svelte/drawer'
  import { XIcon } from 'lucide-svelte'
  import styles from 'styles/drawer.module.css'
</script>

<Drawer.Root>
  <Drawer.Trigger class={styles.Trigger}>Open</Drawer.Trigger>
  <Drawer.Backdrop class={styles.Backdrop} />
  <Drawer.Positioner class={styles.Positioner}>
    <Drawer.Content class={styles.Content}>
      <Drawer.Grabber class={styles.Grabber}>
        <Drawer.GrabberIndicator class={styles.GrabberIndicator} />
      </Drawer.Grabber>
      <Drawer.Title class={styles.Title}>Scrollable Drawer</Drawer.Title>
      <Drawer.CloseTrigger class={styles.CloseTrigger}>
        <XIcon />
      </Drawer.CloseTrigger>
      <div class={styles.Scrollable}>
        {#each Array.from({ length: 50 }) as _, index}
          <div class={styles.ScrollableItem}>Item {index + 1}</div>
        {/each}
      </div>
    </Drawer.Content>
  </Drawer.Positioner>
</Drawer.Root>

```

### No Drag Area

Apply the `data-no-drag` attribute to any element inside the drawer to prevent dragging from starting on it.

**Example: no-drag-area**

#### React

```tsx
import { Drawer } from '@ark-ui/react/drawer'
import { XIcon } from 'lucide-react'
import styles from 'styles/drawer.module.css'

export const NoDragArea = () => (
  <Drawer.Root>
    <Drawer.Trigger className={styles.Trigger}>Open</Drawer.Trigger>
    <Drawer.Backdrop className={styles.Backdrop} />
    <Drawer.Positioner className={styles.Positioner}>
      <Drawer.Content className={styles.Content}>
        <Drawer.Grabber className={styles.Grabber}>
          <Drawer.GrabberIndicator className={styles.GrabberIndicator} />
        </Drawer.Grabber>
        <Drawer.Title className={styles.Title}>Drawer Title</Drawer.Title>
        <p data-no-drag>This is the no drag area of the drawer.</p>
        <Drawer.CloseTrigger className={styles.CloseTrigger}>
          <XIcon />
        </Drawer.CloseTrigger>
      </Drawer.Content>
    </Drawer.Positioner>
  </Drawer.Root>
)

```

#### Solid

```tsx
import { Drawer } from '@ark-ui/solid/drawer'
import { XIcon } from 'lucide-solid'
import styles from 'styles/drawer.module.css'

export const NoDragArea = () => (
  <Drawer.Root>
    <Drawer.Trigger class={styles.Trigger}>Open</Drawer.Trigger>
    <Drawer.Backdrop class={styles.Backdrop} />
    <Drawer.Positioner class={styles.Positioner}>
      <Drawer.Content class={styles.Content}>
        <Drawer.Grabber class={styles.Grabber}>
          <Drawer.GrabberIndicator class={styles.GrabberIndicator} />
        </Drawer.Grabber>
        <Drawer.Title class={styles.Title}>Drawer Title</Drawer.Title>
        <p data-no-drag>This is the no drag area of the drawer.</p>
        <Drawer.CloseTrigger class={styles.CloseTrigger}>
          <XIcon />
        </Drawer.CloseTrigger>
      </Drawer.Content>
    </Drawer.Positioner>
  </Drawer.Root>
)

```

#### Vue

```vue
<script setup lang="ts">
import { Drawer } from '@ark-ui/vue/drawer'
import { XIcon } from 'lucide-vue-next'
import styles from 'styles/drawer.module.css'
</script>

<template>
  <Drawer.Root>
    <Drawer.Trigger :class="styles.Trigger">Open</Drawer.Trigger>
    <Drawer.Backdrop :class="styles.Backdrop" />
    <Drawer.Positioner :class="styles.Positioner">
      <Drawer.Content :class="styles.Content">
        <Drawer.Grabber :class="styles.Grabber">
          <Drawer.GrabberIndicator :class="styles.GrabberIndicator" />
        </Drawer.Grabber>
        <Drawer.Title :class="styles.Title">Drawer Title</Drawer.Title>
        <p data-no-drag>This is the no drag area of the drawer.</p>
        <Drawer.CloseTrigger :class="styles.CloseTrigger">
          <XIcon />
        </Drawer.CloseTrigger>
      </Drawer.Content>
    </Drawer.Positioner>
  </Drawer.Root>
</template>

```

#### Svelte

```svelte
<script lang="ts">
  import { Drawer } from '@ark-ui/svelte/drawer'
  import { XIcon } from 'lucide-svelte'
  import styles from 'styles/drawer.module.css'
</script>

<Drawer.Root>
  <Drawer.Trigger class={styles.Trigger}>Open</Drawer.Trigger>
  <Drawer.Backdrop class={styles.Backdrop} />
  <Drawer.Positioner class={styles.Positioner}>
    <Drawer.Content class={styles.Content}>
      <Drawer.Grabber class={styles.Grabber}>
        <Drawer.GrabberIndicator class={styles.GrabberIndicator} />
      </Drawer.Grabber>
      <Drawer.Title class={styles.Title}>Drawer Title</Drawer.Title>
      <p data-no-drag>This is the no drag area of the drawer.</p>
      <Drawer.CloseTrigger class={styles.CloseTrigger}>
        <XIcon />
      </Drawer.CloseTrigger>
    </Drawer.Content>
  </Drawer.Positioner>
</Drawer.Root>

```

### Non Draggable

Set `draggable` to `false` to disable drag-to-dismiss entirely.

**Example: non-draggable**

#### React

```tsx
import { Drawer } from '@ark-ui/react/drawer'
import { XIcon } from 'lucide-react'
import styles from 'styles/drawer.module.css'

export const NonDraggable = () => (
  <Drawer.Root>
    <Drawer.Trigger className={styles.Trigger}>Open</Drawer.Trigger>
    <Drawer.Backdrop className={styles.Backdrop} />
    <Drawer.Positioner className={styles.Positioner}>
      <Drawer.Content className={styles.Content} draggable={false}>
        <Drawer.Grabber className={styles.Grabber}>
          <Drawer.GrabberIndicator className={styles.GrabberIndicator} />
        </Drawer.Grabber>
        <Drawer.Title className={styles.Title}>Drawer Title</Drawer.Title>
        <p>This is the content of the drawer.</p>
        <Drawer.CloseTrigger className={styles.CloseTrigger}>
          <XIcon />
        </Drawer.CloseTrigger>
      </Drawer.Content>
    </Drawer.Positioner>
  </Drawer.Root>
)

```

#### Solid

```tsx
import { Drawer } from '@ark-ui/solid/drawer'
import { XIcon } from 'lucide-solid'
import styles from 'styles/drawer.module.css'

export const NonDraggable = () => (
  <Drawer.Root>
    <Drawer.Trigger class={styles.Trigger}>Open</Drawer.Trigger>
    <Drawer.Backdrop class={styles.Backdrop} />
    <Drawer.Positioner class={styles.Positioner}>
      <Drawer.Content class={styles.Content} draggable={false}>
        <Drawer.Grabber class={styles.Grabber}>
          <Drawer.GrabberIndicator class={styles.GrabberIndicator} />
        </Drawer.Grabber>
        <Drawer.Title class={styles.Title}>Drawer Title</Drawer.Title>
        <p>This is the content of the drawer.</p>
        <Drawer.CloseTrigger class={styles.CloseTrigger}>
          <XIcon />
        </Drawer.CloseTrigger>
      </Drawer.Content>
    </Drawer.Positioner>
  </Drawer.Root>
)

```

#### Vue

```vue
<script setup lang="ts">
import { Drawer } from '@ark-ui/vue/drawer'
import { XIcon } from 'lucide-vue-next'
import styles from 'styles/drawer.module.css'
</script>

<template>
  <Drawer.Root>
    <Drawer.Trigger :class="styles.Trigger">Open</Drawer.Trigger>
    <Drawer.Backdrop :class="styles.Backdrop" />
    <Drawer.Positioner :class="styles.Positioner">
      <Drawer.Content :class="styles.Content" :draggable="false">
        <Drawer.Grabber :class="styles.Grabber">
          <Drawer.GrabberIndicator :class="styles.GrabberIndicator" />
        </Drawer.Grabber>
        <Drawer.Title :class="styles.Title">Drawer Title</Drawer.Title>
        <p>This is the content of the drawer.</p>
        <Drawer.CloseTrigger :class="styles.CloseTrigger">
          <XIcon />
        </Drawer.CloseTrigger>
      </Drawer.Content>
    </Drawer.Positioner>
  </Drawer.Root>
</template>

```

#### Svelte

```svelte
<script lang="ts">
  import { Drawer } from '@ark-ui/svelte/drawer'
  import { XIcon } from 'lucide-svelte'
  import styles from 'styles/drawer.module.css'
</script>

<Drawer.Root>
  <Drawer.Trigger class={styles.Trigger}>Open</Drawer.Trigger>
  <Drawer.Backdrop class={styles.Backdrop} />
  <Drawer.Positioner class={styles.Positioner}>
    <Drawer.Content class={styles.Content} draggable={false}>
      <Drawer.Grabber class={styles.Grabber}>
        <Drawer.GrabberIndicator class={styles.GrabberIndicator} />
      </Drawer.Grabber>
      <Drawer.Title class={styles.Title}>Drawer Title</Drawer.Title>
      <p>This is the content of the drawer.</p>
      <Drawer.CloseTrigger class={styles.CloseTrigger}>
        <XIcon />
      </Drawer.CloseTrigger>
    </Drawer.Content>
  </Drawer.Positioner>
</Drawer.Root>

```

### Indent Background

Use `Drawer.IndentBackground` to create a visual indent effect on the page behind the drawer.

**Example: indent-background**

#### React

```tsx
import { Drawer } from '@ark-ui/react/drawer'
import { XIcon } from 'lucide-react'
import styles from 'styles/drawer-indent.module.css'

export const IndentBackground = () => (
  <Drawer.Stack>
    <div className={styles.Sandbox}>
      <Drawer.IndentBackground className={styles.IndentBackground} />
      <Drawer.Root modal={false}>
        <Drawer.Indent className={styles.Indent}>
          <div className={styles.IndentCenter}>
            <Drawer.Trigger className={styles.Trigger}>Open Drawer</Drawer.Trigger>
          </div>
        </Drawer.Indent>
        <Drawer.Backdrop className={styles.Backdrop} />
        <Drawer.Positioner className={styles.Positioner}>
          <Drawer.Content className={styles.Content}>
            <Drawer.Grabber className={styles.Grabber}>
              <Drawer.GrabberIndicator className={styles.GrabberIndicator} />
            </Drawer.Grabber>
            <Drawer.Title className={styles.Title}>Notifications</Drawer.Title>
            <Drawer.Description className={styles.Description}>You are all caught up. Good job!</Drawer.Description>
            <Drawer.CloseTrigger className={styles.CloseTrigger}>
              <XIcon />
            </Drawer.CloseTrigger>
          </Drawer.Content>
        </Drawer.Positioner>
      </Drawer.Root>
    </div>
  </Drawer.Stack>
)

```

#### Solid

```tsx
import { Drawer } from '@ark-ui/solid/drawer'
import { XIcon } from 'lucide-solid'
import styles from 'styles/drawer.module.css'

export const IndentBackground = () => (
  <Drawer.Stack>
    <Drawer.IndentBackground class={styles.IndentBackground} />
    <Drawer.Indent class={styles.Indent}>
      <Drawer.Root>
        <Drawer.Trigger class={styles.Trigger}>Open Drawer</Drawer.Trigger>
        <Drawer.Backdrop class={styles.Backdrop} />
        <Drawer.Positioner class={styles.Positioner}>
          <Drawer.Content class={styles.Content}>
            <Drawer.Grabber class={styles.Grabber}>
              <Drawer.GrabberIndicator class={styles.GrabberIndicator} />
            </Drawer.Grabber>
            <Drawer.Title class={styles.Title}>Stacked Drawer</Drawer.Title>
            <p>This drawer uses indent background and indent effects when swiped.</p>
            <Drawer.CloseTrigger class={styles.CloseTrigger}>
              <XIcon />
            </Drawer.CloseTrigger>
          </Drawer.Content>
        </Drawer.Positioner>
      </Drawer.Root>
    </Drawer.Indent>
  </Drawer.Stack>
)

```

#### Vue

```vue
<script setup lang="ts">
import { Drawer } from '@ark-ui/vue/drawer'
import { XIcon } from 'lucide-vue-next'
import styles from 'styles/drawer.module.css'
</script>

<template>
  <Drawer.Stack>
    <Drawer.IndentBackground :class="styles.IndentBackground" />
    <Drawer.Indent :class="styles.Indent">
      <Drawer.Root>
        <Drawer.Trigger :class="styles.Trigger">Open Drawer</Drawer.Trigger>
        <Drawer.Backdrop :class="styles.Backdrop" />
        <Drawer.Positioner :class="styles.Positioner">
          <Drawer.Content :class="styles.Content">
            <Drawer.Grabber :class="styles.Grabber">
              <Drawer.GrabberIndicator :class="styles.GrabberIndicator" />
            </Drawer.Grabber>
            <Drawer.Title :class="styles.Title">Stacked Drawer</Drawer.Title>
            <p>This drawer uses indent background and indent effects when swiped.</p>
            <Drawer.CloseTrigger :class="styles.CloseTrigger">
              <XIcon />
            </Drawer.CloseTrigger>
          </Drawer.Content>
        </Drawer.Positioner>
      </Drawer.Root>
    </Drawer.Indent>
  </Drawer.Stack>
</template>

```

#### Svelte

```svelte
<script lang="ts">
  import { Drawer } from '@ark-ui/svelte/drawer'
  import { XIcon } from 'lucide-svelte'
  import styles from 'styles/drawer.module.css'
</script>

<Drawer.Stack>
  <Drawer.IndentBackground class={styles.IndentBackground} />
  <Drawer.Indent class={styles.Indent}>
    <Drawer.Root>
      <Drawer.Trigger class={styles.Trigger}>Open Drawer</Drawer.Trigger>
      <Drawer.Backdrop class={styles.Backdrop} />
      <Drawer.Positioner class={styles.Positioner}>
        <Drawer.Content class={styles.Content}>
          <Drawer.Grabber class={styles.Grabber}>
            <Drawer.GrabberIndicator class={styles.GrabberIndicator} />
          </Drawer.Grabber>
          <Drawer.Title class={styles.Title}>Stacked Drawer</Drawer.Title>
          <p>This drawer uses indent background and indent effects when swiped.</p>
          <Drawer.CloseTrigger class={styles.CloseTrigger}>
            <XIcon />
          </Drawer.CloseTrigger>
        </Drawer.Content>
      </Drawer.Positioner>
    </Drawer.Root>
  </Drawer.Indent>
</Drawer.Stack>

```

### Multiple Triggers

Use the `value` prop on `Drawer.Trigger` to share a single drawer across multiple trigger elements. The
`onTriggerValueChange` callback fires when a different trigger is activated.

**Example: multiple-triggers**

#### React

```tsx
import { Drawer } from '@ark-ui/react/drawer'
import { XIcon } from 'lucide-react'
import { useState } from 'react'
import button from 'styles/button.module.css'
import styles from 'styles/drawer.module.css'
import field from 'styles/field.module.css'

interface User {
  id: string
  name: string
  email: string
}

const users: User[] = [
  { id: '1', name: 'Alice Johnson', email: 'alice@example.com' },
  { id: '2', name: 'Bob Smith', email: 'bob@example.com' },
  { id: '3', name: 'Carol Davis', email: 'carol@example.com' },
]

export const MultipleTriggers = () => {
  const [activeUser, setActiveUser] = useState<User | null>(null)

  return (
    <Drawer.Root
      swipeDirection="end"
      onTriggerValueChange={(e) => {
        setActiveUser(users.find((u) => u.id === e.value) ?? null)
      }}
    >
      <div className={button.Group}>
        {users.map((user) => (
          <Drawer.Trigger key={user.id} value={user.id} className={button.Root}>
            Edit {user.name}
          </Drawer.Trigger>
        ))}
      </div>
      <Drawer.Backdrop className={styles.Backdrop} />
      <Drawer.Positioner className={styles.Positioner}>
        <Drawer.Content className={styles.Content}>
          <Drawer.Grabber className={styles.Grabber}>
            <Drawer.GrabberIndicator className={styles.GrabberIndicator} />
          </Drawer.Grabber>
          <Drawer.Title className={styles.Title}>Edit User</Drawer.Title>
          {activeUser && (
            <div className="stack">
              <div className={field.Root}>
                <label className={field.Label}>Name</label>
                <input className={field.Input} value={activeUser.name} />
              </div>
              <div className={field.Root}>
                <label className={field.Label}>Email</label>
                <input className={field.Input} value={activeUser.email} />
              </div>
              <div className={button.Group}>
                <Drawer.CloseTrigger className={button.Root}>Cancel</Drawer.CloseTrigger>
                <Drawer.CloseTrigger className={button.Root} data-variant="solid">
                  Save Changes
                </Drawer.CloseTrigger>
              </div>
            </div>
          )}
          <Drawer.CloseTrigger className={styles.CloseTrigger}>
            <XIcon />
          </Drawer.CloseTrigger>
        </Drawer.Content>
      </Drawer.Positioner>
    </Drawer.Root>
  )
}

```

#### Solid

```tsx
import { Drawer } from '@ark-ui/solid/drawer'
import { XIcon } from 'lucide-solid'
import { For, Show, createSignal } from 'solid-js'
import button from 'styles/button.module.css'
import styles from 'styles/drawer.module.css'
import field from 'styles/field.module.css'

interface User {
  id: string
  name: string
  email: string
}

const users: User[] = [
  { id: '1', name: 'Alice Johnson', email: 'alice@example.com' },
  { id: '2', name: 'Bob Smith', email: 'bob@example.com' },
  { id: '3', name: 'Carol Davis', email: 'carol@example.com' },
]

export const MultipleTriggers = () => {
  const [activeUser, setActiveUser] = createSignal<User | null>(null)

  return (
    <Drawer.Root
      swipeDirection="end"
      onTriggerValueChange={(e) => {
        setActiveUser(users.find((u) => u.id === e.value) ?? null)
      }}
    >
      <div class={button.Group}>
        <For each={users}>
          {(user) => (
            <Drawer.Trigger value={user.id} class={button.Root}>
              Edit {user.name}
            </Drawer.Trigger>
          )}
        </For>
      </div>
      <Drawer.Backdrop class={styles.Backdrop} />
      <Drawer.Positioner class={styles.Positioner}>
        <Drawer.Content class={styles.Content}>
          <Drawer.Grabber class={styles.Grabber}>
            <Drawer.GrabberIndicator class={styles.GrabberIndicator} />
          </Drawer.Grabber>
          <Drawer.Title class={styles.Title}>Edit User</Drawer.Title>
          <Show when={activeUser()}>
            {(user) => (
              <div class="stack">
                <div class={field.Root}>
                  <label class={field.Label}>Name</label>
                  <input class={field.Input} value={user().name} />
                </div>
                <div class={field.Root}>
                  <label class={field.Label}>Email</label>
                  <input class={field.Input} value={user().email} />
                </div>
                <div class={button.Group}>
                  <Drawer.CloseTrigger class={button.Root}>Cancel</Drawer.CloseTrigger>
                  <Drawer.CloseTrigger class={button.Root} data-variant="solid">
                    Save Changes
                  </Drawer.CloseTrigger>
                </div>
              </div>
            )}
          </Show>
          <Drawer.CloseTrigger class={styles.CloseTrigger}>
            <XIcon />
          </Drawer.CloseTrigger>
        </Drawer.Content>
      </Drawer.Positioner>
    </Drawer.Root>
  )
}

```

#### Vue

```vue
<script setup lang="ts">
import { Drawer } from '@ark-ui/vue/drawer'
import { XIcon } from 'lucide-vue-next'
import { ref } from 'vue'
import button from 'styles/button.module.css'
import styles from 'styles/drawer.module.css'
import field from 'styles/field.module.css'

interface User {
  id: string
  name: string
  email: string
}

const users: User[] = [
  { id: '1', name: 'Alice Johnson', email: 'alice@example.com' },
  { id: '2', name: 'Bob Smith', email: 'bob@example.com' },
  { id: '3', name: 'Carol Davis', email: 'carol@example.com' },
]

const activeUser = ref<User | null>(null)
</script>

<template>
  <Drawer.Root
    swipe-direction="end"
    @trigger-value-change="(e) => (activeUser = users.find((u) => u.id === e.value) ?? null)"
  >
    <div :class="button.Group">
      <Drawer.Trigger v-for="user in users" :key="user.id" :value="user.id" :class="button.Root">
        Edit {{ user.name }}
      </Drawer.Trigger>
    </div>
    <Drawer.Backdrop :class="styles.Backdrop" />
    <Drawer.Positioner :class="styles.Positioner">
      <Drawer.Content :class="styles.Content">
        <Drawer.Grabber :class="styles.Grabber">
          <Drawer.GrabberIndicator :class="styles.GrabberIndicator" />
        </Drawer.Grabber>
        <Drawer.Title :class="styles.Title">Edit User</Drawer.Title>
        <div v-if="activeUser" class="stack">
          <div :class="field.Root">
            <label :class="field.Label">Name</label>
            <input :class="field.Input" :value="activeUser.name" />
          </div>
          <div :class="field.Root">
            <label :class="field.Label">Email</label>
            <input :class="field.Input" :value="activeUser.email" />
          </div>
          <div :class="button.Group">
            <Drawer.CloseTrigger :class="button.Root">Cancel</Drawer.CloseTrigger>
            <Drawer.CloseTrigger :class="button.Root" data-variant="solid">Save Changes</Drawer.CloseTrigger>
          </div>
        </div>
        <Drawer.CloseTrigger :class="styles.CloseTrigger">
          <XIcon />
        </Drawer.CloseTrigger>
      </Drawer.Content>
    </Drawer.Positioner>
  </Drawer.Root>
</template>

```

#### Svelte

```svelte
<script lang="ts">
  import { Drawer } from '@ark-ui/svelte/drawer'
  import { XIcon } from 'lucide-svelte'
  import button from 'styles/button.module.css'
  import styles from 'styles/drawer.module.css'
  import field from 'styles/field.module.css'

  interface User {
    id: string
    name: string
    email: string
  }

  const users: User[] = [
    { id: '1', name: 'Alice Johnson', email: 'alice@example.com' },
    { id: '2', name: 'Bob Smith', email: 'bob@example.com' },
    { id: '3', name: 'Carol Davis', email: 'carol@example.com' },
  ]

  let activeUser = $state<User | null>(null)
</script>

<Drawer.Root
  swipeDirection="end"
  onTriggerValueChange={(e) => {
    activeUser = users.find((u) => u.id === e.value) ?? null
  }}
>
  <div class={button.Group}>
    {#each users as user (user.id)}
      <Drawer.Trigger value={user.id} class={button.Root}>Edit {user.name}</Drawer.Trigger>
    {/each}
  </div>
  <Drawer.Backdrop class={styles.Backdrop} />
  <Drawer.Positioner class={styles.Positioner}>
    <Drawer.Content class={styles.Content}>
      <Drawer.Grabber class={styles.Grabber}>
        <Drawer.GrabberIndicator class={styles.GrabberIndicator} />
      </Drawer.Grabber>
      <Drawer.Title class={styles.Title}>Edit User</Drawer.Title>
      {#if activeUser}
        <div class="stack">
          <div class={field.Root}>
            <label class={field.Label}>Name</label>
            <input class={field.Input} value={activeUser.name} />
          </div>
          <div class={field.Root}>
            <label class={field.Label}>Email</label>
            <input class={field.Input} value={activeUser.email} />
          </div>
          <div class={button.Group}>
            <Drawer.CloseTrigger class={button.Root}>Cancel</Drawer.CloseTrigger>
            <Drawer.CloseTrigger class={button.Root} data-variant="solid">Save Changes</Drawer.CloseTrigger>
          </div>
        </div>
      {/if}
      <Drawer.CloseTrigger class={styles.CloseTrigger}>
        <XIcon />
      </Drawer.CloseTrigger>
    </Drawer.Content>
  </Drawer.Positioner>
</Drawer.Root>

```

### Using the Root Provider

Use the `useDrawer` hook and `Drawer.RootProvider` to control the drawer from outside the component tree.

**Example: root-provider**

#### React

```tsx
import { Drawer, useDrawer } from '@ark-ui/react/drawer'
import { XIcon } from 'lucide-react'
import button from 'styles/button.module.css'
import styles from 'styles/drawer.module.css'

export const RootProvider = () => {
  const drawer = useDrawer({
    defaultSnapPoint: 0.5,
    snapPoints: [0.25, 0.5, 1],
  })

  return (
    <div className="stack">
      <div className="hstack">
        <button className={button.Root} onClick={() => drawer.setOpen(true)}>
          Open via API
        </button>
        <button className={button.Root} onClick={() => drawer.setSnapPoint(0.25)}>
          Set to 25%
        </button>
        <button className={button.Root} onClick={() => drawer.setSnapPoint(1)}>
          Set to 100%
        </button>
      </div>

      <Drawer.RootProvider value={drawer}>
        <Drawer.Backdrop className={styles.Backdrop} />
        <Drawer.Positioner className={styles.Positioner}>
          <Drawer.Content className={styles.Content}>
            <Drawer.Grabber className={styles.Grabber}>
              <Drawer.GrabberIndicator className={styles.GrabberIndicator} />
            </Drawer.Grabber>
            <Drawer.Title className={styles.Title}>Drawer with RootProvider</Drawer.Title>
            <p>This drawer is controlled via the useDrawer hook and RootProvider.</p>
            <p>Active snap point: {drawer.snapPoint}</p>
            <Drawer.CloseTrigger className={styles.CloseTrigger}>
              <XIcon />
            </Drawer.CloseTrigger>
          </Drawer.Content>
        </Drawer.Positioner>
      </Drawer.RootProvider>
    </div>
  )
}

```

#### Solid

```tsx
import { Drawer, useDrawer } from '@ark-ui/solid/drawer'
import { XIcon } from 'lucide-solid'
import button from 'styles/button.module.css'
import styles from 'styles/drawer.module.css'

export const RootProvider = () => {
  const drawer = useDrawer({
    defaultSnapPoint: 0.5,
    snapPoints: [0.25, 0.5, 1],
  })

  return (
    <div class="stack">
      <div class="hstack">
        <button class={button.Root} onClick={() => drawer().setOpen(true)}>
          Open via API
        </button>
        <button class={button.Root} onClick={() => drawer().setSnapPoint(0.25)}>
          Set to 25%
        </button>
        <button class={button.Root} onClick={() => drawer().setSnapPoint(1)}>
          Set to 100%
        </button>
      </div>

      <Drawer.RootProvider value={drawer}>
        <Drawer.Backdrop class={styles.Backdrop} />
        <Drawer.Positioner class={styles.Positioner}>
          <Drawer.Content class={styles.Content}>
            <Drawer.Grabber class={styles.Grabber}>
              <Drawer.GrabberIndicator class={styles.GrabberIndicator} />
            </Drawer.Grabber>
            <Drawer.Title class={styles.Title}>Drawer with RootProvider</Drawer.Title>
            <p>This drawer is controlled via the useDrawer hook and RootProvider.</p>
            <p>Active snap point: {drawer().snapPoint}</p>
            <Drawer.CloseTrigger class={styles.CloseTrigger}>
              <XIcon />
            </Drawer.CloseTrigger>
          </Drawer.Content>
        </Drawer.Positioner>
      </Drawer.RootProvider>
    </div>
  )
}

```

#### Vue

```vue
<script setup lang="ts">
import { Drawer, useDrawer } from '@ark-ui/vue/drawer'
import { XIcon } from 'lucide-vue-next'
import button from 'styles/button.module.css'
import styles from 'styles/drawer.module.css'

const drawer = useDrawer({
  defaultSnapPoint: 0.5,
  snapPoints: [0.25, 0.5, 1],
})
</script>

<template>
  <div class="stack">
    <div class="hstack">
      <button :class="button.Root" @click="drawer.setOpen(true)">Open via API</button>
      <button :class="button.Root" @click="drawer.setSnapPoint(0.25)">Set to 25%</button>
      <button :class="button.Root" @click="drawer.setSnapPoint(1)">Set to 100%</button>
    </div>

    <Drawer.RootProvider :value="drawer">
      <Drawer.Backdrop :class="styles.Backdrop" />
      <Drawer.Positioner :class="styles.Positioner">
        <Drawer.Content :class="styles.Content">
          <Drawer.Grabber :class="styles.Grabber">
            <Drawer.GrabberIndicator :class="styles.GrabberIndicator" />
          </Drawer.Grabber>
          <Drawer.Title :class="styles.Title">Drawer with RootProvider</Drawer.Title>
          <p>This drawer is controlled via the useDrawer hook and RootProvider.</p>
          <p>Active snap point: {{ drawer.snapPoint }}</p>
          <Drawer.CloseTrigger :class="styles.CloseTrigger">
            <XIcon />
          </Drawer.CloseTrigger>
        </Drawer.Content>
      </Drawer.Positioner>
    </Drawer.RootProvider>
  </div>
</template>

```

#### Svelte

```svelte
<script lang="ts">
  import { Drawer, useDrawer } from '@ark-ui/svelte/drawer'
  import { XIcon } from 'lucide-svelte'
  import button from 'styles/button.module.css'
  import styles from 'styles/drawer.module.css'

  const drawer = useDrawer({
    defaultSnapPoint: 0.5,
    snapPoints: [0.25, 0.5, 1],
  })
</script>

<div class="stack">
  <div class="hstack">
    <button class={button.Root} onclick={() => drawer().setOpen(true)}>Open via API</button>
    <button class={button.Root} onclick={() => drawer().setSnapPoint(0.25)}>Set to 25%</button>
    <button class={button.Root} onclick={() => drawer().setSnapPoint(1)}>Set to 100%</button>
  </div>

  <Drawer.RootProvider value={drawer}>
    <Drawer.Backdrop class={styles.Backdrop} />
    <Drawer.Positioner class={styles.Positioner}>
      <Drawer.Content class={styles.Content}>
        <Drawer.Grabber class={styles.Grabber}>
          <Drawer.GrabberIndicator class={styles.GrabberIndicator} />
        </Drawer.Grabber>
        <Drawer.Title class={styles.Title}>Drawer with RootProvider</Drawer.Title>
        <p>This drawer is controlled via the useDrawer hook and RootProvider.</p>
        <p>Active snap point: {drawer().snapPoint}</p>
        <Drawer.CloseTrigger class={styles.CloseTrigger}>
          <XIcon />
        </Drawer.CloseTrigger>
      </Drawer.Content>
    </Drawer.Positioner>
  </Drawer.RootProvider>
</div>

```

## Guides

### Styling by Swipe Direction

The `Drawer.Content` elements expose a `data-swipe-direction` attribute (`up` | `down` | `left` | `right`) that reflects
the resolved physical direction the drawer slides in from.

Target it to apply direction-aware styles — for example, rounded corners on the side facing the viewport:

```css
[data-scope='drawer'][data-part='content'] {
  /* Top drawer */
  &[data-swipe-direction='up'] {
    border-top-left-radius: 0;
    border-top-right-radius: 0;
    border-bottom-left-radius: 16px;
    border-bottom-right-radius: 16px;
  }

  /* Bottom drawer */
  &[data-swipe-direction='down'] {
    border-bottom-left-radius: 0;
    border-bottom-right-radius: 0;
    border-top-left-radius: 16px;
    border-top-right-radius: 16px;
  }

  /* Left drawer */
  &[data-swipe-direction='left'] {
    border-top-left-radius: 0;
    border-bottom-right-radius: 16px;
    border-top-left-radius: 16px;
    border-top-right-radius: 0;
  }

  /* Right drawer */
  &[data-swipe-direction='right'] {
    border-top-right-radius: 0;
    border-bottom-left-radius: 16px;
    border-top-left-radius: 0;
    border-top-right-radius: 16px;
  }
}
```

### Preventing Overdrag Gaps

When a user drags the drawer past its open position, a small gap can appear between the drawer content and the viewport
edge. To prevent this, extend the drawer's background beyond its visible bounds using a CSS `::after` pseudo-element:

```css
[data-scope='drawer'][data-part='content'] {
  --bleed: 3rem;
  position: relative;

  /* Bleed effect */
  &::after {
    content: '';
    position: absolute;
    inset-inline: 0;
    top: 100%;
    height: var(--bleed);
    background-color: inherit;
    pointer-events: none;
  }
}
```

For side drawers, adjust the pseudo-element to extend horizontally:

```css
[data-scope='drawer'][data-part='content'] {
  /* Right drawer */
  &[data-swipe-direction='right']::after {
    inset-inline: auto;
    inset-block: 0;
    top: 0;
    left: 100%;
    width: var(--bleed);
    height: auto;
  }

  /* Left drawer */
  &[data-swipe-direction='left']::after {
    inset-inline: auto;
    inset-block: 0;
    top: 0;
    right: 100%;
    width: var(--bleed);
    height: auto;
  }
}
```

The `::after` element inherits the drawer's background color and sits just outside the content bounds. During overdrag,
the dampened movement reveals this extension instead of an empty gap.

## API Reference

### Props

**Component API Reference**

#### React

**Root Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `closeOnEscape` | `boolean` | No | Whether to close the drawer when the escape key is pressed. |
| `closeOnInteractOutside` | `boolean` | No | Whether to close the drawer when the outside is clicked. |
| `closeThreshold` | `number` | No | The threshold distance for dismissing the drawer. |
| `defaultOpen` | `boolean` | No | The initial open state of the drawer. |
| `defaultSnapPoint` | `SnapPoint` | No |  |
| `finalFocusEl` | `() => MaybeElement` | No | Element to receive focus when the sheet is closed. |
| `id` | `string` | No | The unique identifier of the machine. |
| `ids` | `Partial<{
  backdrop: string
  positioner: string
  content: string
  title: string
  header: string
  trigger: string
  grabber: string
  grabberIndicator: string
  closeTrigger: string
}>` | No | The ids of the elements in the drawer. Useful for composition. |
| `immediate` | `boolean` | No | Whether to synchronize the present change immediately or defer it to the next frame |
| `initialFocusEl` | `() => MaybeElement` | No | Element to receive focus when the sheet is opened. |
| `lazyMount` | `boolean` | No | Whether to enable lazy mounting |
| `modal` | `boolean` | No | Whether to prevent pointer interaction outside the element and hide all content below it. |
| `onEscapeKeyDown` | `(event: KeyboardEvent) => void` | No | Function called when the escape key is pressed |
| `onExitComplete` | `VoidFunction` | No | Function called when the animation ends in the closed state |
| `onFocusOutside` | `(event: FocusOutsideEvent) => void` | No | Function called when the focus is moved outside the component |
| `onInteractOutside` | `(event: InteractOutsideEvent) => void` | No | Function called when an interaction happens outside the component |
| `onOpenChange` | `(details: OpenChangeDetails) => void` | No | Function called when the open state changes. |
| `onPointerDownOutside` | `(event: PointerDownOutsideEvent) => void` | No | Function called when the pointer is pressed down outside the component |
| `onRequestDismiss` | `(event: LayerDismissEvent) => void` | No | Function called when this layer is closed due to a parent layer being closed |
| `onSnapPointChange` | `(details: SnapPointChangeDetails) => void` | No | Callback fired when the snap point changes. |
| `open` | `boolean` | No | Whether the drawer is open. |
| `present` | `boolean` | No | Whether the node is present (controlled by the user) |
| `preventDragOnScroll` | `boolean` | No | Whether to prevent dragging on scrollable elements.
When enabled, the sheet will not start dragging if the user is interacting with a scrollable element. |
| `preventScroll` | `boolean` | No | Whether to prevent scrolling behind the sheet when it's opened |
| `restoreFocus` | `boolean` | No | Whether to restore focus to the element that had focus before the sheet was opened. |
| `role` | `'dialog' | 'alertdialog'` | No | The sheet's role |
| `skipAnimationOnMount` | `boolean` | No | Whether to allow the initial presence animation. |
| `snapPoint` | `SnapPoint` | No | The currently active snap point. |
| `snapPoints` | `SnapPoint[]` | No | The snap points of the drawer.
Array of numbers or strings representing the snap points. |
| `snapToSequentialPoints` | `boolean` | No | Whether the drawer should snap to sequential points when swiping. |
| `stack` | `DrawerStack` | No | Optional external store for coordinating app-level drawer stack visuals
(e.g. indent and background layers). |
| `swipeDirection` | `SwipeDirection` | No | The direction in which the drawer can be swiped. |
| `swipeVelocityThreshold` | `number` | No | The threshold velocity (in pixels/s) for closing the drawer. |
| `trapFocus` | `boolean` | No | Whether to trap focus inside the sheet when it's opened. |
| `unmountOnExit` | `boolean` | No | Whether to unmount on exit. |


**Backdrop Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |


**Backdrop Data Attributes:**

| Attribute | Value |
|-----------|-------|
| `[data-scope]` | drawer |
| `[data-part]` | backdrop |
| `[data-state]` | "open" | "closed" |
| `[data-swiping]` |  |


**Backdrop CSS Variables:**

| Variable | Description |
|----------|-------------|
| `--drawer-swipe-progress` | The drawer swipe progress value for the Backdrop |
| `--drawer-swipe-strength` | The drawer swipe strength value for the Backdrop |
| `--layer-index` | The index of the dismissable in the layer stack |


**CloseTrigger Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |


**Content Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |
| `draggable` | `boolean` | No | Whether the drawer content is draggable.
If false, the drawer can only be dragged by the grabber. |


**Content Data Attributes:**

| Attribute | Value |
|-----------|-------|
| `[data-scope]` | drawer |
| `[data-part]` | content |
| `[data-state]` | "open" | "closed" |
| `[data-expanded]` | Present when expanded |
| `[data-swipe-direction]` |  |
| `[data-swiping]` |  |
| `[data-dragging]` | Present when in the dragging state |
| `[data-nested-drawer-open]` |  |
| `[data-nested-drawer-swiping]` |  |


**Content CSS Variables:**

| Variable | Description |
|----------|-------------|
| `--drawer-translate` | The drawer translate value for the Content |
| `--drawer-translate-x` | The drawer translate x value for the Content |
| `--drawer-translate-y` | The drawer translate y value for the Content |
| `--drawer-snap-point-offset-x` | The offset position for drawer snap point |
| `--drawer-snap-point-offset-y` | The offset position for drawer snap point |
| `--drawer-swipe-movement-x` | The drawer swipe movement x value for the Content |
| `--drawer-swipe-movement-y` | The drawer swipe movement y value for the Content |
| `--drawer-swipe-strength` | The drawer swipe strength value for the Content |
| `--nested-drawers` | The nested drawers value for the Content |
| `--drawer-height` | The height of the Content |
| `--drawer-frontmost-height` | The height of the Content |
| `--layer-index` | The index of the dismissable in the layer stack |
| `--nested-layer-count` | The number of nested drawers |


**GrabberIndicator Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |


**Grabber Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |


**IndentBackground Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |


**Indent Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |


**Positioner Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |


**Positioner Data Attributes:**

| Attribute | Value |
|-----------|-------|
| `[data-scope]` | drawer |
| `[data-part]` | positioner |
| `[data-state]` | "open" | "closed" |
| `[data-swipe-direction]` |  |


**RootProvider Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `value` | `UseDrawerReturn` | Yes |  |
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |
| `immediate` | `boolean` | No | Whether to synchronize the present change immediately or defer it to the next frame |
| `lazyMount` | `boolean` | No | Whether to enable lazy mounting |
| `onExitComplete` | `VoidFunction` | No | Function called when the animation ends in the closed state |
| `present` | `boolean` | No | Whether the node is present (controlled by the user) |
| `skipAnimationOnMount` | `boolean` | No | Whether to allow the initial presence animation. |
| `unmountOnExit` | `boolean` | No | Whether to unmount on exit. |


**Title Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |


**Trigger Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |


**Trigger Data Attributes:**

| Attribute | Value |
|-----------|-------|
| `[data-scope]` | drawer |
| `[data-part]` | trigger |
| `[data-value]` | The value of the item |
| `[data-state]` | "open" | "closed" |
| `[data-current]` | Present when current |


#### Solid

**Root Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `closeOnEscape` | `boolean` | No | Whether to close the drawer when the escape key is pressed. |
| `closeOnInteractOutside` | `boolean` | No | Whether to close the drawer when the outside is clicked. |
| `closeThreshold` | `number` | No | The threshold distance for dismissing the drawer. |
| `defaultOpen` | `boolean` | No | The initial open state of the drawer. |
| `defaultSnapPoint` | `SnapPoint` | No |  |
| `finalFocusEl` | `() => MaybeElement` | No | Element to receive focus when the sheet is closed. |
| `id` | `string` | No | The unique identifier of the machine. |
| `ids` | `Partial<{
  backdrop: string
  positioner: string
  content: string
  title: string
  header: string
  trigger: string
  grabber: string
  grabberIndicator: string
  closeTrigger: string
}>` | No | The ids of the elements in the drawer. Useful for composition. |
| `immediate` | `boolean` | No | Whether to synchronize the present change immediately or defer it to the next frame |
| `initialFocusEl` | `() => MaybeElement` | No | Element to receive focus when the sheet is opened. |
| `lazyMount` | `boolean` | No | Whether to enable lazy mounting |
| `modal` | `boolean` | No | Whether to prevent pointer interaction outside the element and hide all content below it. |
| `onEscapeKeyDown` | `(event: KeyboardEvent) => void` | No | Function called when the escape key is pressed |
| `onExitComplete` | `VoidFunction` | No | Function called when the animation ends in the closed state |
| `onFocusOutside` | `(event: FocusOutsideEvent) => void` | No | Function called when the focus is moved outside the component |
| `onInteractOutside` | `(event: InteractOutsideEvent) => void` | No | Function called when an interaction happens outside the component |
| `onOpenChange` | `(details: OpenChangeDetails) => void` | No | Function called when the open state changes. |
| `onPointerDownOutside` | `(event: PointerDownOutsideEvent) => void` | No | Function called when the pointer is pressed down outside the component |
| `onRequestDismiss` | `(event: LayerDismissEvent) => void` | No | Function called when this layer is closed due to a parent layer being closed |
| `onSnapPointChange` | `(details: SnapPointChangeDetails) => void` | No | Callback fired when the snap point changes. |
| `open` | `boolean` | No | Whether the drawer is open. |
| `present` | `boolean` | No | Whether the node is present (controlled by the user) |
| `preventDragOnScroll` | `boolean` | No | Whether to prevent dragging on scrollable elements.
When enabled, the sheet will not start dragging if the user is interacting with a scrollable element. |
| `preventScroll` | `boolean` | No | Whether to prevent scrolling behind the sheet when it's opened |
| `restoreFocus` | `boolean` | No | Whether to restore focus to the element that had focus before the sheet was opened. |
| `role` | `'dialog' | 'alertdialog'` | No | The sheet's role |
| `skipAnimationOnMount` | `boolean` | No | Whether to allow the initial presence animation. |
| `snapPoint` | `SnapPoint` | No | The currently active snap point. |
| `snapPoints` | `SnapPoint[]` | No | The snap points of the drawer.
Array of numbers or strings representing the snap points. |
| `snapToSequentialPoints` | `boolean` | No | Whether the drawer should snap to sequential points when swiping. |
| `stack` | `DrawerStack` | No | Optional external store for coordinating app-level drawer stack visuals
(e.g. indent and background layers). |
| `swipeDirection` | `SwipeDirection` | No | The direction in which the drawer can be swiped. |
| `swipeVelocityThreshold` | `number` | No | The threshold velocity (in pixels/s) for closing the drawer. |
| `trapFocus` | `boolean` | No | Whether to trap focus inside the sheet when it's opened. |
| `unmountOnExit` | `boolean` | No | Whether to unmount on exit. |


**Backdrop Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `(props: ParentProps<'div'>) => Element` | No | Use the provided child element as the default rendered element, combining their props and behavior. |


**Backdrop Data Attributes:**

| Attribute | Value |
|-----------|-------|
| `[data-scope]` | drawer |
| `[data-part]` | backdrop |
| `[data-state]` | "open" | "closed" |
| `[data-swiping]` |  |


**Backdrop CSS Variables:**

| Variable | Description |
|----------|-------------|
| `--drawer-swipe-progress` | The drawer swipe progress value for the Backdrop |
| `--drawer-swipe-strength` | The drawer swipe strength value for the Backdrop |
| `--layer-index` | The index of the dismissable in the layer stack |


**CloseTrigger Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `(props: ParentProps<'button'>) => Element` | No | Use the provided child element as the default rendered element, combining their props and behavior. |


**Content Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `(props: ParentProps<'div'>) => Element` | No | Use the provided child element as the default rendered element, combining their props and behavior. |
| `draggable` | `boolean` | No | Whether the drawer content is draggable.
If false, the drawer can only be dragged by the grabber. |


**Content Data Attributes:**

| Attribute | Value |
|-----------|-------|
| `[data-scope]` | drawer |
| `[data-part]` | content |
| `[data-state]` | "open" | "closed" |
| `[data-expanded]` | Present when expanded |
| `[data-swipe-direction]` |  |
| `[data-swiping]` |  |
| `[data-dragging]` | Present when in the dragging state |
| `[data-nested-drawer-open]` |  |
| `[data-nested-drawer-swiping]` |  |


**Content CSS Variables:**

| Variable | Description |
|----------|-------------|
| `--drawer-translate` | The drawer translate value for the Content |
| `--drawer-translate-x` | The drawer translate x value for the Content |
| `--drawer-translate-y` | The drawer translate y value for the Content |
| `--drawer-snap-point-offset-x` | The offset position for drawer snap point |
| `--drawer-snap-point-offset-y` | The offset position for drawer snap point |
| `--drawer-swipe-movement-x` | The drawer swipe movement x value for the Content |
| `--drawer-swipe-movement-y` | The drawer swipe movement y value for the Content |
| `--drawer-swipe-strength` | The drawer swipe strength value for the Content |
| `--nested-drawers` | The nested drawers value for the Content |
| `--drawer-height` | The height of the Content |
| `--drawer-frontmost-height` | The height of the Content |
| `--layer-index` | The index of the dismissable in the layer stack |
| `--nested-layer-count` | The number of nested drawers |


**GrabberIndicator Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `(props: ParentProps<'div'>) => Element` | No | Use the provided child element as the default rendered element, combining their props and behavior. |


**Grabber Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `(props: ParentProps<'div'>) => Element` | No | Use the provided child element as the default rendered element, combining their props and behavior. |


**IndentBackground Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `(props: ParentProps<'div'>) => Element` | No | Use the provided child element as the default rendered element, combining their props and behavior. |


**Indent Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `(props: ParentProps<'div'>) => Element` | No | Use the provided child element as the default rendered element, combining their props and behavior. |


**Positioner Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `(props: ParentProps<'div'>) => Element` | No | Use the provided child element as the default rendered element, combining their props and behavior. |


**Positioner Data Attributes:**

| Attribute | Value |
|-----------|-------|
| `[data-scope]` | drawer |
| `[data-part]` | positioner |
| `[data-state]` | "open" | "closed" |
| `[data-swipe-direction]` |  |


**RootProvider Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `value` | `UseDrawerReturn` | Yes |  |
| `immediate` | `boolean` | No | Whether to synchronize the present change immediately or defer it to the next frame |
| `lazyMount` | `boolean` | No | Whether to enable lazy mounting |
| `onExitComplete` | `VoidFunction` | No | Function called when the animation ends in the closed state |
| `present` | `boolean` | No | Whether the node is present (controlled by the user) |
| `skipAnimationOnMount` | `boolean` | No | Whether to allow the initial presence animation. |
| `unmountOnExit` | `boolean` | No | Whether to unmount on exit. |


**Title Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `(props: ParentProps<'h2'>) => Element` | No | Use the provided child element as the default rendered element, combining their props and behavior. |


**Trigger Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `(props: ParentProps<'button'>) => Element` | No | Use the provided child element as the default rendered element, combining their props and behavior. |


**Trigger Data Attributes:**

| Attribute | Value |
|-----------|-------|
| `[data-scope]` | drawer |
| `[data-part]` | trigger |
| `[data-value]` | The value of the item |
| `[data-state]` | "open" | "closed" |
| `[data-current]` | Present when current |


#### Vue

**Root Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `closeOnEscape` | `boolean` | No | Whether to close the drawer when the escape key is pressed. |
| `closeOnInteractOutside` | `boolean` | No | Whether to close the drawer when the outside is clicked. |
| `closeThreshold` | `number` | No | The threshold distance for dismissing the drawer. |
| `defaultOpen` | `boolean` | No | The initial open state of the drawer. |
| `defaultSnapPoint` | `string | number` | No | The default snap point of the drawer. |
| `finalFocusEl` | `() => HTMLElement | null` | No | Element to receive focus when the sheet is closed. |
| `id` | `string` | No | The unique identifier of the machine. |
| `ids` | `Partial<{
  backdrop: string
  positioner: string
  content: string
  title: string
  trigger: string
  grabber: string
  grabberIndicator: string
  closeTrigger: string
}>` | No | The ids of the elements in the drawer. Useful for composition. |
| `initialFocusEl` | `() => HTMLElement | null` | No | Element to receive focus when the sheet is opened. |
| `lazyMount` | `boolean` | No | Whether to enable lazy mounting |
| `modal` | `boolean` | No | Whether to prevent pointer interaction outside the element and hide all content below it. |
| `open` | `boolean` | No | Whether the drawer is open. |
| `preventDragOnScroll` | `boolean` | No | Whether to prevent dragging on scrollable elements. |
| `preventScroll` | `boolean` | No | Whether to prevent scrolling behind the sheet when it's opened |
| `restoreFocus` | `boolean` | No | Whether to restore focus to the element that had focus before the sheet was opened. |
| `role` | `'dialog' | 'alertdialog'` | No | The sheet's role |
| `snapPoint` | `string | number` | No | The currently active snap point. |
| `snapPoints` | `(string | number)[]` | No | The snap points of the drawer. |
| `snapToSequentialPoints` | `boolean` | No | Whether the drawer should snap to sequential points when swiping. |
| `swipeDirection` | `'up' | 'down' | 'left' | 'right'` | No | The direction in which the drawer can be swiped. |
| `swipeVelocityThreshold` | `number` | No | The threshold velocity (in pixels/s) for closing the drawer. |
| `trapFocus` | `boolean` | No | Whether to trap focus inside the sheet when it's opened. |
| `unmountOnExit` | `boolean` | No | Whether to unmount on exit. |


**Backdrop Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |


**Backdrop Data Attributes:**

| Attribute | Value |
|-----------|-------|
| `[data-scope]` | drawer |
| `[data-part]` | backdrop |
| `[data-state]` | "open" | "closed" |
| `[data-swiping]` |  |


**Backdrop CSS Variables:**

| Variable | Description |
|----------|-------------|
| `--drawer-swipe-progress` | The drawer swipe progress value for the Backdrop |
| `--drawer-swipe-strength` | The drawer swipe strength value for the Backdrop |
| `--layer-index` | The index of the dismissable in the layer stack |


**CloseTrigger Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |


**Content Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |
| `draggable` | `boolean` | No | Whether the drawer content is draggable.
If false, the drawer can only be dragged by the grabber. |


**Content Data Attributes:**

| Attribute | Value |
|-----------|-------|
| `[data-scope]` | drawer |
| `[data-part]` | content |
| `[data-state]` | "open" | "closed" |
| `[data-expanded]` | Present when expanded |
| `[data-swipe-direction]` |  |
| `[data-swiping]` |  |
| `[data-dragging]` | Present when in the dragging state |
| `[data-nested-drawer-open]` |  |
| `[data-nested-drawer-swiping]` |  |


**Content CSS Variables:**

| Variable | Description |
|----------|-------------|
| `--drawer-translate` | The drawer translate value for the Content |
| `--drawer-translate-x` | The drawer translate x value for the Content |
| `--drawer-translate-y` | The drawer translate y value for the Content |
| `--drawer-snap-point-offset-x` | The offset position for drawer snap point |
| `--drawer-snap-point-offset-y` | The offset position for drawer snap point |
| `--drawer-swipe-movement-x` | The drawer swipe movement x value for the Content |
| `--drawer-swipe-movement-y` | The drawer swipe movement y value for the Content |
| `--drawer-swipe-strength` | The drawer swipe strength value for the Content |
| `--nested-drawers` | The nested drawers value for the Content |
| `--drawer-height` | The height of the Content |
| `--drawer-frontmost-height` | The height of the Content |
| `--layer-index` | The index of the dismissable in the layer stack |
| `--nested-layer-count` | The number of nested drawers |


**GrabberIndicator Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |


**Grabber Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |


**IndentBackground Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |


**Indent Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |


**Positioner Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |


**Positioner Data Attributes:**

| Attribute | Value |
|-----------|-------|
| `[data-scope]` | drawer |
| `[data-part]` | positioner |
| `[data-state]` | "open" | "closed" |
| `[data-swipe-direction]` |  |


**RootProvider Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `value` | `DrawerApi<PropTypes>` | Yes |  |
| `lazyMount` | `boolean` | No | Whether to enable lazy mounting |
| `unmountOnExit` | `boolean` | No | Whether to unmount on exit. |


**Title Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |


**Trigger Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |


**Trigger Data Attributes:**

| Attribute | Value |
|-----------|-------|
| `[data-scope]` | drawer |
| `[data-part]` | trigger |
| `[data-value]` | The value of the item |
| `[data-state]` | "open" | "closed" |
| `[data-current]` | Present when current |


#### Svelte

**Root Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `closeOnEscape` | `boolean` | No | Whether to close the drawer when the escape key is pressed. |
| `closeOnInteractOutside` | `boolean` | No | Whether to close the drawer when the outside is clicked. |
| `closeThreshold` | `number` | No | The threshold distance for dismissing the drawer. |
| `defaultOpen` | `boolean` | No | The initial open state of the drawer. |
| `defaultSnapPoint` | `SnapPoint` | No |  |
| `finalFocusEl` | `() => MaybeElement` | No | Element to receive focus when the sheet is closed. |
| `id` | `string` | No | The unique identifier of the machine. |
| `ids` | `Partial<{
  backdrop: string
  positioner: string
  content: string
  title: string
  header: string
  trigger: string
  grabber: string
  grabberIndicator: string
  closeTrigger: string
}>` | No | The ids of the elements in the drawer. Useful for composition. |
| `immediate` | `boolean` | No | Whether to synchronize the present change immediately or defer it to the next frame |
| `initialFocusEl` | `() => MaybeElement` | No | Element to receive focus when the sheet is opened. |
| `lazyMount` | `boolean` | No | Whether to enable lazy mounting |
| `modal` | `boolean` | No | Whether to prevent pointer interaction outside the element and hide all content below it. |
| `onEscapeKeyDown` | `(event: KeyboardEvent) => void` | No | Function called when the escape key is pressed |
| `onExitComplete` | `VoidFunction` | No | Function called when the animation ends in the closed state |
| `onFocusOutside` | `(event: FocusOutsideEvent) => void` | No | Function called when the focus is moved outside the component |
| `onInteractOutside` | `(event: InteractOutsideEvent) => void` | No | Function called when an interaction happens outside the component |
| `onOpenChange` | `(details: OpenChangeDetails) => void` | No | Function called when the open state changes. |
| `onPointerDownOutside` | `(event: PointerDownOutsideEvent) => void` | No | Function called when the pointer is pressed down outside the component |
| `onRequestDismiss` | `(event: LayerDismissEvent) => void` | No | Function called when this layer is closed due to a parent layer being closed |
| `onSnapPointChange` | `(details: SnapPointChangeDetails) => void` | No | Callback fired when the snap point changes. |
| `open` | `boolean` | No | Whether the drawer is open. |
| `present` | `boolean` | No | Whether the node is present (controlled by the user) |
| `preventDragOnScroll` | `boolean` | No | Whether to prevent dragging on scrollable elements.
When enabled, the sheet will not start dragging if the user is interacting with a scrollable element. |
| `preventScroll` | `boolean` | No | Whether to prevent scrolling behind the sheet when it's opened |
| `restoreFocus` | `boolean` | No | Whether to restore focus to the element that had focus before the sheet was opened. |
| `role` | `'alertdialog' | 'dialog'` | No | The sheet's role |
| `skipAnimationOnMount` | `boolean` | No | Whether to allow the initial presence animation. |
| `snapPoint` | `SnapPoint` | No | The currently active snap point. |
| `snapPoints` | `SnapPoint[]` | No | The snap points of the drawer.
Array of numbers or strings representing the snap points. |
| `snapToSequentialPoints` | `boolean` | No | Whether the drawer should snap to sequential points when swiping. |
| `stack` | `DrawerStack` | No | Optional external store for coordinating app-level drawer stack visuals
(e.g. indent and background layers). |
| `swipeDirection` | `SwipeDirection` | No | The direction in which the drawer can be swiped. |
| `swipeVelocityThreshold` | `number` | No | The threshold velocity (in pixels/s) for closing the drawer. |
| `trapFocus` | `boolean` | No | Whether to trap focus inside the sheet when it's opened. |
| `unmountOnExit` | `boolean` | No | Whether to unmount on exit. |


**Backdrop Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `Snippet<[PropsFn<'div'>]>` | No | Use the provided child element as the default rendered element, combining their props and behavior. |
| `ref` | `Element` | No |  |


**Backdrop Data Attributes:**

| Attribute | Value |
|-----------|-------|
| `[data-scope]` | drawer |
| `[data-part]` | backdrop |
| `[data-state]` | "open" | "closed" |
| `[data-swiping]` |  |


**Backdrop CSS Variables:**

| Variable | Description |
|----------|-------------|
| `--drawer-swipe-progress` | The drawer swipe progress value for the Backdrop |
| `--drawer-swipe-strength` | The drawer swipe strength value for the Backdrop |
| `--layer-index` | The index of the dismissable in the layer stack |


**CloseTrigger Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `Snippet<[PropsFn<'button'>]>` | No | Use the provided child element as the default rendered element, combining their props and behavior. |
| `ref` | `Element` | No |  |


**Content Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `Snippet<[PropsFn<'div'>]>` | No | Use the provided child element as the default rendered element, combining their props and behavior. |
| `draggable` | `boolean` | No | Whether the drawer content is draggable.
If false, the drawer can only be dragged by the grabber. |
| `ref` | `Element` | No |  |


**Content Data Attributes:**

| Attribute | Value |
|-----------|-------|
| `[data-scope]` | drawer |
| `[data-part]` | content |
| `[data-state]` | "open" | "closed" |
| `[data-expanded]` | Present when expanded |
| `[data-swipe-direction]` |  |
| `[data-swiping]` |  |
| `[data-dragging]` | Present when in the dragging state |
| `[data-nested-drawer-open]` |  |
| `[data-nested-drawer-swiping]` |  |


**Content CSS Variables:**

| Variable | Description |
|----------|-------------|
| `--drawer-translate` | The drawer translate value for the Content |
| `--drawer-translate-x` | The drawer translate x value for the Content |
| `--drawer-translate-y` | The drawer translate y value for the Content |
| `--drawer-snap-point-offset-x` | The offset position for drawer snap point |
| `--drawer-snap-point-offset-y` | The offset position for drawer snap point |
| `--drawer-swipe-movement-x` | The drawer swipe movement x value for the Content |
| `--drawer-swipe-movement-y` | The drawer swipe movement y value for the Content |
| `--drawer-swipe-strength` | The drawer swipe strength value for the Content |
| `--nested-drawers` | The nested drawers value for the Content |
| `--drawer-height` | The height of the Content |
| `--drawer-frontmost-height` | The height of the Content |
| `--layer-index` | The index of the dismissable in the layer stack |
| `--nested-layer-count` | The number of nested drawers |


**GrabberIndicator Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `Snippet<[PropsFn<'div'>]>` | No | Use the provided child element as the default rendered element, combining their props and behavior. |
| `ref` | `Element` | No |  |


**Grabber Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `Snippet<[PropsFn<'div'>]>` | No | Use the provided child element as the default rendered element, combining their props and behavior. |
| `ref` | `Element` | No |  |


**IndentBackground Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `Snippet<[PropsFn<'div'>]>` | No | Use the provided child element as the default rendered element, combining their props and behavior. |
| `ref` | `Element` | No |  |


**Indent Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `Snippet<[PropsFn<'div'>]>` | No | Use the provided child element as the default rendered element, combining their props and behavior. |
| `ref` | `Element` | No |  |


**Positioner Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `Snippet<[PropsFn<'div'>]>` | No | Use the provided child element as the default rendered element, combining their props and behavior. |
| `ref` | `Element` | No |  |


**Positioner Data Attributes:**

| Attribute | Value |
|-----------|-------|
| `[data-scope]` | drawer |
| `[data-part]` | positioner |
| `[data-state]` | "open" | "closed" |
| `[data-swipe-direction]` |  |


**RootProvider Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `value` | `UseDrawerReturn` | Yes |  |


**Title Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `Snippet<[PropsFn<'h2'>]>` | No | Use the provided child element as the default rendered element, combining their props and behavior. |
| `ref` | `Element` | No |  |


**Trigger Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `Snippet<[PropsFn<'button'>]>` | No | Use the provided child element as the default rendered element, combining their props and behavior. |
| `ref` | `Element` | No |  |


**Trigger Data Attributes:**

| Attribute | Value |
|-----------|-------|
| `[data-scope]` | drawer |
| `[data-part]` | trigger |
| `[data-value]` | The value of the item |
| `[data-state]` | "open" | "closed" |
| `[data-current]` | Present when current |


### Context

**API:**

| Property | Type | Description |
|----------|------|-------------|
| `open` | `boolean` | Whether the drawer is open. |
| `dragging` | `boolean` | Whether the drawer is currently being dragged. |
| `triggerValue` | `string | null` | The value of the active trigger. |
| `setTriggerValue` | `(value: string | null) => void` | Set the active trigger value. |
| `setOpen` | `(open: boolean) => void` | Function to open or close the menu. |
| `snapPoints` | `SnapPoint[]` | The snap points of the drawer. |
| `swipeDirection` | `SwipeDirection` | The swipe direction of the drawer. |
| `snapPoint` | `SnapPoint | null` | The currently active snap point. |
| `setSnapPoint` | `(snapPoint: SnapPoint | null) => void` | Function to set the active snap point. |
| `getOpenPercentage` | `() => number` | Get the current open percentage of the drawer. |
| `getSnapPointIndex` | `() => number` | Get the index of the currently active snap point. |
| `getContentSize` | `() => number | null` | Get the current main-axis size of the drawer content. |


## Accessibility

Complies with the [Dialog WAI-ARIA design pattern](https://www.w3.org/WAI/ARIA/apg/patterns/dialog-modal/).

<KeyBindingsTable id="dialog" />