Dialog
A focus-managed modal window over the current page.
- Chromium
- Firefox
- WebKit
- WCAG 2.2 AA
Preview
import { Button } from "@moe-ui/registry/components/ui/button";
import {
Dialog,
DialogClose,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@moe-ui/registry/components/ui/dialog";
import { Text } from "@moe-ui/registry/components/ui/text";
import { View } from "react-native";
export default function DialogPreview() {
return (
<Dialog>
<DialogTrigger asChild>
<Button variant="outline">
<Text>Edit Profile</Text>
</Button>
</DialogTrigger>
<DialogContent className="sm:max-w-[425px]">
<DialogHeader>
<DialogTitle>Edit profile</DialogTitle>
<DialogDescription>
Make changes to your profile here. Click save when you’re done.
</DialogDescription>
</DialogHeader>
<View>
<Text>Profile editing form goes here...</Text>
</View>
<DialogFooter>
<DialogClose asChild>
<Button>
<Text>Save changes</Text>
</Button>
</DialogClose>
</DialogFooter>
</DialogContent>
</Dialog>
);
}
One command, your source
Installation
pnpm dlx @moe-ui/cli@beta add dialogUsage
The CLI installs editable source into your application. Import the component from your configured UI directory:
import { Dialog } from "@/components/ui/dialog";Variants and composition
Compound parts and variants remain regular source in your project, so you can change them without wrapping a package API.
Public API
The canonical source panel below lists every public export, dependency, and the complete implementation shipped by the registry.
Keyboard and accessibility
The trigger is keyboard reachable, Escape dismisses the surface, and focus returns to the trigger. Modal surfaces keep focus within the active layer.
Browser support
This beta is release-tested in Chromium, Firefox, and WebKit in light and dark themes. See the web compatibility matrix for the current gate.
Nothing hiddenCanonical source and public exports10 exports
DialogDialogCloseDialogContentDialogDescriptionDialogFooterDialogHeaderDialogOverlayDialogPortalDialogTitleDialogTrigger"use client";
import * as DialogPrimitive from "@rn-primitives/dialog";
import * as React from "react";
import { Platform, StyleSheet, View } from "react-native";
import Animated, { FadeIn, FadeOut } from "react-native-reanimated";
import { X } from "lucide-react-native";
import { cn } from "../../lib/utils";
const Dialog = DialogPrimitive.Root;
const DialogTrigger = DialogPrimitive.Trigger;
const DialogPortal = DialogPrimitive.Portal;
const DialogClose = DialogPrimitive.Close;
const DialogOverlay = React.forwardRef<
React.ElementRef<typeof DialogPrimitive.Overlay>,
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Overlay>
>(({ className, ...props }, ref) => {
return (
<DialogPrimitive.Overlay
style={StyleSheet.absoluteFill}
className={cn(
"z-50 bg-black/80 flex justify-center items-center p-2 web:data-[state=open]:animate-in web:data-[state=open]:fade-in-0 web:data-[state=closed]:animate-out web:data-[state=closed]:fade-out-0",
className,
)}
{...props}
ref={ref}
>
{Platform.OS === "web" ? (
(props.children as React.ReactNode)
) : (
<Animated.View
entering={FadeIn.duration(150)}
exiting={FadeOut.duration(150)}
>
{props.children as React.ReactNode}
</Animated.View>
)}
</DialogPrimitive.Overlay>
);
});
DialogOverlay.displayName = DialogPrimitive.Overlay.displayName;
const DialogContent = React.forwardRef<
React.ElementRef<typeof DialogPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content> & {
portalHost?: string;
}
>(({ className, children, portalHost, ...props }, ref) => {
return (
<DialogPortal hostName={portalHost}>
<DialogOverlay>
<DialogPrimitive.Content
ref={ref}
className={cn(
"z-50 grid w-full max-w-lg gap-4 border border-border bg-background p-6 shadow-lg sm:rounded-lg web:cursor-default web:data-[state=open]:animate-in web:data-[state=open]:fade-in-0 web:data-[state=open]:zoom-in-95 web:data-[state=closed]:animate-out web:data-[state=closed]:fade-out-0 web:data-[state=closed]:zoom-out-95",
className,
)}
{...props}
>
{children}
<DialogPrimitive.Close
className={
"absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground"
}
>
<X size={18} className="text-muted-foreground" />
<span className="sr-only">Close</span>
</DialogPrimitive.Close>
</DialogPrimitive.Content>
</DialogOverlay>
</DialogPortal>
);
});
DialogContent.displayName = DialogPrimitive.Content.displayName;
const DialogHeader = ({
className,
...props
}: React.ComponentPropsWithoutRef<typeof View>) => (
<View
className={cn(
"flex flex-col space-y-1.5 text-center sm:text-left",
className,
)}
{...props}
/>
);
DialogHeader.displayName = "DialogHeader";
const DialogFooter = ({
className,
...props
}: React.ComponentPropsWithoutRef<typeof View>) => (
<View
className={cn(
"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2",
className,
)}
{...props}
/>
);
DialogFooter.displayName = "DialogFooter";
const DialogTitle = React.forwardRef<
React.ElementRef<typeof DialogPrimitive.Title>,
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Title>
>(({ className, ...props }, ref) => (
<DialogPrimitive.Title
ref={ref}
className={cn(
"text-lg font-semibold leading-none tracking-tight text-foreground",
className,
)}
{...props}
/>
));
DialogTitle.displayName = DialogPrimitive.Title.displayName;
const DialogDescription = React.forwardRef<
React.ElementRef<typeof DialogPrimitive.Description>,
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Description>
>(({ className, ...props }, ref) => (
<DialogPrimitive.Description
ref={ref}
className={cn("text-sm text-muted-foreground", className)}
{...props}
/>
));
DialogDescription.displayName = DialogPrimitive.Description.displayName;
export {
Dialog,
DialogClose,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogOverlay,
DialogPortal,
DialogTitle,
DialogTrigger,
};