Responsive Header
A responsive header component for React that supports drag-to-close gestures on mobile devices and functions as a dialog or drawer on desktop. Built with accessibility, responsiveness, and smooth animations in mind.
A responsive header component for React that supports drag-to-close gestures on mobile devices and functions as a dialog or drawer on desktop. Built with accessibility, responsiveness, and smooth animations in mind.
npm install motion vaul
please add this property in your layout.js or app.js
1'use client';23import { MenuIcon } from 'lucide-react';4import React, {5createContext,6useContext,7useState,8useEffect,9ReactNode,10} from 'react';11import { Drawer as VaulHeader } from 'vaul';1213interface DrawerContextProps {14open: boolean;15setOpen: (open: boolean) => void;16}1718const DrawerContext = createContext<DrawerContextProps | undefined>(undefined);1920const useSidebarDrawer = () => {21const context = useContext(DrawerContext);22if (!context) {23throw new Error('useDrawer must be used within a DrawerProvider');24}25return context;26};2728interface DrawerSidebarProps {29children: ReactNode;30open?: boolean;31setOpen?: (open: boolean) => void;32drawerBtn?: any | null;33}3435export function HeaderDrawer({36children,37open: controlledOpen,38setOpen: controlledSetOpen,39drawerBtn,40}: DrawerSidebarProps) {41const [internalOpen, setInternalOpen] = useState(false);42const open = controlledOpen !== undefined ? controlledOpen : internalOpen;43const setOpen =44controlledSetOpen !== undefined ? controlledSetOpen : setInternalOpen;4546const [isDesktop, setIsDesktop] = useState(false);4748useEffect(() => {49const mediaQuery = window.matchMedia('(min-width: 768px)');50const handleMediaChange = (event: MediaQueryListEvent) => {51setIsDesktop(event.matches);52};5354setIsDesktop(mediaQuery.matches);55mediaQuery.addEventListener('change', handleMediaChange);5657return () => {58mediaQuery.removeEventListener('change', handleMediaChange);59};60}, []);61console.log(drawerBtn);6263return (64<DrawerContext.Provider value={{ open, setOpen }}>65<>66<VaulHeader.Root67open={open}68direction='top'69onOpenChange={setOpen}70dismissible={isDesktop ? false : true}71>72{drawerBtn && (73<VaulHeader.Trigger asChild>{drawerBtn()}</VaulHeader.Trigger>74)}75<VaulHeader.Portal>76<VaulHeader.Overlay className='fixed inset-0 dark:bg-black/40 bg-white/50 backdrop-blur-sm z-50 ' />77<VaulHeader.Content className='dark:bg-gray-900 bg-white border-b z-50 w-full h-fit py-3 fixed top-0 left-0'>78{children}79</VaulHeader.Content>80</VaulHeader.Portal>81</VaulHeader.Root>82</>83</DrawerContext.Provider>84);85}8687export function DrawerContent({ children }: { children: ReactNode }) {88return <>{children}</>;89}
you can use state or default button to control the dialog
const [sidebarOpen, setSidebarOpen] = useState(false)return (<><HeaderDraweropen={headerOpen}setOpen={setHeaderOpen}drawerBtn={()=> { return <button><MenuIcon/></button>}}><DrawerContent></DrawerContent></HeaderDrawer>)
Prop | Type | Description |
---|---|---|
open | boolean | The content to be displayed within the AuroraBackground component. |
setOpen | boolean | this is an function to close and open the drawer |
drawerBtn | function | this is an function for default button, when you don't to use state then you can use drawerBtn |