Skip to content
Interactive demoInteractive UIIntro

Modal dialogs

Focus trapping, escape handling, and the difference between dismissible and destructive dialogs.

Built withRadix DialogReact

Before you try it

What it needs

Nothing to configure. It runs the same everywhere.

Mode right now

ReadyThe provider is configured here; nobody has watched the real path succeed on this deployment yet.

A safe failure to try

Open the destructive confirm and press Escape: nothing is deleted, and focus returns to the button that opened it.

Uncontrolled dialog

Radix holds the open state internally. Right for the common case: a dialog that opens from a trigger and closes on Escape, backdrop click, or a close button.

Controlled destructive confirm

Irreversible actions get friction on purpose. Typing the resource name defeats muscle-memory clicking in a way a plain “Are you sure?” does not.

Try it with the keyboard

These behaviors are what the dependency buys us. Hand-rolling them correctly is genuinely hard, and getting them wrong locks some people out of the feature entirely.

  • TabCycles only within the dialog — focus cannot leak to the page behind.
  • EscapeCloses the dialog without touching the mouse.
  • On openFocus moves into the dialog automatically.
  • On closeFocus returns to the trigger button you came from.

What this demonstrates

  • Focus moves into the dialog and returns to the trigger on close
  • The page behind a modal is hidden from screen readers
  • Destructive actions require an explicit, differently-worded confirmation
  • Controlled vs uncontrolled open state, and when each is appropriate

Where the code lives

src/experiments/modals/demo.tsx

Registered in src/experiments/registry.ts and loaded on demand by loader.tsx.