Lattice components

Dialog

Modal surface primitive that owns open state, focus trapping, layered dismissal, and presence motion while you own the visuals.

@lattice-ui/dialog Stable direction import Dialog depends on core , focus , layer , motion

Dialog is the primitive for any surface that should take over the screen: confirmations, forms, settings panels, and store windows. It coordinates open state, focus, dismissal, and exit motion so your component only has to render the frame and its contents.

Reach for Dialog when a surface needs to be modal (block interaction behind it), restore focus to whatever opened it, and dismiss predictably — by an explicit close, an outside interaction, or a controlled state change.

Import

import { Dialog } from "@lattice-ui/dialog";

Anatomy

Compose the full set of parts. Trigger, Overlay, and Close are optional depending on how you drive the dialog, but Root, Portal, and Content form the minimum useful surface.

Dialog anatomy
<Dialog.Root>
<Dialog.Trigger />
<Dialog.Portal>
<Dialog.Overlay />
<Dialog.Content />
</Dialog.Portal>
</Dialog.Root>
PartRequiredResponsibility
Dialog.RootyesOwns open state and shares it with every part through context.
Dialog.TriggernoA button that opens the dialog and is the default focus-restore target.
Dialog.PortalyesRenders the surface into a ScreenGui outside the local tree.
Dialog.OverlaynoA full-screen backdrop behind the content.
Dialog.ContentyesThe focus-trapped, dismissable, motion-driven surface.
Dialog.ClosenoA button that closes the dialog from inside the content.

Example

A controlled dialog driven by a trigger, with an app-owned frame inside the content.

InvitePlayerDialog.tsx
import { useState } from "@rbxts/react";
import { Dialog } from "@lattice-ui/dialog";
export function InvitePlayerDialog() {
const [open, setOpen] = useState(false);
return (
<Dialog.Root open={open} onOpenChange={setOpen}>
<Dialog.Trigger asChild>
<textbutton Text="Invite player" Size={UDim2.fromOffset(140, 38)} />
</Dialog.Trigger>
<Dialog.Portal>
<Dialog.Overlay>
<frame
BackgroundColor3={Color3.fromRGB(0, 0, 0)}
BackgroundTransparency={0.5}
Size={UDim2.fromScale(1, 1)}
/>
</Dialog.Overlay>
<Dialog.Content trapFocus restoreFocus>
<frame
AnchorPoint={new Vector2(0.5, 0.5)}
BackgroundColor3={Color3.fromRGB(24, 26, 32)}
Position={UDim2.fromScale(0.5, 0.5)}
Size={UDim2.fromOffset(320, 180)}
>
<textlabel
BackgroundTransparency={1}
Size={UDim2.fromOffset(280, 40)}
Text="Invite a player"
TextColor3={Color3.fromRGB(240, 244, 250)}
/>
<Dialog.Close asChild>
<textbutton Text="Close" Size={UDim2.fromOffset(100, 34)} />
</Dialog.Close>
</frame>
</Dialog.Content>
</Dialog.Portal>
</Dialog.Root>
);
}

How it behaves

Open state

Dialog.Root is controllable. Pass open and onOpenChange to control it, or defaultOpen to run uncontrolled. Dialog.Trigger opens it on activation and Dialog.Close closes it; both go through the same state, so controlled and uncontrolled usage behave identically.

Focus and selection

Dialog.Content wraps its children in a focus scope. By default it traps focus (trapFocus defaults to true) so gamepad and selection movement stay inside the surface while it is open, and restores focus (restoreFocus defaults to true) to the element that was selected before it opened — usually the trigger. The trigger registers itself as the restore target, so wiring focus back is automatic.

Dismissal

Dialog.Content participates in dismissable-layer behavior. When modal is true (the default), interaction behind the surface is blocked and an outside press dismisses it. Use onPointerDownOutside and onInteractOutside to observe or veto those interactions before the dialog closes.

Motion and presence

Dialog.Content runs a default canvas-group reveal/exit recipe, so it animates in and out without extra setup. Override it with transition, and pass forceMount to keep the content mounted through its exit animation (useful when you drive motion yourself or need the node to persist).

API reference

Dialog.Root

Prop Type Description
open boolean Controlled open state. Pair with onOpenChange.
defaultOpen boolean Initial open state for uncontrolled usage. Defaults to false.
onOpenChange (open: boolean) => void Called whenever the open state changes.
modal boolean When true, blocks interaction behind the dialog and enables outside-press dismissal. Defaults to true.
children React.ReactNode The dialog parts.

Dialog.Trigger

Prop Type Description
asChild boolean Merge behavior onto the single child element instead of rendering the default textbutton.
disabled boolean Prevents the trigger from opening the dialog and removes it from selection.
children React.ReactElement The element to render. Required when asChild is set.

Dialog.Portal

Prop Type Description
container BasePlayerGui Target PlayerGui to render the surface into. Defaults to the player's PlayerGui.
displayOrderBase number Base DisplayOrder for the generated ScreenGui, used to order it against other layers.
children React.ReactNode Overlay and content parts.

Dialog.Overlay

Prop Type Description
asChild boolean Merge the overlay behavior onto the single child element.
forceMount boolean Keeps the overlay mounted while exit motion runs.
children React.ReactElement The backdrop element to render.

Dialog.Content

Prop Type Description
trapFocus boolean Traps focus and selection inside the content while open. Defaults to true.
restoreFocus boolean Restores focus to the previously focused element on close. Defaults to true.
forceMount boolean Keeps the content mounted while exit motion runs.
transition PresenceMotionConfig Overrides the default canvas-group reveal/exit recipe.
onPointerDownOutside (event: LayerInteractEvent) => void Called when a pointer press occurs outside the content, before dismissal.
onInteractOutside (event: LayerInteractEvent) => void Called for any other outside interaction, before dismissal.
children React.ReactNode The surface contents.

Dialog.Close

Prop Type Description
asChild boolean Merge close behavior onto the single child element instead of rendering the default textbutton.
children React.ReactElement The element to render. Required when asChild is set.