Skip to content

Handling multiple dialogs

The Privy modal is an HTML <dialog> element that will appear in the foreground of your app when opened.

If your app makes use of dialog components (most commonly, for modals and pop-ups), you may encounter issues with the Privy dialog interfering with those from your app.

When using other non-Privy dialog elements within your app, we generally recommend:

  • Avoid UIs that involve a modal overlaying another modal. This can be a confusing and visually jarring experience for users, especially since users can only interact with a single modal at a time.
  • Use the Dialog component from headless-ui, as it has the best compatibility with UI components and HTML elements from third-party libraries like Privy.

Radix UI dialogs

If your app uses the Dialog component from Radix UI, we suggest making the following modifications to the default Dialog component:

  1. Prevent the default behavior of the Radix dialog closing when the user clicks outside of it, via the onPointerDownOutside prop of the Dialog.Content component.
  2. Prevent the default behavior of the Radix dialog always trapping the browser's focus (even if other dialogs are opened), by wrapping yourDialog.Content with the FocusScope component from the @radix-ui/react-focus-scope library. In this FocusScope component, you should set the prop trapped to false. See this GitHub discussion for more info!

Altogether, the modifications to a Dialog component might look as follows:

tsx

import * as Dialog from '@radix-ui/react-dialog';
import {FocusScope} from '@radix-ui/react-focus-scope';

<Dialog.Root>
  ...
  <Dialog.Portal>
    <Dialog.Overlay />
    {/* This wrapper prevents the Radix dialog from stealing focus away from other dialogs in the page. */}
    <FocusScope trapped={false}>
        {/* The `onPointerDownOutside` handler prevents Radix from closing the dialog when the user clicks outside. */}
      <Dialog.Content
        onPointerDownOutside={(e) => e.preventDefault()}
      />
        ...
      </Dialog.Content>
    </FocusScope>
  </Dialog.Portal>
<Dialog.Root>