ToggleDrawer

free

ToggleDrawer moves supplementary navigation or settings content into an edge drawer behind a compact trigger.

@floating-ui/reactmotion
Preview
Dark

Workspace

Settings menu moves off-canvas on mobile.

import { type ReactNode } from 'react';
import { PiBell, PiCreditCard, PiGear, PiUser, PiUsers } from 'react-icons/pi'
import Menu from '@/components/ui/Menu';
import ToggleDrawer from '@/components/composites/ToggleDrawer';

const MenuLabel = ({
  icon,
  children,
}: {
  icon: ReactNode;
  children: ReactNode;
}) => (
  <span className="flex min-w-0 items-center gap-2">
    {icon}
    <span className="truncate">{children}</span>
  </span>
);

export default function UsageDemo() {
  return (
    <div className="flex w-full max-w-sm items-center justify-between gap-4 rounded-card border border-border p-4">
      <div className="min-w-0">
        <p className="font-medium">Workspace</p>
        <p className="truncate text-sm text-muted-foreground">
          Settings menu moves off-canvas on mobile.
        </p>
      </div>
      <ToggleDrawer title="Settings" placement="left">
        <Menu
          variant="subtle"
          defaultActiveKeys={['profile']}
          defaultExpandedKeys={['account']}
        >
          <Menu.MenuItem eventKey="profile">
            <MenuLabel icon={<PiUser className="text-base" />}>
              Profile
            </MenuLabel>
          </Menu.MenuItem>
          <Menu.MenuItem eventKey="notifications">
            <MenuLabel icon={<PiBell className="text-base" />}>
              Notifications
            </MenuLabel>
          </Menu.MenuItem>
          <Menu.MenuCollapse
            eventKey="account"
            label={
              <MenuLabel icon={<PiGear className="text-base" />}>
                Account
              </MenuLabel>
            }
          >
            <Menu.MenuItem eventKey="billing">
              <MenuLabel icon={<PiCreditCard className="text-base" />}>
                Billing
              </MenuLabel>
            </Menu.MenuItem>
            <Menu.MenuItem eventKey="team">
              <MenuLabel icon={<PiUsers className="text-base" />}>
                Team
              </MenuLabel>
            </Menu.MenuItem>
          </Menu.MenuCollapse>
        </Menu>
      </ToggleDrawer>
    </div>
  );
}

Installation

Add this component with the NateUI CLI.

npx nateui@latest add ToggleDrawer

API

ToggleDrawer owns its open state and accepts Drawer props except isOpen. The built-in trigger toggles the drawer; use the custom ref when another control needs to open or close it.

PropDescriptionTypeDefault
childrenContent rendered in the scrollable drawer body.ReactNode
placementEdge the drawer opens from. Top and bottom placements add a drag handle.DrawerPlacement'left'
titleContent rendered in the drawer header.`stringReactNode`
bodyClassExtra classes applied to the scrollable body.string
closableShows the default close button in the header.booleantrue
contentClassNameClasses applied to the drawer surface.string
footerContent rendered in the fixed footer region.`stringReactNode`
footerClassExtra classes applied to the footer.string
headerClassExtra classes applied to the header.string
heightDrawer height for top or bottom placement.`stringnumber`
lockScrollLocks document scrolling while the overlay is mounted.booleantrue
onCloseCallback forwarded to Drawer for close requests. Supplying it replaces ToggleDrawer's internal close handler.DrawerOpenChangeHandler
onOpenCallback forwarded to Drawer when it opens.DrawerOpenChangeHandler
overlayClassNameClasses applied to the backdrop overlay.string
refImperative controls for opening and closing the drawer.Ref<ToggleDrawerRef>
shouldCloseOnEscAllows Escape to request closing.booleantrue
shouldCloseOnOverlayClickAllows an overlay click to request closing.booleantrue
widthDrawer width for left or right placement.number280
...nativeDivPropsNative props forwarded to the drawer content element.NativeDrawerDivProps

isOpen is intentionally not part of ToggleDrawer's API because the component manages that state internally.

Types

type DrawerPlacement = 'top' | 'right' | 'bottom' | 'left';
type DrawerOpenChangeHandler = (open: boolean) => void;
 
type ToggleDrawerRef = {
  handleCloseDrawer: () => void;
  handleOpenDrawer: () => void;
};
 
type NativeDrawerDivProps = Omit<ComponentProps<'div'>, 'title'>;