Multi direction React Drawer Component with Tailwind CSS
In modern web applications, a drawer is a great UI pattern to display additional content without navigating away from the current page. Today, I’ll walk you through how I built a reusable drawer component in React with Tailwind CSS that can open from three different directions: right, left, and bottom.
A drawer is essentially a panel that slides in from one side of the screen. It’s often used for navigation menus, forms, or quick actions. The goal of this project was to create a flexible and reusable drawer that could easily be used across different parts of an application.
Key requirements for this drawer:
- Support for three directions: right, left, and bottom.
- Accept children so that it can render any type of content.
- Smooth open and close animations.
- Reusable in different contexts like forms, menus, or previews.
Drawer Component UI
React Drawer Component
1import clsx from 'clsx' 2import { X } from 'lucide-react' 3 4interface DrawerProps { 5 isOpen: boolean 6 onClose: () => void 7 title?: string 8 children: React.ReactNode 9 direction?: 'left' | 'right' | 'bottom' 10} 11 12const Drawer: React.FC<DrawerProps> = ({ 13 isOpen, 14 onClose, 15 title, 16 children, 17 direction = 'right', 18}) => { 19 // Drawer position classes based on direction 20 const positionClasses = 21 direction === 'left' 22 ? 'left-0 top-0 h-full' 23 : direction === 'right' 24 ? 'right-0 top-0 h-full' 25 : 'bottom-0 left-0 w-full' 26 27 // Animation classes based on direction 28 const translateClasses = 29 direction === 'left' 30 ? isOpen 31 ? 'translate-x-0' 32 : '-translate-x-full' 33 : direction === 'right' 34 ? isOpen 35 ? 'translate-x-0' 36 : 'translate-x-full' 37 : isOpen 38 ? 'translate-y-0' 39 : 'translate-y-full' 40 41 return ( 42 <> 43 {/* Overlay */} 44 <div 45 className={clsx( 46 'fixed inset-0 z-40 bg-black/50 transition-opacity duration-300', 47 isOpen ? 'visible opacity-100' : 'invisible opacity-0', 48 )} 49 onClick={onClose} 50 /> 51 52 {/* Drawer Panel */} 53 <div 54 className={clsx( 55 'fixed z-50 flex transform flex-col bg-white shadow-2xl transition-transform duration-300', 56 positionClasses, 57 translateClasses, 58 )} 59 style={{ 60 width: direction === 'bottom' ? '100%' : '30rem', 61 maxWidth: direction === 'bottom' ? '100%' : undefined, 62 }} 63 > 64 {/* Header */} 65 <div className="flex items-center justify-between border-b px-4 py-3"> 66 <h2 className="text-lg font-semibold text-gray-800">{title}</h2> 67 <button 68 type="button" 69 onClick={onClose} 70 className="flex h-8 w-8 items-center justify-center rounded border border-gray-300 bg-white text-gray-600 transition-colors hover:bg-gray-100 hover:text-gray-800" 71 > 72 <X className="h-5 w-5" /> 73 </button> 74 </div> 75 76 {/* Body */} 77 78 <div className="mx-auto w-full max-w-3xl p-4">{children}</div> 79 </div> 80 </> 81 ) 82} 83 84export default Drawer
By accepting children
, the drawer becomes fully reusable. You can place any content inside, from simple text to complex forms.
The drawer uses Tailwind CSS classes for positioning, transitions, and responsiveness. Depending on the direction
prop, it slides in from the right, left, or bottom.
Using the Drawer: Student Form Example
To demonstrate reusability, I created a Student Drawer. This drawer contains a simple form with:
- Three input fields (e.g., Name, Email, Course)
- A Save button
- A Close button
Because the drawer accepts children, it’s easy to pass the form content directly into it, making the component modular.
Drawer accept children, on top of drawer we created student drawer, you can create any component.
To handle form state and action I used react 19
useActionState
hook.
1'use client' 2import { useActionState } from 'react' 3import Button from '../button/button' 4import Drawer from './drawer' 5import Input from './input' 6 7interface StudentDrawerProps { 8 isOpen: boolean 9 onClose: () => void 10 direction?: 'left' | 'right' | 'bottom' 11} 12 13interface FormState { 14 name: string 15 age: string 16 major: string 17 message?: string 18 error?: string 19} 20 21const initialState: FormState = { 22 name: '', 23 age: '', 24 major: '', 25} 26 27const handleStudentSubmit = (prevState: FormState, formData: FormData): FormState => { 28 const name = formData.get('name') as string 29 const age = formData.get('age') as string 30 const major = formData.get('major') as string 31 32 console.log('Submitted:', { name, age, major }) 33 34 if (name && age && major) { 35 return { 36 name: '', 37 age: '', 38 major: '', 39 message: 'Student added successfully!', 40 } 41 } else { 42 return { 43 ...prevState, 44 error: 'Please fill all fields', 45 } 46 } 47} 48 49const StudentDrawer: React.FC<StudentDrawerProps> = ({ isOpen, onClose, direction }) => { 50 const [state, formAction, isPending] = useActionState<FormState, FormData>( 51 handleStudentSubmit, 52 initialState, 53 ) 54 55 return ( 56 <Drawer isOpen={isOpen} onClose={onClose} title="Add New Student" direction={direction}> 57 <form action={formAction} className="flex flex-col gap-4"> 58 <Input 59 label="Name" 60 name="name" 61 placeholder="Enter student name" 62 defaultValue={state.name} 63 /> 64 <Input 65 label="Age" 66 name="age" 67 type="number" 68 placeholder="Enter student Age" 69 defaultValue={state.age} 70 /> 71 <Input 72 label="Major" 73 name="major" 74 placeholder="Enter student major" 75 defaultValue={state.major} 76 /> 77 78 {state.message && <p className="text-green-600">{state.message}</p>} 79 {state.error && <p className="text-red-600">{state.error}</p>} 80 81 <div className="mt-4 flex justify-end gap-3"> 82 <Button variant="outline" type="button" onClick={onClose} disabled={isPending}> 83 Cancel 84 </Button> 85 <Button type="submit" disabled={isPending}> 86 {isPending ? 'Saving...' : 'Save'} 87 </Button> 88 </div> 89 </form> 90 </Drawer> 91 ) 92} 93 94export default StudentDrawer
Demos: Opening Drawer from Different Directions
Here are some live demos to show how flexible the drawer is.
The drawer can be opened by clicking the button, and closed with the close icon in the header.
Open Drawer from Right
This demo shows the drawer sliding in from the right:
Add New Student
Open Drawer from Left
Similarly, the drawer can slide in from the left:
Add New Student
This allows you to have a menu or panel appear from the left side of the screen, perfect for side navigation or settings.
Open Drawer from Bottom
Finally, the drawer can slide up from the bottom of the screen:
Add New Student
This pattern works well for mobile devices or modals, where a bottom sheet is a familiar interaction.
Feel free to adjust the style and use the code in your app.
If this code helps you in your project, or if you need more components, please let me know via LinkedIn.