A high-quality, accessible dialog component for React applications, providing overlay modals, focus management, keyboard navigation, and screen reader support as part of the Radix UI primitives collection
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Portal rendering and overlay components for proper dialog layering, modal behavior, and visual presentation.
Portal component that renders dialog content outside the normal DOM flow for proper layering and z-index management.
/**
* Portal component that renders children outside normal DOM flow
* Ensures proper layering and avoids z-index conflicts
*/
type PortalProps = React.ComponentPropsWithoutRef<typeof PortalPrimitive>;
interface DialogPortalProps {
/** Content to be portaled */
children?: React.ReactNode;
/** Custom container element to portal into (defaults to document.body) */
container?: PortalProps['container'];
/** Force mounting when more control is needed for animations */
forceMount?: true;
}
const DialogPortal: React.FC<DialogPortalProps>;Usage Examples:
import {
Dialog,
DialogTrigger,
DialogPortal,
DialogContent
} from "@radix-ui/react-dialog";
// Basic portal usage
function BasicPortal() {
return (
<Dialog>
<DialogTrigger>Open</DialogTrigger>
<DialogPortal>
<DialogContent>
This content is rendered outside the normal DOM flow
</DialogContent>
</DialogPortal>
</Dialog>
);
}
// Custom portal container
function CustomContainer() {
const [container, setContainer] = React.useState<HTMLElement | null>(null);
return (
<div>
<div ref={setContainer} id="custom-portal-root" />
<Dialog>
<DialogTrigger>Open</DialogTrigger>
<DialogPortal container={container}>
<DialogContent>
This renders into the custom container
</DialogContent>
</DialogPortal>
</Dialog>
</div>
);
}
// Force mount for animations
function AnimatedPortal() {
const [open, setOpen] = React.useState(false);
return (
<Dialog open={open} onOpenChange={setOpen}>
<DialogTrigger>Open</DialogTrigger>
<DialogPortal forceMount>
<DialogContent className={open ? 'fade-in' : 'fade-out'}>
Always mounted for smooth animations
</DialogContent>
</DialogPortal>
</Dialog>
);
}Background overlay component that provides visual separation and modal behavior for dialogs.
/**
* Background overlay component for modal dialogs
* Provides visual backdrop and handles scroll locking
* Only renders for modal dialogs (modal={true})
*/
type DialogOverlayElement = React.ComponentRef<typeof Primitive.div>;
interface DialogOverlayProps extends React.ComponentPropsWithoutRef<typeof Primitive.div> {
/** Force mounting when more control is needed for animations */
forceMount?: true;
}
const DialogOverlay: React.ForwardRefExoticComponent<
DialogOverlayProps & React.RefAttributes<DialogOverlayElement>
>;Usage Examples:
import {
Dialog,
DialogTrigger,
DialogPortal,
DialogOverlay,
DialogContent
} from "@radix-ui/react-dialog";
// Basic overlay usage
function BasicOverlay() {
return (
<Dialog>
<DialogTrigger>Open Modal</DialogTrigger>
<DialogPortal>
<DialogOverlay className="dialog-overlay" />
<DialogContent>
Modal content with overlay background
</DialogContent>
</DialogPortal>
</Dialog>
);
}
// Styled overlay
function StyledOverlay() {
return (
<Dialog>
<DialogTrigger>Open</DialogTrigger>
<DialogPortal>
<DialogOverlay
className="overlay"
style={{
backgroundColor: 'rgba(0, 0, 0, 0.75)',
backdropFilter: 'blur(4px)'
}}
/>
<DialogContent>Content here</DialogContent>
</DialogPortal>
</Dialog>
);
}
// Overlay with animation
function AnimatedOverlay() {
return (
<Dialog>
<DialogTrigger>Open</DialogTrigger>
<DialogPortal>
<DialogOverlay
forceMount
className="overlay-with-transition"
data-state={open ? 'open' : 'closed'}
/>
<DialogContent>Content here</DialogContent>
</DialogPortal>
</Dialog>
);
}
// No overlay for non-modal dialog
function NonModalDialog() {
return (
<Dialog modal={false}>
<DialogTrigger>Open Non-Modal</DialogTrigger>
<DialogPortal>
{/* DialogOverlay will not render when modal={false} */}
<DialogOverlay />
<DialogContent>
Non-modal content without blocking overlay
</DialogContent>
</DialogPortal>
</Dialog>
);
}By default, DialogPortal renders content into document.body. This ensures:
You can specify a custom container for special use cases:
// Portal into a specific element
const portalRoot = document.getElementById('portal-root');
<DialogPortal container={portalRoot}>
<DialogContent>Custom container content</DialogContent>
</DialogPortal>Portal content maintains access to React context from its original location in the component tree, even though it renders elsewhere in the DOM.
When modal={true} (default), DialogOverlay provides:
The overlay renders as a div element with:
data-state attribute ("open" or "closed")pointer-events: auto style to enable click handling.dialog-overlay {
position: fixed;
inset: 0;
background-color: rgba(0, 0, 0, 0.5);
animation: fadeIn 150ms ease-out;
}
.dialog-overlay[data-state="closed"] {
animation: fadeOut 150ms ease-in;
}Use forceMount to control overlay mounting for animations:
// Overlay always mounted for smooth transitions
<DialogOverlay forceMount data-state={open ? 'open' : 'closed'} /><Dialog>
<DialogTrigger>Open</DialogTrigger>
<DialogPortal>
<DialogOverlay />
<DialogContent>
{/* Dialog content */}
</DialogContent>
</DialogPortal>
</Dialog><Dialog modal={false}>
<DialogTrigger>Open</DialogTrigger>
<DialogPortal>
{/* No overlay needed for non-modal */}
<DialogContent>
{/* Dialog content */}
</DialogContent>
</DialogPortal>
</Dialog><div className="app-container">
<div id="dialog-portal" className="dialog-container" />
<Dialog>
<DialogTrigger>Open</DialogTrigger>
<DialogPortal container={document.getElementById('dialog-portal')}>
<DialogOverlay />
<DialogContent>
{/* Content portaled to custom container */}
</DialogContent>
</DialogPortal>
</Dialog>
</div>Install with Tessl CLI
npx tessl i tessl/npm-radix-ui--react-dialog