Dialog
A focused interruption with trapped focus, an explicit title, and a return path.
- Family
- Transaction
- Available import
@/components/ui/dialog- Status
- Thorbis wrapper and live specimen ship here.
dialog24 Example Street. Edit a local preview; nothing is saved or sent.
This example needs JavaScript. If the button stays disabled, reload the page.
Preview: routine. Note: none
- Use
- Use for consequential confirmation or a short task that must be completed before returning.
- Avoid
- Do not use for routine navigation, passive detail, or a workflow that needs its own URL.
Use this component
GitHub source links require access to the private Thorbis design repository.
Wrapper sourceRepository and dependencies
- Value exports
DialogDialogCloseDialogContentDialogDescriptionDialogFooterDialogHeaderDialogOverlayDialogPortalDialogTitleDialogTrigger
- Type exports
- None.
Setup, styles, and dependencies
These are React wrappers in the Thorbis design repository, not an installable npm UI package. In this repository, @/ resolves to src/ through tsconfig.json.
Use the repository's package.json and package-lock.json for dependencies. Load src/styles.css once at the application root for global tokens, typography, and themes. The wrappers compose colocated StyleX owners; include the generated token CSS and extracted StyleX output.
For another project, bring the wrapper and its transitive local imports, configure the alias, install the dependencies those files use, and provide the same token and style setup. Review the source before copying; the direct imports below are not a complete dependency closure.
Interactive examples require client-side React. This TanStack Start site server-renders and hydrates React; another host must hydrate or render the component on the client. Example code is the same TypeScript source used by its live preview.
Direct imports in this wrapper:
./dialog.stylex./overlay-activation@radix-ui/react-dialog@stylexjs/stylexreact
Dialog draft, apply and dismissal
Edit a sample job's note and priority, then apply to its local preview or dismiss to discard the draft. Reopening starts from the current preview. Nothing is saved or sent. The trigger stays disabled with guidance until client hydration. Focus is contained while open and returns to the trigger after applying, cancelling, Escape or an outside click. DialogContent includes its portal and overlay, and keeps bounded content within the available viewport and safe-area insets.
24 Example Street. Edit a local preview; nothing is saved or sent.
This example needs JavaScript. If the button stays disabled, reload the page.
Preview: routine. Note: none
Read the complete example
import { useEffect, useId, useRef, useState, type FormEvent } from "react";
import { Button } from "@/components/ui/button";
import {
Dialog,
DialogClose,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog";
import {
FormField,
FormHelp,
FormLabel,
FormSection,
FormSectionDescription,
FormSectionFields,
FormSectionInfo,
} from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import { NativeSelect } from "@/components/ui/native-select";
export function JobDialogExample() {
const id = useId();
const title = useRef<HTMLHeadingElement>(null);
const [ready, setReady] = useState(false);
const [open, setOpen] = useState(false);
const [current, setCurrent] = useState({ note: "", priority: "routine" });
const [draft, setDraft] = useState(current);
useEffect(() => setReady(true), []);
function changeOpen(next: boolean) {
if (next) setDraft(current);
setOpen(next);
}
function apply(event: FormEvent<HTMLFormElement>) {
event.preventDefault();
setCurrent({ ...draft, note: draft.note.trim() });
setOpen(false);
}
return (
<Dialog open={open} onOpenChange={changeOpen}>
<FormSection data-example="job-dialog" aria-busy={!ready}>
<FormSectionInfo>
<FormSectionDescription>
24 Example Street. Edit a local preview; nothing is saved or sent.
</FormSectionDescription>
{!ready && (
<FormHelp>
This example needs JavaScript. If the button stays disabled, reload the page.
</FormHelp>
)}
</FormSectionInfo>
<FormSectionFields>
<DialogTrigger asChild>
<Button variant="secondary" disabled={!ready}>
Edit sample job
</Button>
</DialogTrigger>
<FormHelp role="status">
Preview: {current.priority}. Note: {current.note || "none"}
</FormHelp>
</FormSectionFields>
</FormSection>
<DialogContent
size="sm"
onOpenAutoFocus={(event) => {
const content = event.target as HTMLElement;
if (content.scrollHeight > content.clientHeight) {
event.preventDefault();
title.current?.focus({ preventScroll: true });
}
}}
>
<form onSubmit={apply}>
<FormSection>
<DialogHeader>
<DialogTitle ref={title} tabIndex={-1}>
Edit sample job
</DialogTitle>
<DialogDescription>
24 Example Street. Apply updates to this page's preview, or cancel to discard your
changes.
</DialogDescription>
</DialogHeader>
<FormSectionFields>
<FormField>
<FormLabel htmlFor={`${id}-note`}>Job note (optional)</FormLabel>
<Input
id={`${id}-note`}
name="note"
value={draft.note}
autoComplete="off"
onChange={(event) => setDraft({ ...draft, note: event.target.value })}
/>
</FormField>
<FormField>
<FormLabel htmlFor={`${id}-priority`}>Priority</FormLabel>
<NativeSelect
id={`${id}-priority`}
name="priority"
value={draft.priority}
onChange={(event) => setDraft({ ...draft, priority: event.target.value })}
>
<option value="routine">Routine</option>
<option value="urgent">Urgent</option>
</NativeSelect>
</FormField>
</FormSectionFields>
<DialogFooter>
<DialogClose asChild>
<Button type="button" variant="secondary">
Cancel changes
</Button>
</DialogClose>
<Button type="submit">Apply preview</Button>
</DialogFooter>
</FormSection>
</form>
</DialogContent>
</Dialog>
);
}
Adoption contract
- Start from the ShadCN accessible source primitive; keep upstream keyboard and focus behavior.
- Use Thorbis color, type, space, radius, shadow, and motion tokens only.
- Design hover, focus, selected, disabled, loading, empty, error, offline, and reduced-motion states where they apply.
- Verify desktop and 390px mobile, keyboard order, accessible names, and stable async geometry.