@rc-component/drawer
An accessible React drawer with portal rendering, nested push behavior, resizable panels, and focus management out of the box.
Repository Health
Technical Analysis
@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
Drawercomponent withopen/onClosecontrol and left/right/top/bottom placement - Portal-based rendering via
@rc-component/portalso 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/focusTrapescape 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/onResizeEndcallbacks
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.