Drawer
- Stable
- WCAG 2.2 evidence
- RTL
A side panel over the page for details, filters or a secondary task. Built on React Aria
ModalOverlay + Modal + Dialog: a role="dialog" named by its title and described by its subtitle; focus moves
inside on open, Tab stays inside while the rest of the page is inert and does not scroll, Escape, the close button or a
click on the scrim closes it, and focus returns to the trigger (WCAG 2.1.1, 2.1.2, 2.4.3, 4.1.2). The body scrolls
between a fixed header and footer and becomes focusable when it overflows. On narrow screens the panel fills the width
(1.4.10). The slide-in only runs when motion is allowed (2.3.3). Consumer duties: title and action wording; use Dialog for blocking decisions.
import { Drawer } from "@nexera-ui/react";- 7
- examples
- 20
- props
- 6
- live controls
- 1
- platform
- 10
- WCAG criteria
- 6
- blocks use it
Try every prop. Copy the code.
Change the props and the code updates. Check light and dark, LTR and RTL, and phone width.
import { Drawer } from "@nexera-ui/react";
<Drawer />Examples 6
The same examples as Storybook, rendered live. Open Code to copy one.
Medium
Figma Size=md (480 px).
import { Button, Drawer } from "@nexera-ui/react";
function ProfileDetails() {
return (
<dl className="m-0 grid grid-cols-1 gap-3">
{[
["Department", "Finance"],
["Manager", "Sara Malik"],
["Joined", "4 March 2021"],
["Location", "Lahore office"],
].map(([term, value]) => (
<div key={term} className="flex flex-col gap-0.5">
<dt className="text-body-small text-muted">{term}</dt>
<dd className="m-0 text-body-default text-primary">{value}</dd>
</div>
))}
</dl>
);
}
<Drawer
title="Ayesha Khan"
subtitle="Senior accountant"
trigger={<Button variant="secondary">View profile</Button>}
secondaryAction={
<Button variant="secondary" size="md">
Edit
</Button>
}
primaryAction={
<Button size="md" slot="close">
Done
</Button>
}
defaultOpen
size="md"
>
{<ProfileDetails />}
</Drawer>;
Large
Figma Size=lg (640 px).
import { Button, Drawer } from "@nexera-ui/react";
function ProfileDetails() {
return (
<dl className="m-0 grid grid-cols-1 gap-3">
{[
["Department", "Finance"],
["Manager", "Sara Malik"],
["Joined", "4 March 2021"],
["Location", "Lahore office"],
].map(([term, value]) => (
<div key={term} className="flex flex-col gap-0.5">
<dt className="text-body-small text-muted">{term}</dt>
<dd className="m-0 text-body-default text-primary">{value}</dd>
</div>
))}
</dl>
);
}
<Drawer
title="Ayesha Khan"
subtitle="Senior accountant"
trigger={<Button variant="secondary">View profile</Button>}
secondaryAction={
<Button variant="secondary" size="md">
Edit
</Button>
}
primaryAction={
<Button size="md" slot="close">
Done
</Button>
}
defaultOpen
size="lg"
>
{<ProfileDetails />}
</Drawer>;
Start
Figma Position=Left: the start edge, which is the right edge in right-to-left layouts.
import { Button, Drawer } from "@nexera-ui/react";
function ProfileDetails() {
return (
<dl className="m-0 grid grid-cols-1 gap-3">
{[
["Department", "Finance"],
["Manager", "Sara Malik"],
["Joined", "4 March 2021"],
["Location", "Lahore office"],
].map(([term, value]) => (
<div key={term} className="flex flex-col gap-0.5">
<dt className="text-body-small text-muted">{term}</dt>
<dd className="m-0 text-body-default text-primary">{value}</dd>
</div>
))}
</dl>
);
}
<Drawer
title="Ayesha Khan"
subtitle="Senior accountant"
trigger={<Button variant="secondary">View profile</Button>}
secondaryAction={
<Button variant="secondary" size="md">
Edit
</Button>
}
primaryAction={
<Button size="md" slot="close">
Done
</Button>
}
defaultOpen
position="start"
>
{<ProfileDetails />}
</Drawer>;
With back
Figma Show back: a back button for drawers that step through nested views.
import { Button, Drawer } from "@nexera-ui/react";
function ProfileDetails() {
return (
<dl className="m-0 grid grid-cols-1 gap-3">
{[
["Department", "Finance"],
["Manager", "Sara Malik"],
["Joined", "4 March 2021"],
["Location", "Lahore office"],
].map(([term, value]) => (
<div key={term} className="flex flex-col gap-0.5">
<dt className="text-body-small text-muted">{term}</dt>
<dd className="m-0 text-body-default text-primary">{value}</dd>
</div>
))}
</dl>
);
}
<Drawer
title="Leave balance"
subtitle="Ayesha Khan"
trigger={<Button variant="secondary">View profile</Button>}
secondaryAction={
<Button variant="secondary" size="md">
Edit
</Button>
}
primaryAction={
<Button size="md" slot="close">
Done
</Button>
}
defaultOpen
onBack={() => undefined}
>
{<ProfileDetails />}
</Drawer>;
Long content
A body longer than the panel scrolls (and joins the tab order) between the fixed header and footer.
import { Button, Drawer } from "@nexera-ui/react";
<Drawer
title="Activity of the payroll and finance team during the last quarter of the year"
subtitle="Senior accountant"
trigger={<Button variant="secondary">View profile</Button>}
secondaryAction={
<Button variant="secondary" size="md">
Edit
</Button>
}
primaryAction={
<Button size="md" slot="close">
Done
</Button>
}
defaultOpen
>
{
<div className="flex flex-col gap-3">
{Array.from({ length: 24 }, (_, index) => (
<p key={index} className="m-0">
{index + 1}. Sara Malik approved a leave request for the last week of December and updated
the payroll calendar for the team.
</p>
))}
</div>
}
</Drawer>;
Right to left
Right-to-left: the default end drawer opens from the left edge and the close button sits on the left.
import { Button, Drawer } from "@nexera-ui/react";
function ProfileDetails() {
return (
<dl className="m-0 grid grid-cols-1 gap-3">
{[
["Department", "Finance"],
["Manager", "Sara Malik"],
["Joined", "4 March 2021"],
["Location", "Lahore office"],
].map(([term, value]) => (
<div key={term} className="flex flex-col gap-0.5">
<dt className="text-body-small text-muted">{term}</dt>
<dd className="m-0 text-body-default text-primary">{value}</dd>
</div>
))}
</dl>
);
}
<Drawer
title="عائشة خان"
subtitle="محاسبة أولى"
trigger={<Button variant="secondary">عرض الملف</Button>}
secondaryAction={
<Button variant="secondary" size="md">
تعديل
</Button>
}
primaryAction={
<Button size="md" slot="close">
تم
</Button>
}
defaultOpen
closeLabel="إغلاق"
backLabel="رجوع"
onBack={() => undefined}
>
{<ProfileDetails />}
</Drawer>;
Props 20
Press "Try it" on a card to load that prop into the playground.
20 props shown
title*NexeraReactNodeTitle: a level 2 heading that names the dialog. It wraps instead of truncating.
subtitleNexeraReactNodeSupporting line under the title. Also the dialog's description.
childrenNexeraReactNodeThe body: details, a form, a list. It fills the panel and scrolls.
primaryActionNexeraReactNodeMain action at the end of the footer: <Button size="md">. Give it slot="close" to close
the drawer when pressed. The footer is shown when an action is provided.
secondaryActionNexeraReactNodeAction before it: <Button variant="secondary" size="md">.
onBackNexera() => voidShows the back button at the start of the header and is called when it is pressed, for drawers that step through nested views.
backLabelNexerastringAccessible name of the back button. Translate it for your locale.
closeLabelNexerastringAccessible name of the close button. Translate it for your locale.
sizeNexera"sm" | "md" | "lg"Width: 400 / 480 / 640 px, never wider than the viewport.
positionNexera"start" | "end" | "left" | "right"Edge the panel opens from. start and end follow the reading direction; left and
right stay on that physical side in both directions.
isDismissableNexerabooleanWhether a click on the scrim closes the drawer. Escape and the close button always close it unless
isKeyboardDismissDisabled.
idNexerastringId of the panel element.
classNameNexerastringExtra classes for the panel, merged last so they win over the defaults.
styleNexeraCSSPropertiesInline style for the panel.
triggerNexeraReactElement<unknown, string | JSXElementConstructor<any>>The element that opens the overlay, usually a Nexera Button. It toggles the overlay when pressed and gets focus
back when the overlay closes. Leave it out to open the overlay from your own state (isOpen + onOpenChange).
isOpenNexerabooleanWhether the overlay is open (controlled). Use with onOpenChange.
defaultOpenNexerabooleanWhether the overlay is open on first render (uncontrolled).
onOpenChangeNexera(isOpen: boolean) => voidCalled when the overlay opens or closes (Escape, the close button, a slot="close" action, a click outside).
shouldCloseOnInteractOutsideReact Aria(element: Element) => booleanWhen user interacts with the argument element outside of the overlay ref, return true if onClose should be called. This gives you a chance to filter out interaction with elements that should not dismiss the overlay. By default, onClose will always be called on interaction outside the overlay ref.
isKeyboardDismissDisabledReact AriabooleanWhether pressing the escape key to close the modal should be disabled.
* Required. React Aria props shown are the ones most apps use; the component accepts the rest of its React Aria props too.
Accessible by default.
Built on React Aria, and covered by the WCAG 2.2 evidence generated on every build.
WCAG 2.2 evidence
5 direct · 5 supporting- 1.3.1Info and RelationshipsLevel A · tested directly
- 1.3.2Meaningful SequenceLevel A · supporting test
- 1.4.10ReflowLevel AA · supporting test
- 2.1.1KeyboardLevel A · tested directly
- 2.1.2No Keyboard TrapLevel A · tested directly
- 2.3.3Animation from InteractionsLevel AAA · supporting test
- 2.4.3Focus OrderLevel A · tested directly
- 2.4.7Focus VisibleLevel AA · supporting test
- 2.5.8Target Size (Minimum)Level AA · supporting test
- 4.1.2Name, Role, ValueLevel A · tested directly
Fits any width.
Nexera components respond to the space they are given. Drag the corner of the frame, or pick a width.
Styling hooks
Pass className to add Tailwind classes (merged last). State is exposed as data attributes, so you can style it with variants like data-pressed:.
data-enteringdata-exiting
<Drawer className="data-entering:opacity-90 shadow-sm" />Used in blocks
- Filters drawerLists, tables and records
- Record details drawerLists, tables and records
- Leave requestScheduling and calendar
- Leave approvalScheduling and calendar
- Team mapMaps and places
- Office finderMaps and places
Related components
- ActionSheetAn iOS-style sheet of actions that slides up from the bottom, with a separate Cancel row.
- ActionSheetItemOne action in an `ActionSheet`.
- BottomSheetA mobile sheet that slides up from the bottom edge for a short task or a set of options.
- CommandPaletteA searchable list of pages, actions and people in a modal.
- DialogA short modal question that needs a decision before work continues, such as "Submit October payroll?".
- ModalA modal window for a task that needs the user's full attention, such as a short form.
- PopoverA short, non-modal panel anchored to a trigger.
- TooltipA short description of a control, shown on hover and keyboard focus.