Drawer

Drawer is a panel that slides in from one of the four edges of the viewport. It can be opened and closed programmatically using its API methods open() and close(). An optional backdrop dims the content behind the drawer.

Key features:

  • Slide from any edge: Drawer can slide in from left, right, top, or bottom
  • Programmatic control: Open and close via exposed methods like open(), close(), and isOpen()
  • Optional backdrop: Semi-transparent overlay dims content behind the drawer
  • Click-away closing: Automatically close when user clicks outside the drawer
  • Focus management: Automatically handles focus trapping and keyboard navigation
  • Smooth animations: Configurable slide and fade animations with theme variables

Using the Component

The Drawer component is a sliding panel that appears from the edge of the viewport. It's commonly used for navigation menus, settings panels, or contextual filters.

Basic Usage with Imperative API

The simplest way to use a drawer is with the imperative API. Set an id and call open() and close() methods to control its visibility.

<App>
  <VStack>
    <Button label="Open Drawer" onClick="drawer.open()" />
  </VStack>
  <Drawer id="drawer" position="right">
    <Text weight="bold" size="lg">Navigation</Text>
    <VStack marginTop="$space-3">
      <Button label="Profile" variant="ghost" />
      <Button label="Settings" variant="ghost" />
      <Button label="Help" variant="ghost" />
    </VStack>
  </Drawer>
</App>
Example: Basic drawer
<App>
  <VStack>
    <Button label="Open Drawer" onClick="drawer.open()" />
  </VStack>
  <Drawer id="drawer" position="right">
    <Text weight="bold" size="lg">Navigation</Text>
    <VStack marginTop="$space-3">
      <Button label="Profile" variant="ghost" />
      <Button label="Settings" variant="ghost" />
      <Button label="Help" variant="ghost" />
    </VStack>
  </Drawer>
</App>

Different Positions

Use the position property to slide the drawer from different edges of the viewport.

<App scrollWholePage="false">
  <HStack>
    <Button label="Right" onClick="rightDrawer.open()" />
    <Button label="Bottom" onClick="bottomDrawer.open()" />
  </HStack>
  <SpaceFiller />
  <HStack>
    <SpaceFiller />
    <Button label="Left" onClick="leftDrawer.open()" />
    <Button label="Top" onClick="topDrawer.open()" />
  </HStack>
  
  <Drawer id="leftDrawer" position="left">
    <Text>Left Drawer</Text>
  </Drawer>
  <Drawer id="rightDrawer" position="right">
    <Text>Right Drawer</Text>
  </Drawer>
  <Drawer id="topDrawer" position="top">
    <Text>Top Drawer</Text>
  </Drawer>
  <Drawer id="bottomDrawer" position="bottom">
    <Text>Bottom Drawer</Text>
  </Drawer>
</App>
Example: Drawer positions
<App scrollWholePage="false">
  <HStack>
    <Button label="Right" onClick="rightDrawer.open()" />
    <Button label="Bottom" onClick="bottomDrawer.open()" />
  </HStack>
  <SpaceFiller />
  <HStack>
    <SpaceFiller />
    <Button label="Left" onClick="leftDrawer.open()" />
    <Button label="Top" onClick="topDrawer.open()" />
  </HStack>
  
  <Drawer id="leftDrawer" position="left">
    <Text>Left Drawer</Text>
  </Drawer>
  <Drawer id="rightDrawer" position="right">
    <Text>Right Drawer</Text>
  </Drawer>
  <Drawer id="topDrawer" position="top">
    <Text>Top Drawer</Text>
  </Drawer>
  <Drawer id="bottomDrawer" position="bottom">
    <Text>Bottom Drawer</Text>
  </Drawer>
</App>

With Backdrop Control

By default, a semi-transparent backdrop appears behind the drawer. Disable it with hasBackdrop="false" or control whether clicking the backdrop closes the drawer with closeOnClickAway.

<App>
  <VStack horizontalAlignment="end">
    <Button label="With Backdrop" onClick="drawer1.open()" />
    <Button label="No Backdrop" onClick="drawer2.open()" />
    <Button label="Click-away disabled" onClick="drawer3.open()" />
  </VStack>
  
  <Drawer id="drawer1">
    <Text>Click outside to close</Text>
  </Drawer>
  <Drawer id="drawer2" hasBackdrop="false">
    <Text>No backdrop overlay</Text>
  </Drawer>
  <Drawer id="drawer3" closeOnClickAway="false">
    <Text>Click the close button to dismiss</Text>
  </Drawer>
</App>
Example: Backdrop control
<App>
  <VStack horizontalAlignment="end">
    <Button label="With Backdrop" onClick="drawer1.open()" />
    <Button label="No Backdrop" onClick="drawer2.open()" />
    <Button label="Click-away disabled" onClick="drawer3.open()" />
  </VStack>
  
  <Drawer id="drawer1">
    <Text>Click outside to close</Text>
  </Drawer>
  <Drawer id="drawer2" hasBackdrop="false">
    <Text>No backdrop overlay</Text>
  </Drawer>
  <Drawer id="drawer3" closeOnClickAway="false">
    <Text>Click the close button to dismiss</Text>
  </Drawer>
</App>

Drawer with Events

Monitor when the drawer opens or closes using onOpen and onClose events.

<App var.openCount="{0}" var.closeCount="{0}">
  <VStack>
    <Button label="Open Drawer" onClick="drawer.open()" />
    <Text>Opened: {openCount} times</Text>
    <Text>Closed: {closeCount} times</Text>
  </VStack>
  
  <Drawer 
    id="drawer"
    position="right"
    onOpen="openCount++" 
    onClose="closeCount++">
    <Text>I track my open/close events</Text>
  </Drawer>
</App>
Example: Events
<App var.openCount="{0}" var.closeCount="{0}">
  <VStack>
    <Button label="Open Drawer" onClick="drawer.open()" />
    <Text>Opened: {openCount} times</Text>
    <Text>Closed: {closeCount} times</Text>
  </VStack>
  
  <Drawer 
    id="drawer"
    position="right"
    onOpen="openCount++" 
    onClose="closeCount++">
    <Text>I track my open/close events</Text>
  </Drawer>
</App>

Behaviors

This component supports the following behaviors:

BehaviorProperties
Animationanimation, animationOptions
Bookmarkbookmark, bookmarkLevel, bookmarkTitle, bookmarkOmitFromToc
Component Labellabel, labelPosition, labelWidth, labelBreak, required, enabled, shrinkToLabel, style, readOnly
Publish/SubscribesubscribeToTopic
Tooltiptooltip, tooltipMarkdown, tooltipOptions
Styling Variantvariant

Properties

closeButtonVisible

default: true

When true, an ✕ button is displayed in the top-right corner of the drawer that closes it when clicked.

Controls whether the close (✕) button appears in the top-right corner of the drawer.

<App>
  <VStack horizontalAlignment="end">
    <Button label="With Close Button (default)" onClick="drawer1.open()" />
    <Button label="Without Close Button" onClick="drawer2.open()" />
  </VStack>
  
  <Drawer id="drawer1" closeButtonVisible="true">
    <Text>Close button visible</Text>
  </Drawer>
  <Drawer id="drawer2" closeButtonVisible="false">
    <Text>No close button, click outside to dismiss</Text>
  </Drawer>
</App>
Example: closeButtonVisible
<App>
  <VStack horizontalAlignment="end">
    <Button label="With Close Button (default)" onClick="drawer1.open()" />
    <Button label="Without Close Button" onClick="drawer2.open()" />
  </VStack>
  
  <Drawer id="drawer1" closeButtonVisible="true">
    <Text>Close button visible</Text>
  </Drawer>
  <Drawer id="drawer2" closeButtonVisible="false">
    <Text>No close button, click outside to dismiss</Text>
  </Drawer>
</App>

closeOnClickAway

default: true

When true, clicking outside the drawer panel closes it.

hasBackdrop

default: true

When true, a translucent overlay is shown behind the drawer while it is open.

initiallyOpen

default: false

When true, the drawer is open on its first render.

position

default: "left"

Specifies the edge from which the drawer slides in.

Available values: left (default), right, top, bottom

Events

close

Fired when the Drawer is closed.

Fired when the drawer closes (via API, close button, backdrop click, or Escape key).

<App var.status="">
  <VStack>
    <Button label="Open Drawer" onClick="drawer.open()" />
    <Text>{status}</Text>
  </VStack>
  
  <Drawer 
    id="drawer" 
    position="right"
    onClose="status = 'Drawer closed at ' + formatDateTime(getDate())">
    <Text>I triggered the onClose event</Text>
  </Drawer>
</App>
Example: onClose event
<App var.status="">
  <VStack>
    <Button label="Open Drawer" onClick="drawer.open()" />
    <Text>{status}</Text>
  </VStack>
  
  <Drawer 
    id="drawer" 
    position="right"
    onClose="status = 'Drawer closed at ' + formatDateTime(getDate())">
    <Text>I triggered the onClose event</Text>
  </Drawer>
</App>

open

Fired when the Drawer is opened.

Fired when the drawer opens (either via open() API or another trigger).

<App var.status="">
  <VStack >
    <Button label="Open Drawer" onClick="drawer.open()" />
    <Text>{status}</Text>
  </VStack>
  
  <Drawer 
    id="drawer" 
    position="right"
    onOpen="status = 'Drawer opened at ' + formatDateTime(getDate())">
    <Text>I triggered the onOpen event</Text>
  </Drawer>
</App>
Example: onOpen event
<App var.status="">
  <VStack >
    <Button label="Open Drawer" onClick="drawer.open()" />
    <Text>{status}</Text>
  </VStack>
  
  <Drawer 
    id="drawer" 
    position="right"
    onOpen="status = 'Drawer opened at ' + formatDateTime(getDate())">
    <Text>I triggered the onOpen event</Text>
  </Drawer>
</App>

Exposed Methods

close

Closes the Drawer. Invoke with drawerId.close().

Signature: close(): void

isOpen

Returns true when the Drawer is currently open, false otherwise.

Signature: isOpen(): boolean

open

Opens the Drawer. Invoke with drawerId.open().

Signature: open(): void

Styling

Theme Variables

VariableDefault Value (Light)Default Value (Dark)
animationDuration-Drawer250ms250ms
animationEasing-Drawercubic-bezier(0.4, 0, 0.2, 1)cubic-bezier(0.4, 0, 0.2, 1)
backgroundColor-backdrop-Drawerrgba(0, 0, 0, 0.4)rgba(0, 0, 0, 0.4)
backgroundColor-Drawer$backgroundColor-primary$backgroundColor-primary
borderRadius-Drawer$borderRadius$borderRadius
boxShadow-Drawer0 10px 15px -3px rgba(0,0,0,0.1), 0 4px 6px -2px rgba(0,0,0,0.05)0 10px 15px -3px rgba(0,0,0,0.1), 0 4px 6px -2px rgba(0,0,0,0.05)
height-Drawer320px320px
maxHeight-Drawer50%50%
maxWidth-Drawer80%80%
padding-Drawer$space-4$space-4
width-Drawer320px320px
zIndex-Drawer200200