Skip to content
Protocore Design Systemv1.6.9

/// Overlay

Tray

A bottom sheet: a panel that rises from the bottom edge for pickers, filters, and quick actions, with optional drag-to-dismiss.

import { Tray } from "@protocore/pds";
View as Markdown

Basics

Tray mirrors Sheet — same parts, Tray.Root / Trigger / Content / Title / Description / Footer / Close — but anchors to the bottom edge and spans the viewport width. size caps the height: sm 40vh, md 60vh (default), lg 85vh.

Drag to dismiss

dragToDismiss turns the grab handle into a drag target: pull the panel down past a third of its height to close it — the familiar mobile bottom-sheet gesture. The handle always renders (grabber) as an affordance; dragging is opt-in.

When to use it

A Tray is the bottom-edge counterpart to a Sheet. Where a Sheet slides in from the side for master-detail on wide screens, a Tray rises from the bottom — the natural home for touch-first pickers, filter panels, and quick action lists, especially on narrow viewports where a side drawer would feel cramped.

Choose a [Sheet](/overlay/sheet) for tall, scrollable detail views beside a list. Choose a [Dialog](/overlay/dialog) for a short, self-contained decision that reads better centered. Reach for a Tray when the content is a compact set of options or actions the user summons from the bottom.

Usage

Do

  • Use it for pickers, filters, and quick actions — especially on touch.
  • Pick a size that fits: sm for a short list, lg for a fuller panel.
  • Include a Tray.Title for accessibility, as with Dialog and Sheet.
  • Enable dragToDismiss for a mobile-native feel; the handle signals it.

Don't

  • Use a Tray for long master-detail content — that's a Sheet.
  • Stack multiple Trays; drive everything from one panel.
  • Omit the Title — Radix warns and screen readers need the label.
  • Make it taller than the content needs; a bottom sheet should feel light.

Accessibility

KeysAction
EscCloses the tray and returns focus to the trigger.
TabCycles focus forward, trapped within the panel.
Shift + TabCycles focus backward, trapped within the panel.
Enter / SpaceActivates the focused control.
  • Built on Radix Dialog: role=dialog with aria-modal, focus trapped and restored on close.
  • A Tray.Title is required to label the panel; Description describes it.
  • The drag handle is decorative (aria-hidden); dismissal is always keyboard-reachable via Esc or the close button.
  • Content outside the tray is hidden from assistive tech while it's open.

Tray.Content props

PropTypeDefaultDescription
asChildboolean
classNamestring
dragToDismissbooleanfalseLet the user drag the panel down past a threshold to dismiss it.
grabberbooleantrueShow the top grab handle. Enabled automatically when `dragToDismiss`.
showClosebooleantrueRender a top-right 32px ghost × close button.
sizeenummdPanel max-height. sm 40vh / md 60vh / lg 85vh.
styleCSSProperties

Related

  • SheetA full-height panel that slides in from the right for detail views and side forms that shouldn't cover the whole screen.
  • DialogA modal panel over a dimmed backdrop for focused tasks — forms, confirmations, and detail views that demand attention.
  • DrawerAlias of Sheet — a side-anchored overlay that slides in from an edge for inspectors, filters, and detail panes.
  • ModalsProviderAn imperative modal manager: open Dialogs and confirm prompts from anywhere with a hook call, no JSX in your view.