@rc-component/drawer

An accessible React drawer with portal rendering, nested push behavior, resizable panels, and focus management out of the box.

Library
npm
v1.4.2
425stars
MIT License

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
74/100Good
Development Activity68
Maintenance68
Community80
Maturity60
Momentum20

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
76/100Good
Architecture80
Code Quality85
Innovation65
Learning Curve75

@rc-component/drawer is the drawer primitive behind Ant Design’s Drawer component, built for teams that need panel-style overlays without hand-rolling portal mounting, focus trapping, and animation state. It renders through @rc-component/portal so the panel escapes clipping/overflow containers, uses @rc-component/motion for configurable enter/exit transitions, and manages a mask, keyboard-close, and focus-return lifecycle so the drawer behaves like a native modal dialog.

Beyond the basics, it supports nested drawers that visually “push” parent panels aside via React Context, deprecates legacy width/height props in favor of a placement-aware size prop, and ships an opt-in resizable mode with drag handles and resize callbacks. The library is SSR-safe (it warns rather than crashes when open is used before mount) and exposes semantic classNames/styles objects for styling the mask, wrapper, section, and dragger independently.

What You Get

  • A Drawer component with open/onClose control and left/right/top/bottom placement
  • Portal-based rendering via @rc-component/portal so drawers escape parent overflow/z-index constraints
  • Configurable enter/exit motion via @rc-component/motion, either a static config or a placement-aware function
  • Automatic focus trapping and focus-return-to-trigger on close, with an autoFocus/focusTrap escape hatch
  • Nested drawer support where opening a child drawer pushes parent drawers aside by a configurable distance
  • Optional resizable panels with drag handles and onResize/onResizeStart/onResizeEnd callbacks

Common Use Cases

  • Side-panel navigation menus that slide in from the edge of the viewport
  • Contextual detail/edit panels (e.g. “view record” or “edit item”) layered over a list view
  • Multi-step or nested workflows where opening a follow-up drawer pushes the previous one aside
  • Resizable inspector or settings panels where users drag an edge to change the panel width

Under The Hood

Architecture The library is layered into three components plus isolated hooks: Drawer.tsx is the public entry point that resolves defaults, tracks mount state (to avoid SSR portal issues), and wires focus-return-after-close; it wraps Portal from @rc-component/portal and hands everything to DrawerPopup.tsx. DrawerPopup owns the visual/interaction orchestration — mask rendering via CSSMotion, panel size computation (with backward-compatible width/height fallbacks), the resizable-drag integration through useDrag, and the nested-push behavior via a DrawerContext that lets a child drawer signal its parent to translate aside. DrawerPanel.tsx is the innermost layer, applying role="dialog"/aria-modal semantics and merging refs through RefContext. Cross-cutting concerns are isolated into their own modules — hooks/useDrag.ts for resize-by-drag math, hooks/useFocusable.ts for focus trapping, and util.ts for width/height parsing and SSR warnings — so each concern can change independently of the component tree that consumes it.

Tech Stack Written in TypeScript against React’s function-component and context APIs (peer dependency react/react-dom >=18), with a lean runtime dependency set limited to sibling @rc-component packages (motion, portal, util) plus clsx for class composition. It builds with father (via @rc-component/father-plugin) into CJS/ESM/lib+es outputs, compiles Less styles separately, and documents itself with dumi, the same toolchain used across the broader react-component/Ant Design ecosystem.

Code Quality Tests live under tests/ and run through rc-test (a Jest wrapper) with @testing-library/react, covering focus behavior, motion, ref forwarding, and SSR safety in dedicated spec files, with the main spec file alone exceeding 800 lines. Development-mode warning() calls flag deprecated props (e.g. wrapperClassName, using open without deferring to useEffect in SSR) rather than failing silently. ESLint (with a modern flat config and typescript-eslint), Prettier, and Stylelint are enforced via Husky/lint-staged pre-commit hooks, and GitHub Actions CI runs the shared react-component-ci workflow on every change.

What Makes It Unique Drawer components are a well-worn UI pattern, so the differentiation here is in execution rather than concept: the nested-push behavior is handled transparently through context (a child drawer opening automatically translates its parent aside) without the consumer wiring any state, the resizable mode is opt-in and composable with the same size/placement model used everywhere else, and the SSR guard rails plus deprecation warnings reflect a library that has been iterated on across many consuming applications in the Ant Design ecosystem rather than built for a single use case.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search