Components / Overlays

Drawer

A bottom sheet for focused choices, with a drag handle and directional swipe to dismiss.

Preview

Aurora launch

Workspace · Design studio

Active

24 tasks · Updated today

Install

Run in your project
Package manager
npx @getweini/cli@latest add drawer

Use cases

Account picker

Offer a touch-friendly choice panel from the bottom edge.

Quick mobile task

Use a handle for an explicitly selected swipe-to-dismiss surface.

Usage example

Vue
<script setup lang="ts">
import { ref } from 'vue'

const open = ref(false)
const workspace = ref('Design studio')
const destination = ref(workspace.value)

function moveProject() {
  workspace.value = destination.value
  open.value = false
}
</script>

<template>
<Drawer v-model:open="open">
  <DrawerTrigger @click="destination = workspace">Move project</DrawerTrigger>
  <DrawerContent>
    <DrawerHandle />
    <DrawerHeader>
      <DrawerTitle>Move project</DrawerTitle>
      <DrawerDescription>Choose a destination workspace.</DrawerDescription>
    </DrawerHeader>
    <DrawerBody>
      <RadioGroup v-model="destination" name="destination-workspace">
        <template #legend>Destination workspace</template>
        <RadioGroupItem value="Design studio">Design studio</RadioGroupItem>
        <RadioGroupItem value="Product team">Product team</RadioGroupItem>
        <RadioGroupItem value="Launch team">Launch team</RadioGroupItem>
      </RadioGroup>
    </DrawerBody>
    <DrawerFooter>
      <DrawerClose>Cancel</DrawerClose>
      <Button :disabled="destination === workspace" @click="moveProject">Move project</Button>
    </DrawerFooter>
  </DrawerContent>
</Drawer>
<p>Current workspace: {{ workspace }}</p>
</template>

More examples

Choose an account

The handle is a separate drag target above the header.

Choose an account
<script setup lang="ts">
import Drawer from './components/ui/drawer/Drawer.vue'
import DrawerTrigger from './components/ui/drawer/DrawerTrigger.vue'
import DrawerContent from './components/ui/drawer/DrawerContent.vue'
import DrawerHandle from './components/ui/drawer/DrawerHandle.vue'
import DrawerHeader from './components/ui/drawer/DrawerHeader.vue'
import DrawerTitle from './components/ui/drawer/DrawerTitle.vue'
import DrawerBody from './components/ui/drawer/DrawerBody.vue'
import DrawerClose from './components/ui/drawer/DrawerClose.vue'
</script>
<template>
  <Drawer>
    <DrawerTrigger>Choose account</DrawerTrigger>
    <DrawerContent>
      <DrawerHandle />
      <DrawerHeader><DrawerTitle>Accounts</DrawerTitle></DrawerHeader>
      <DrawerBody><button type="button" @click="$emit('choose', 'personal')">Personal</button></DrawerBody>
      <DrawerClose>Cancel</DrawerClose>
    </DrawerContent>
  </Drawer>
</template>

Adjust drag dismissal

Raise the threshold for a drawer with long scrolling content.

Adjust drag dismissal
<script setup lang="ts">
import Drawer from './components/ui/drawer/Drawer.vue'
import DrawerTrigger from './components/ui/drawer/DrawerTrigger.vue'
import DrawerContent from './components/ui/drawer/DrawerContent.vue'
import DrawerHandle from './components/ui/drawer/DrawerHandle.vue'
import DrawerHeader from './components/ui/drawer/DrawerHeader.vue'
import DrawerTitle from './components/ui/drawer/DrawerTitle.vue'
import DrawerBody from './components/ui/drawer/DrawerBody.vue'
import DrawerFooter from './components/ui/drawer/DrawerFooter.vue'
import DrawerClose from './components/ui/drawer/DrawerClose.vue'
</script>
<template>
  <Drawer>
    <DrawerTrigger>Review order</DrawerTrigger>
    <DrawerContent :dismiss-threshold="0.4">
      <DrawerHandle />
      <DrawerHeader><DrawerTitle>Review order</DrawerTitle></DrawerHeader>
      <DrawerBody><p>Order summary and delivery details.</p></DrawerBody>
      <DrawerFooter><DrawerClose>Close review</DrawerClose></DrawerFooter>
    </DrawerContent>
  </Drawer>
</template>

Key API

The most useful props, models, slots, events, and methods for this component.

Key API for Drawer
NameTypeDefault or requirement
v-model:open / defaultOpenbooleanfalse
sidetop | right | bottom | leftbottom
dismissThreshold / velocityThresholdnumber0.25 / 0.5

Drag the handle toward the attached edge to dismiss. The distance threshold defaults to 25% of the drawer (at least 96px), or a faster swipe can close it sooner. You can also dismiss with Escape or an explicit close button.

Implementation notes

  • Dragging begins on DrawerHandle, not on the scrolling body.
  • Reduced-motion preferences disable drag gestures and the snap transition.

Accessibility and localization

  • Drawer shares the modal and focus behavior of Sheet and Dialog.
  • Always provide a visible close path in addition to the drag gesture.
Weini UIDesign system · Vue component library · Free and public