Slide-Over Drawer
A free React slide-over drawer component built on the native dialog element, with a spring slide-in and staggered content reveal.
- Automated axe check
- Reduced-motion tested
- SSR build verified
- Motion
- 2 runtime packages
Slide-Over Drawer is a free drawer component for React and Next.js, built with Motion and Tailwind on top of the native dialog element. Opening it fades in a blurred backdrop while a full-height panel glides in from the right or left edge on a soft spring, and once the panel is in flight its content rises into place with a 50 millisecond stagger. Because it uses showModal, the focus trap, top layer rendering and Escape handling come from the browser rather than from JavaScript re-implementations.
The component is fully controlled through open and onClose props, picks its entry edge with the side prop, locks background scroll while open, and never lets the exit animation delay focus: the moment the drawer closes, focus returns to the button that opened it. The visible title labels the drawer for screen readers, the close button is a real button with an accessible name, a click on the backdrop closes the drawer, and the staggered content is real DOM that assistive technology can read throughout the animation. With prefers-reduced-motion enabled there is no slide and no stagger; panel and content fade in over 150 milliseconds or less.
Details, one glide away
Slide-overs keep context on screen while secondary tasks get a full panel. Open the drawer to see the slide and content stagger.
Customise
Component settings update the preview and generated code together.
Content
Appearance
Behaviour
Install
Add the @te registry to your components.json once:
{
"registries": {
"@te": "https://templateempire.io/r/{name}.json"
}
}Then install the component:
npx shadcn@latest add @te/modal-drawerUsage
"use client";
import { useState } from "react";
import { ModalDrawer } from "@/components/modal-drawer";
export default function Page() {
const [open, setOpen] = useState(false);
return (
<>
<button type="button" onClick={() => setOpen(true)}>
Open drawer
</button>
<ModalDrawer
open={open}
onClose={() => setOpen(false)}
title="Notification settings"
side="right"
>
<p>Choose how the workspace reaches you.</p>
</ModalDrawer>
</>
);
}Props
| Prop | Type | Default | Description |
|---|---|---|---|
| open | boolean | required | Controlled open state. |
| onClose | () => void | required | Called when the drawer asks to close: Escape, a click on the backdrop or the close button. |
| title | string | required | Visible h2 heading that names the drawer via aria-labelledby. |
| children | ReactNode | required | Drawer body; direct children stagger on entry. |
| side | "right" | "left" | "right" | Edge the panel enters from. |
| className | string | undefined | Additional classes merged onto the panel. |
Customisation
Slide-Over Drawer declares no tokens of its own; the panel and backdrop consume the global semantic tokens through Tailwind utilities, and the slide uses the shared springSoft preset from lib/motion.ts, so retuning that preset retunes every soft-spring component together. The className prop is merged onto the panel, which is the usual place to change its width or padding:
:root {
--border: oklch(from var(--color-neutral-50) l c h / 0.16);
}
<ModalDrawer
open={open}
onClose={() => setOpen(false)}
title="Notification settings"
className="max-w-lg p-8"
>
...
</ModalDrawer>Frequently asked questions
- Is Slide-Over Drawer free for commercial use?
- Yes. Slide-Over Drawer is MIT licensed, so you can use it in personal and commercial projects without attribution.
- Is Slide-Over Drawer accessible for keyboard and screen reader users?
- Yes. It renders a native dialog element opened with showModal, so the browser provides the focus trap, top layer rendering and Escape handling, and the page behind it becomes inert. The visible title labels the drawer through aria-labelledby, the close button is a real button named Close drawer, background scroll is locked while the drawer is open, and the staggered content is ordinary DOM that screen readers can read at any point during the animation. When the drawer closes, focus returns to the button that opened it immediately, never waiting for the exit animation.
- How does the slide and stagger animation work, and what happens with reduced motion?
- Motion animates only transform and opacity: the backdrop fades in over 300 milliseconds while the full-height panel slides from the chosen edge to rest on the shared springSoft preset, settling in about half a second. Once the panel is in flight its direct children rise 12 pixels into place with a 50 millisecond stagger that starts 0.15 seconds in, and closing slides the panel out over 0.25 seconds while the backdrop fades. With prefers-reduced-motion enabled the slide and stagger are dropped entirely and both panel and content become plain opacity fades of 150 milliseconds or less.