Drawer

A panel that slides in from an edge and keeps the page behind it visible, for work that is a detour rather than a new destination.

Usage

When to use a drawer, where to put it, and how to close it.

When to use

A drawer slides in from an edge and keeps the page visible behind it. Use it for a short detour where the reader wants to keep their place:
  • A detail panel for a row in a list, opened from that row and closed back to it.
  • A form too big for a popover and too small for its own page: a new record, a set of filters, a settings pane.
  • Navigation on a narrow screen, where a side rail does not fit.
Use something else when:
  • Modal, the reader has to answer before anything else can happen, like a confirmation or a destructive action.
  • A page, the work should be linkable, reachable with the back button, or will keep the reader for minutes.
  • Popover, it is a few fields or a definition tied to one element.
open is always controlled. onClose fires for the close button, a click on the mask and Escape, so a check for unsaved work belongs there and covers all three.

Placement

placement takes left, right, top or bottom, and defaults to right. It also decides whether size is a width or a height, and which edge the resize handle sits on.
  • right reads as “more about this thing” and does not clash with a left-hand menu. Keep it unless you have a reason.
  • left reads as navigation. Use it for a menu on a narrow screen, not for a detail panel.
  • bottom is the phone sheet. Give it a height.
  • top competes with the page header and with notifications. Use it only for banner-like content.
In a right-to-left layout, left and right swap sides on their own. The close button's closable.placement takes start or end and follows the direction too.

Size

size is the width for left and right, and the height for top and bottom. It replaces the deprecated width and height.
  • "default" is 378px.
  • "large" is 736px, for long values and two-column forms.
  • A number is read as pixels.
  • Any other string is a CSS length, so "60%" and clamp() both work. A percentage suits screens whose size you do not know.
Set resizable to let the reader drag the inner edge. To keep their choice, store the value from onResize and pass it back as size.

The mask, and a drawer without one

mask is on by default and takes true, false or { enabled, blur, closable }. blur blurs the page behind the panel as well as dimming it. mask.closable replaces the deprecated maskClosable.
Turning the mask off with mask={false} changes more than the dimming:
  • Focus is no longer trapped, so Tab moves from the panel into the page behind it.
  • The page keeps scrolling under the reader's wheel.
  • Clicking away does not close it. The close button and Escape are the only ways out.
That suits a panel the reader keeps open while working on the page, like an inspector. Give it a border or a shadow so it does not merge with the page.
keyboard={false} stops Escape from closing the drawer. Together with mask={{ closable: false }} it suits a drawer holding unsaved work, but then only your own button can close it, so put one in the footer.
The header and footer stay in place while the body scrolls, so put the drawer's actions in footer, where the reader can always reach them.
  • Put the primary action last and Cancel before it, and let Cancel call the same handler as onClose.
  • extra sits opposite title in the header. Use it for the status of what is shown (a Tag, a small secondary action), not for a second Save.
  • title also names the drawer for screen readers. See the Accessibility tab.
  • With no title, no extra and closable={false}, there is no header at all. The body then needs its own heading, and the drawer loses its accessible name.
  • loading shows a skeleton in the body and keeps the header and footer. Start a fetch in afterOpenChange to wait until the slide-in has finished.
  • To check for unsaved changes, compare the form's values with the record you opened. form.isFieldsTouched() is already true after setFieldsValue fills in the form.

Nested drawers

Put a <Drawer> inside another, each with its own open. Nothing else is needed.
When the inner drawer opens, the outer one slides aside by push, 180px by default. Set push={false} on the outer drawer to keep it in place.
Escape closes only the top drawer, so a reader three levels deep presses it three times. Before adding a level, ask whether the inner panel should be a page.

Rendering in place

The drawer renders at the end of the page so it can cover the whole screen. getContainer={false} renders it inside its parent instead, and the mask covers only that parent. Give the parent position: relative, and usually overflow: hidden, or the drawer escapes it. You can also pass an element, or a function that returns one.
Use it when the drawer has to scroll or clip with one part of the page, like a panel inside a card. An in-place drawer does not trap focus or lock the page scroll, so do not use it for a task that needs the reader's full attention.

Scrolling inside a drawer

The body scrolls on its own and the header and footer stay put. You do not need a maxHeight on the body.
  • Keep one scroll area. A body and an inner pane that both scroll give the wheel two meanings.
  • Let content with its own overflow (a log, a code block, a wide table) scroll inside itself instead of widening the panel.
  • destroyOnHidden unmounts the body on close, which resets the scroll position and any form inside. Without it, the drawer reopens where the reader left it. Use it instead of the deprecated destroyOnClose.