Skip to main content

Drawer

The Drawer component is a sliding panel that appears from the edge of the screen. It's commonly used for navigation menus, filters, or additional content that complements the main view. The component is fully theme-aware and supports light/dark modes through the ThemeProvider.

Import

import { Drawer } from '@lazy-panda-ui/lazy-panda-ui';

Usage

import React, { useState } from 'react';
import { Drawer, Button, Typography } from '@lazy-panda-ui/lazy-panda-ui';

function MyComponent() {
const [open, setOpen] = useState(false);

return (
<>
<Button onPress={() => setOpen(true)}>
Open Drawer
</Button>

<Drawer open={open} onClose={() => setOpen(false)}>
<Drawer.Header>Drawer Title</Drawer.Header>
<Drawer.Content>
<Typography>This is the drawer content.</Typography>
</Drawer.Content>
<Drawer.Footer>
<Button onPress={() => setOpen(false)}>
Close
</Button>
</Drawer.Footer>
</Drawer>
</>
);
}

Props

Drawer Props

NameTypeDefaultDescription
openboolean-Whether the drawer is open/visible
onClose() => void-Callback function called when the drawer should be closed
placement'left' | 'right' | 'top' | 'bottom''right'The side of the screen from which the drawer slides in
size'sm' | 'md' | 'lg' | 'xl' | 'full''md'The size of the drawer as a percentage of screen dimension
closeOnOverlayClickbooleantrueWhether clicking the overlay should close the drawer
closeOnEscapebooleantrueWhether pressing the back button (Android) should close the drawer
trapFocusboolean-Whether focus should be trapped within the drawer (for accessibility)
returnFocusboolean-Whether focus should return to the previous element when drawer closes
overlayColorstringtheme valueOverlay background color (defaults to theme.drawer.backdrop)
childrenReact.ReactNode-The content to be rendered inside the drawer
styleViewStyle-Additional styles for the drawer container

Drawer.Header Props

NameTypeDefaultDescription
childrenReact.ReactNode-The content to be rendered in the header. Can be a string or JSX
showCloseButtonbooleantrueWhether to show the close button in the header
closeButtonPropsany-Props to pass to the close button component
styleViewStyle-Additional styles for the header container

Drawer.Content Props

NameTypeDefaultDescription
childrenReact.ReactNode-The main content to be rendered in the drawer body
styleViewStyle-Additional styles for the content container

Drawer.Footer Props

NameTypeDefaultDescription
childrenReact.ReactNode-The content to be rendered in the footer (typically buttons)
styleViewStyle-Additional styles for the footer container

Examples

Basic Drawer

<Drawer open={open} onClose={onClose}>
<Drawer.Header>Navigation</Drawer.Header>
<Drawer.Content>
<Stack spacing="md">
<Button variant="ghost" onPress={() => navigate('/')}>Home</Button>
<Button variant="ghost" onPress={() => navigate('/profile')}>Profile</Button>
<Button variant="ghost" onPress={() => navigate('/settings')}>Settings</Button>
</Stack>
</Drawer.Content>
</Drawer>

Different Placements

{['left', 'right', 'top', 'bottom'].map((placement) => (
<Button
key={placement}
onPress={() => openDrawer(placement)}
>
Open {placement} drawer
</Button>
))}

<Drawer
open={open}
onClose={onClose}
placement={placement}
>
<Drawer.Header>
{placement} Drawer
</Drawer.Header>
<Drawer.Content>
<Typography>Content for {placement} drawer</Typography>
</Drawer.Content>
</Drawer>

Different Sizes

<Drawer
open={open}
onClose={onClose}
size={size} // 'sm', 'md', 'lg', 'xl', 'full'
>
<Drawer.Header>
{size} Drawer
</Drawer.Header>
<Drawer.Content>
<Typography>Content for {size} drawer</Typography>
</Drawer.Content>
</Drawer>

Animation and Easing

You can control the animation duration and easing globally via the theme. The overlay fade uses the native driver for smoothness, and the slide uses the configured easing.

const customTheme = {
...defaultTheme,
drawer: {
...defaultTheme.drawer,
animation: {
duration: 400,
easing: 'easeOut', // 'linear' | 'easeIn' | 'easeOut' | 'easeInOut'
},
},
};

<ThemeProvider theme={customTheme}>
<Drawer open={open} onClose={onClose} />
{/* ... */}
</ThemeProvider>
<Drawer open={open} onClose={onClose} placement="left">
<Drawer.Header>Menu</Drawer.Header>
<Drawer.Content>
<Stack spacing="md">
<Button
variant="ghost"
onPress={() => navigate('/')}
>
🏠 Home
</Button>
<Button
variant="ghost"
onPress={() => navigate('/profile')}
>
👤 Profile
</Button>
<Button
variant="ghost"
onPress={() => navigate('/settings')}
>
⚙️ Settings
</Button>
<Divider />
<Button
variant="ghost"
onPress={handleLogout}
>
🚪 Logout
</Button>
</Stack>
</Drawer.Content>
</Drawer>

Filter Drawer

<Drawer open={open} onClose={onClose} placement="right">
<Drawer.Header>Filters</Drawer.Header>
<Drawer.Content>
<Stack spacing="lg">
<Select
label="Category"
value={category}
onValueChange={setCategory}
items={categories}
/>
<Slider
label="Price Range"
value={priceRange}
onValueChange={setPriceRange}
minimumValue={0}
maximumValue={1000}
/>
<Stack spacing="sm">
<Typography variant="h6">Brands</Typography>
{availableBrands.map(brand => (
<CheckBox
key={brand}
label={brand}
value={brands.includes(brand)}
onValueChange={(checked) => toggleBrand(brand, checked)}
/>
))}
</Stack>
</Stack>
</Drawer.Content>
<Drawer.Footer>
<Stack direction="row" spacing="sm">
<Button variant="outlined" onPress={resetFilters}>
Reset
</Button>
<Button onPress={applyFilters}>
Apply Filters
</Button>
</Stack>
</Drawer.Footer>
</Drawer>

Theme Customization

The Drawer component is fully configurable through the ThemeProvider. All visual aspects can be customized through the theme configuration.

Theme Configuration

const customTheme = {
...defaultTheme,
drawer: {
backdrop: {
color: '#000000', // Overlay color
opacity: 0.6, // Overlay opacity
},
container: {
backgroundColor: '#ffffff', // Drawer background
borderRadius: 8, // Border radius for corners
shadow: {
color: '#000000',
offset: { width: 0, height: 4 },
opacity: 0.3,
radius: 6,
elevation: 8,
},
},
header: {
backgroundColor: '#f8f9fa', // Header background
borderColor: '#e9ecef', // Header border
borderWidth: 1,
padding: 20,
minHeight: 64,
},
content: {
backgroundColor: '#ffffff', // Content background
padding: 24,
},
footer: {
backgroundColor: '#f8f9fa', // Footer background
borderColor: '#e9ecef', // Footer border
borderWidth: 1,
padding: 20,
minHeight: 64,
},
animation: {
duration: 400, // Animation duration in ms
easing: 'easeInOut', // Animation easing ('linear' | 'easeIn' | 'easeOut' | 'easeInOut')
},
sizes: {
sm: 0.3, // 30% of screen dimension
md: 0.5, // 50% of screen dimension
lg: 0.7, // 70% of screen dimension
xl: 0.9, // 90% of screen dimension
},
},
};

// Use with ThemeProvider
<ThemeProvider theme={customTheme}>
<Drawer open={open} onClose={onClose}>
{/* Drawer content */}
</Drawer>
</ThemeProvider>

Custom Styles

You can also override styles directly using the style props:

<Drawer
open={open}
onClose={onClose}
style={{
backgroundColor: '#f8f9fa',
borderRadius: 16,
}}
overlayColor="rgba(0, 0, 0, 0.8)"
>
<Drawer.Header
style={{
backgroundColor: '#007AFF',
borderBottomWidth: 0,
}}
>
<Typography variant="h6" style={{ color: 'white' }}>
Custom Header
</Typography>
</Drawer.Header>
<Drawer.Content style={{ padding: 32 }}>
<Typography>Custom styled content</Typography>
</Drawer.Content>
<Drawer.Footer style={{ backgroundColor: '#f0f0f0' }}>
<Button>Custom Footer</Button>
</Drawer.Footer>
</Drawer>

Best Practices

  1. Choose appropriate placement: Use left for navigation menus, right for filters/settings, bottom for mobile sheets
  2. Consider content hierarchy: Structure content with Header, Content, and Footer for consistency
  3. Implement proper navigation: Ensure drawer actions don't conflict with main app navigation
  4. Handle mobile views: Use appropriate sizes and consider touch targets
  5. Maintain focus management: Let the component handle focus automatically with trapFocus
  6. Provide clear closing options: Always include a way to close the drawer (close button, overlay click, back button)
  7. Use consistent patterns: Follow the same drawer structure across your app
  8. Theme integration: Use ThemeProvider for consistent styling across light/dark modes

Size Reference

The drawer sizes are calculated as percentages of the screen dimension:

const drawerSizes = {
sm: '25%', // Small drawer
md: '40%', // Medium drawer (default)
lg: '60%', // Large drawer
xl: '80%', // Extra large drawer
full: '100%' // Full screen
};

For horizontal drawers (left/right), percentages apply to screen width. For vertical drawers (top/bottom), percentages apply to screen height.

Accessibility

The Drawer component includes built-in accessibility features:

React Native Accessibility

<Drawer
open={open}
onClose={onClose}
trapFocus={true} // Traps focus within drawer
returnFocus={true} // Returns focus when closed
>
<Drawer.Header>
<Typography variant="h6" accessibilityRole="heading">
Navigation Menu
</Typography>
</Drawer.Header>
<Drawer.Content>
<Button
accessibilityLabel="Go to home page"
accessibilityHint="Navigates to the main home screen"
>
Home
</Button>
</Drawer.Content>
</Drawer>

Note: Drawer.Header includes a close button by default (when showCloseButton is true) that calls onClose from Drawer automatically. You can override its behavior via closeButtonProps.onPress.

Features

  • Modal behavior: Uses React Native Modal for proper overlay handling
  • Back button support: Android back button automatically closes drawer
  • Touch overlay: Tapping outside the drawer closes it (configurable)
  • Focus management: Optional focus trapping and return
  • Screen reader support: Works with accessibility services

Animation Details

The Drawer uses React Native's Animated API for smooth transitions:

  • Slide animation: Drawer slides in from the specified edge
  • Fade animation: Overlay fades in/out simultaneously
  • Duration: Configurable through theme (default: 300ms)
  • Easing: Theme-configurable easing curves ('linear' | 'easeIn' | 'easeOut' | 'easeInOut')
  • Performance: Uses native driver where possible for smooth animations

Platform Differences

Android

  • Back button automatically closes drawer (configurable with closeOnEscape)
  • Hardware back button handling
  • Material Design elevation shadows

iOS

  • iOS-style shadows and styling when using iOS theme
  • Respects safe area insets
  • Gesture-based navigation compatibility
  • Modal - For modal dialogs and overlays
  • Dialog - For confirmation dialogs
  • Alert - For alert messages
  • Backdrop - For overlay backgrounds