Drawer
The Drawer component provides a slide-in panel for navigation, forms, or supplementary content, appearing from the edge of the screen.
Installation
$ cbui-cli install drawercbui-cli globally and authenticated your account. This will ensure you can access all the necessary features.Import
1import { Drawer } from '@/crossbuildui/drawer';
2import type { DrawerPlacement, DrawerSize, DrawerRadius, DrawerBackdrop, DrawerAnimationConfig } from '@crossbuildui/drawer'; // Optional: for type safety
3import { Button } from '@/crossbuildui/button'; // Or your preferred button componentexpo-blur for the blur backdrop effect. Ensure it's installed if you plan to use this feature:npm install expo-blur or yarn add expo-blurIt also relies on
react-native-reanimated for animations. Make sure it's installed and configured.Drawer Component
Basic Usage
Control the Drawer's visibility using the isOpen and onOpenChange props. Provide content as children.
1function App() {
2 const [isOpen, setIsOpen] = useState(false);
3
4 return (
5 <View>
6 <Button onPress={() => setIsOpen(true)}>Open Drawer</Button>
7 <Drawer
8 isOpen={isOpen}
9 onOpenChange={setIsOpen}
10 placement="left"
11 >
12 <View style={{ padding: 20, flex: 1 }}>
13 <Text style={{ fontSize: 18, fontWeight: 'bold', marginBottom: 10 }}>Drawer Content</Text>
14 <Text>This is the content of the drawer.</Text>
15 <Button onPress={() => setIsOpen(false)} style={{ marginTop: 20 }}>
16 Close Drawer
17 </Button>
18 </View>
19 </Drawer>
20 </View>
21 );
22}Placement
The placement prop determines which edge the drawer slides in from: left (default), right, top, or bottom.
1function App() {
2 const [isOpen, setIsOpen] = useState(false);
3 const [placement, setPlacement] = useState<DrawerPlacement>('left');
4
5 const openDrawer = (p: DrawerPlacement) => {
6 setPlacement(p);
7 setIsOpen(true);
8 };
9
10 return (
11 <View style={{ flexDirection: 'row', gap: 8, flexWrap: 'wrap' }}>
12 <Button onPress={() => openDrawer('left')}>Open Left</Button>
13 <Button onPress={() => openDrawer('right')}>Open Right</Button>
14 <Button onPress={() => openDrawer('top')}>Open Top</Button>
15 <Button onPress={() => openDrawer('bottom')}>Open Bottom</Button>
16
17 <Drawer isOpen={isOpen} onOpenChange={setIsOpen} placement={placement}>
18 <View style={{ padding: 20, flex: 1, alignItems: 'center', justifyContent: 'center' }}>
19 <Text>Drawer from {placement}</Text>
20 <Button onPress={() => setIsOpen(false)} style={{ marginTop: 10 }}>Close</Button>
21 </View>
22 </Drawer>
23 </View>
24 );
25}Sizes
Adjust the drawer's size (width for left/right, height for top/bottom) using the size prop. Options: sm, md (default), lg, full.
1function App() {
2 const [isOpen, setIsOpen] = useState(false);
3 const [size, setSize] = useState<DrawerSize>('md');
4
5 const openDrawer = (s: DrawerSize) => {
6 setSize(s);
7 setIsOpen(true);
8 };
9
10 return (
11 <View style={{ flexDirection: 'row', gap: 8, flexWrap: 'wrap' }}>
12 <Button onPress={() => openDrawer('sm')}>Small</Button>
13 <Button onPress={() => openDrawer('md')}>Medium</Button>
14 <Button onPress={() => openDrawer('lg')}>Large</Button>
15 <Button onPress={() => openDrawer('full')}>Full</Button>
16
17 <Drawer isOpen={isOpen} onOpenChange={setIsOpen} size={size} placement="left">
18 <View style={{ padding: 20, flex: 1 }}>
19 <Text>Drawer size: {size}</Text>
20 <Button onPress={() => setIsOpen(false)} style={{ marginTop: 10 }}>Close</Button>
21 </View>
22 </Drawer>
23 </View>
24 );
25}Radius
Apply a border radius to the relevant corners of the drawer panel with the radius prop. Options: none, sm, md, lg (default).
1function App() {
2 const [isOpen, setIsOpen] = useState(false);
3 const [radius, setRadius] = useState<DrawerRadius>('lg');
4
5 const openDrawer = (r: DrawerRadius) => {
6 setRadius(r);
7 setIsOpen(true);
8 };
9
10 return (
11 <View style={{ flexDirection: 'row', gap: 8, flexWrap: 'wrap' }}>
12 <Button onPress={() => openDrawer('none')}>None</Button>
13 <Button onPress={() => openDrawer('sm')}>Small</Button>
14 <Button onPress={() => openDrawer('md')}>Medium</Button>
15 <Button onPress={() => openDrawer('lg')}>Large</Button>
16
17 <Drawer isOpen={isOpen} onOpenChange={setIsOpen} radius={radius} placement="right">
18 <View style={{ padding: 20, flex: 1 }}>
19 <Text>Drawer radius: {radius}</Text>
20 <Button onPress={() => setIsOpen(false)} style={{ marginTop: 10 }}>Close</Button>
21 </View>
22 </Drawer>
23 </View>
24 );
25}Backdrop
Customize the backdrop using the backdrop prop: opaque (default, semi-transparent black), blur (requires expo-blur), or transparent.
1function App() {
2 const [isOpen, setIsOpen] = useState(false);
3 const [backdrop, setBackdrop] = useState<DrawerBackdrop>('opaque');
4
5 const openDrawer = (b: DrawerBackdrop) => {
6 setBackdrop(b);
7 setIsOpen(true);
8 };
9
10 return (
11 <View style={{ flexDirection: 'row', gap: 8, flexWrap: 'wrap' }}>
12 <Button onPress={() => openDrawer('opaque')}>Opaque</Button>
13 <Button onPress={() => openDrawer('blur')}>Blur</Button>
14 <Button onPress={() => openDrawer('transparent')}>Transparent</Button>
15
16 <Drawer isOpen={isOpen} onOpenChange={setIsOpen} backdrop={backdrop}>
17 <View style={{ padding: 20, flex: 1 }}>
18 <Text>Backdrop: {backdrop}</Text>
19 <Button onPress={() => setIsOpen(false)} style={{ marginTop: 10 }}>Close</Button>
20 </View>
21 </Drawer>
22 </View>
23 );
24}Dismissable Behavior
By default (isDismissable=), the drawer can be closed by pressing the backdrop or the hardware back button (Android). Set to false to prevent this.
1function App() {
2 const [isOpen, setIsOpen] = useState(false);
3
4 return (
5 <View>
6 <Button onPress={() => setIsOpen(true)}>Open Non-Dismissable Drawer</Button>
7 <Drawer
8 isOpen={isOpen}
9 onOpenChange={setIsOpen}
10 isDismissable={false}
11 >
12 <View style={{ padding: 20, flex: 1 }}>
13 <Text>This drawer cannot be dismissed by backdrop press or back button.</Text>
14 <Button onPress={() => setIsOpen(false)} style={{ marginTop: 20 }}>
15 Close Manually
16 </Button>
17 </View>
18 </Drawer>
19 </View>
20 );
21}Animation Configuration
Customize the opening and closing animation using animationConfig. Supports type: 'spring' (default) or type: 'timing', with respective Reanimated configuration objects.
1function App() {
2 const [isOpenSpring, setIsOpenSpring] = useState(false);
3 const [isOpenTiming, setIsOpenTiming] = useState(false);
4
5 const springConfig: DrawerAnimationConfig = {
6 type: 'spring',
7 springConfig: { damping: 15, stiffness: 120 }
8 };
9
10 const timingConfig: DrawerAnimationConfig = {
11 type: 'timing',
12 timingConfig: { duration: 500 }
13 };
14
15 return (
16 <View style={{ flexDirection: 'row', gap: 8 }}>
17 <Button onPress={() => setIsOpenSpring(true)}>Open with Spring</Button>
18 <Button onPress={() => setIsOpenTiming(true)}>Open with Timing</Button>
19
20 <Drawer isOpen={isOpenSpring} onOpenChange={setIsOpenSpring} animationConfig={springConfig}>
21 <View style={{ padding: 20, flex: 1 }}><Text>Spring Animation</Text><Button onPress={() => setIsOpenSpring(false)}>Close</Button></View>
22 </Drawer>
23
24 <Drawer isOpen={isOpenTiming} onOpenChange={setIsOpenTiming} animationConfig={timingConfig} placement="right">
25 <View style={{ padding: 20, flex: 1 }}><Text>Timing Animation</Text><Button onPress={() => setIsOpenTiming(false)}>Close</Button></View>
26 </Drawer>
27 </View>
28 );
29}Glass Panel
Apply a frosted glass effect to the drawer panel itself using the isGlass prop. Customize its appearance with glassIntensity (0-100) and glassTint ('light', 'dark', or 'default'). When isGlass is true, the panel's direct background color becomes transparent to allow the blur effect. Ensure content within the drawer has sufficient contrast.
1function App() {
2 const [isOpen, setIsOpen] = useState(false);
3
4 return (
5 <View>
6 <Button onPress={() => setIsOpen(true)}>Open Glass Drawer</Button>
7 <Drawer
8 isOpen={isOpen}
9 onOpenChange={setIsOpen}
10 placement="right"
11 isGlass
12 glassIntensity={70}
13 glassTint="light" // or 'dark', 'default'
14 >
15 <View style={{ padding: 20, flex: 1 }}>
16 <Text style={{ fontSize: 18, fontWeight: 'bold', marginBottom: 10, color: 'black' /* Adjust for contrast */ }}>
17 Glass Panel Content
18 </Text>
19 <Button onPress={() => setIsOpen(false)} style={{ marginTop: 20 }}>Close</Button>
20 </View>
21 </Drawer>
22 </View>
23 );
24}Props Overview
| PROP | TYPE | DEFAULT | DESCRIPTION |
|---|---|---|---|
children | ReactNode | - | Content of the drawer panel. |
size | DrawerSize | 'md' | Size of the drawer. |
radius | DrawerRadius | 'lg' | Border radius of the panel. |
placement | DrawerPlacement | 'left' | Edge from which drawer slides. |
isOpen | boolean | - | Controlled open state. |
defaultOpen | boolean | false | Initial open state (uncontrolled). |
isDismissable | boolean | true | If dismissable by backdrop/back press. |
backdrop | DrawerBackdrop | 'opaque' | Backdrop type: transparent, opaque, blur. |
closeButton | ReactNode | - | Custom close button element (user places it). |
animationConfig | DrawerAnimationConfig | { type: 'spring' } | Animation settings (spring/timing). |
styles | DrawerSlotsStyles | - | Styles for slots (backdrop, panel). |
style | StyleProp<ViewStyle> | - | Style for the main drawer panel. |
onOpenChange | (isOpen: boolean) => void | - | Callback on open state change. |
onClose | () => void | - | Callback when drawer closes. |
isGlass | boolean | false | Apply glassmorphism to the drawer panel. |
glassTint | 'default' | 'light' | 'dark' | Theme-derived | Tint for the panel's glass effect. |
glassIntensity | number | 50 | Intensity (0-100) for the panel's glass effect. |
Styling
Customize the Drawer's appearance using the style prop for the main panel or the styles prop for more granular control over internal slots.
The styles prop accepts an object with the following keys:
backdrop: Styles applied to the backdrop overlayVieworBlurView.panel: Styles applied to the main drawer panelAnimated.View. These are merged with and can be overridden by the mainstyleprop.- If
isGlassis true for the panel, aBlurViewis rendered as its background. Thepanelstyles (and mainstyleprop) apply to theAnimated.Viewthat contains thisBlurViewand the children. ThebackgroundColorof the panel will be set to transparent to show the blur.
style prop directly styles the drawer panel and will take precedence over styles defined in styles.panel for conflicting properties.1<Drawer
2 isOpen={true} // Keep open for example
3 onOpenChange={() => {}}
4 placement="left"
5 size="md"
6 style={{ borderWidth: 2, borderColor: 'purple' }} // Styles the main panel
7 styles={{
8 backdrop: { backgroundColor: 'rgba(0, 255, 0, 0.2)' }, // Custom backdrop style
9 panel: { shadowColor: 'blue', shadowOpacity: 0.8, elevation: 10 } // Additional panel styles
10 }}
11>
12 <View style={{ padding: 20, flex: 1 }}>
13 <Text>Styled Drawer</Text>
14 </View>S
15</Drawer>Accessibility
The Drawer component is built on top of React Native's Modal, which handles some accessibility aspects like focus management.
- The
onRequestCloseprop of the underlyingRNModalis connected tohandleClose, aiding in hardware back button dismissal on Android. - Ensure any interactive elements within the drawer, including a custom close button, are properly labeled for accessibility (e.g., using
accessibilityLabel). - The content within the drawer should be structured semantically for screen readers.
- Consider announcing the drawer's appearance or purpose to screen reader users if it's not immediately obvious from the context.