Drawer
Slide-out panel component that displays content from any edge of the viewport
Drawer
A slide-out panel component that displays auxiliary content from any edge of the viewport. Provides a less intrusive alternative to modals for navigation menus, settings panels, filters, and contextual information with smooth transitions and flexible dismissal behaviors.
Installation
import { Drawer } from '@geckoui/geckoui';Basic Usage
Drawer Content
This is a basic drawer that slides in from the right side.
import { useState } from 'react';
import { Drawer, Button } from '@geckoui/geckoui';
function Example() {
const [open, setOpen] = useState(false);
return (
<>
<Button onClick={() => setOpen(true)}>Open Drawer</Button>
<Drawer
open={open}
handleClose={() => setOpen(false)}
placement="right"
allowClickOutside
className="w-80 p-6"
>
<h2 className="text-xl font-semibold mb-4">Drawer Content</h2>
<p className="text-gray-600 mb-4">
This is a basic drawer that slides in from the right side.
</p>
<Button onClick={() => setOpen(false)}>Close</Button>
</Drawer>
</>
);
}Props API
| Prop | Type | Default | Description |
|---|---|---|---|
open | boolean | false | Control drawer visibility (required) |
handleClose | () => void | - | Callback when drawer should close |
placement | 'top' | 'right' | 'bottom' | 'left' | 'right' | Edge from which drawer slides |
allowClickOutside | boolean | false | Allow clicking backdrop to close |
dismissOnEscape | boolean | true | Close drawer on Escape key press |
hideBackdrop | boolean | false | Hide backdrop overlay (visual only) |
backdropClassName | string | - | CSS class for backdrop |
className | string | - | CSS class for drawer panel |
children | ReactNode | - | Drawer content |
Examples
Mobile Navigation Menu
const [menuOpen, setMenuOpen] = useState(false);
<Button onClick={() => setMenuOpen(true)}>
<MenuIcon /> Menu
</Button>
<Drawer
open={menuOpen}
handleClose={() => setMenuOpen(false)}
placement="left"
allowClickOutside
dismissOnEscape
className="w-80"
>
<nav className="p-6">
<h2 className="text-lg font-semibold mb-4">Navigation</h2>
<ul className="space-y-2">
<li><a href="#" className="block py-2 px-3 rounded hover:bg-gray-100">Home</a></li>
<li><a href="#" className="block py-2 px-3 rounded hover:bg-gray-100">Products</a></li>
<li><a href="#" className="block py-2 px-3 rounded hover:bg-gray-100">About</a></li>
</ul>
</nav>
</Drawer>Filter Panel
Filters
import { Input, Label, Select, SelectOption } from '@geckoui/geckoui';
const [filtersOpen, setFiltersOpen] = useState(false);
const [category, setCategory] = useState('');
<Drawer
open={filtersOpen}
handleClose={() => setFiltersOpen(false)}
placement="right"
allowClickOutside
backdropClassName="bg-black/60"
className="w-96 p-6"
>
<h2 className="text-xl font-semibold mb-4">Filters</h2>
<div className="space-y-4">
<div className="space-y-1">
<Label>Price Range</Label>
<div className="flex gap-2">
<Input type="number" placeholder="Min" />
<Input type="number" placeholder="Max" />
</div>
</div>
<div className="space-y-1">
<Label>Category</Label>
<Select value={category} onChange={setCategory}>
<SelectOption value="" label="All Categories" />
<SelectOption value="electronics" label="Electronics" />
<SelectOption value="clothing" label="Clothing" />
<SelectOption value="books" label="Books" />
</Select>
</div>
<div className="flex gap-2 pt-4">
<Button variant="ghost" onClick={() => setFiltersOpen(false)}>Cancel</Button>
<Button onClick={() => setFiltersOpen(false)}>Apply Filters</Button>
</div>
</div>
</Drawer>Notification Center (Top)
Notifications
New message received
5 minutes ago
Your order has shipped
2 hours ago
Password changed successfully
1 day ago
<Drawer
open={showNotifications}
handleClose={() => setShowNotifications(false)}
placement="top"
hideBackdrop={false}
allowClickOutside
className="h-96 border-b"
>
<div className="p-6">
<h2 className="text-xl font-semibold mb-4">Notifications</h2>
<div className="space-y-3">
<div className="p-3 border rounded-md">
<p className="font-medium">New message received</p>
<p className="text-sm text-gray-600">5 minutes ago</p>
</div>
</div>
</div>
</Drawer>Bottom Sheet
Quick Actions
<Drawer
open={isBottomSheetOpen}
handleClose={() => setBottomSheetOpen(false)}
placement="bottom"
allowClickOutside
backdropClassName="bg-black/40"
className="h-64 rounded-t-2xl"
>
<div className="p-6">
<h3 className="text-lg font-semibold mb-4">Quick Actions</h3>
<div className="space-y-2">
<button className="w-full text-left py-3 px-4 rounded-md hover:bg-gray-100">
Share
</button>
<button className="w-full text-left py-3 px-4 rounded-md hover:bg-gray-100">
Edit
</button>
</div>
</div>
</Drawer>Without Backdrop
Settings
import { Input, Label } from '@geckoui/geckoui';
<Drawer
open={settingsVisible}
handleClose={() => setSettingsVisible(false)}
placement="right"
hideBackdrop
allowClickOutside={false}
className="w-[600px] border-l"
>
<div className="p-6">
<h2 className="text-xl font-semibold mb-4">Settings</h2>
<div className="space-y-4">
<div className="space-y-1">
<Label htmlFor="profile">Profile</Label>
<Input id="profile" type="text" defaultValue="John Doe" />
</div>
<div className="space-y-1">
<Label htmlFor="settings-email">Email</Label>
<Input id="settings-email" type="email" defaultValue="john@example.com" />
</div>
<Button onClick={() => setSettingsVisible(false)}>Save Settings</Button>
</div>
</div>
</Drawer>Imperative API
Drawer also provides an imperative API for displaying drawers without managing React state:
// Show drawer imperatively
Drawer.show(
<QuickActionsMenu
actions={globalActions}
onActionClick={(action) => {
handleAction(action);
Drawer.dismiss();
}}
/>,
{
placement: "bottom",
handleClose: () => Drawer.dismiss(),
className: "h-80 rounded-t-xl"
}
);
// Dismiss drawer
Drawer.dismiss();Contextual Help Panel
Drawer.show(
<HelpDocumentation topic={currentTopic} />,
{
placement: "right",
handleClose: () => {
logHelpUsage(currentTopic);
Drawer.dismiss();
},
allowClickOutside: true,
className: "w-[500px]"
}
);Placement Options
The drawer can slide in from any edge of the viewport:
"right"- Slides in from the right (default)"left"- Slides in from the left"top"- Slides down from the top"bottom"- Slides up from the bottom
Behavior
Click Outside
When allowClickOutside is enabled, clicking the backdrop or any element outside the drawer will close it. Use with caution as clickable elements beneath the drawer will also trigger the close action.
Escape Key
By default, pressing the Escape key will close the drawer. Disable this with dismissOnEscape={false} for critical workflows.
Backdrop
The backdrop overlay can be:
- Visible (default) - Shows a semi-transparent overlay
- Hidden - Set
hideBackdrop={true}to hide the backdrop visually - Custom - Use
backdropClassNamefor custom styling
Portal Rendering
Drawer requires <GeckoUIPortal /> to be mounted when using the imperative API:
// app/layout.tsx
import { GeckoUIPortal } from '@geckoui/geckoui';
export default function RootLayout({ children }) {
return (
<html>
<body>
{children}
<GeckoUIPortal />
</body>
</html>
);
}Styling
The component uses BEM-style class names:
GeckoUIDrawer- Main containerGeckoUIDrawer__backdrop- Background overlayGeckoUIDrawer__backdrop--visible- Visible backdrop stateGeckoUIDrawer__backdrop--hidden- Hidden backdrop stateGeckoUIDrawer__backdrop--clickthrough- Clickable backdropGeckoUIDrawer__drawer- Drawer panelGeckoUIDrawer__drawer--{placement}- Placement modifierGeckoUIDrawer__drawer--open- Open stateGeckoUIDrawer__drawer--closed- Closed state
Accessibility
- Keyboard navigation with Escape key support
- Click-outside detection
- Focus management
- ARIA
role="dialog"applied - Backdrop prevents interaction with underlying content when visible