A modal window that appears above the page to focus people on a task or message.
Dialog is an open-source component from suiss/ui for React, React Native, SwiftUI and Jetpack Compose. The @esuiss/ui CLI copies its source into your project, so you own the code.
import { Button } from "@/components/ui/button"import { Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger,} from "@/components/ui/dialog"import { Input } from "@/components/ui/input"import { Label } from "@/components/ui/label"export default function DialogDemo() { return ( <Dialog> <DialogTrigger render={<Button variant="outline" />}> Edit profile </DialogTrigger> <DialogContent> <DialogHeader> <DialogTitle>Edit profile</DialogTitle> <DialogDescription> Update your name and username. Click save when you are done. </DialogDescription> </DialogHeader> <div className="grid gap-4"> <div className="grid gap-2"> <Label htmlFor="name">Name</Label> <Input id="name" defaultValue="Ada Lovelace" /> </div> <div className="grid gap-2"> <Label htmlFor="username">Username</Label> <Input id="username" defaultValue="@ada" /> </div> </div> <DialogFooter> <DialogClose render={<Button variant="secondary" />}> Cancel </DialogClose> <Button>Save changes</Button> </DialogFooter> </DialogContent> </Dialog> )}
Dialog wraps the dialog primitive of the chosen base. The Dialog root manages the open state, and DialogTrigger opens it. DialogContent renders the window in a portal over a dimmed DialogOverlay, with a close button in the top corner by default. Inside it, DialogHeader holds the DialogTitle and DialogDescription, and DialogFooter holds the actions. DialogClose closes the dialog from any element.
Use a dialog for a short, focused task such as editing a few fields. To confirm a destructive or important action that needs an explicit response, consider an alert dialog. For larger content that slides in from an edge, use a sheet or drawer.
Installation
CLI
$ pnpm dlx shadcn@latest add @esuiss/dialog
Works in any project with a components.json. See Installation for the one-line registry setup. Or use the suiss/ui CLI:
The dialog is uncontrolled by default; set defaultOpen to start open, or control it with open and onOpenChange. Render the trigger as another component, such as a Button, with the render prop. Use DialogClose with render for a Cancel button.
The dialog is uncontrolled by default; set defaultOpen to start open, or control it with open and onOpenChange. Set asChild on DialogTrigger to render your own Button as the trigger, and on DialogClose for a Cancel button.
The dialog is uncontrolled by default; set defaultOpen to start open, or control it with isOpen and onOpenChange. The first pressable child of Dialog, such as a Button, opens it. Give a Buttonslot="close" for a Cancel button.
Set showCloseButton={false} on DialogContent to remove the corner close button, and set closeLabel to replace its icon with a text label. Set showCloseButton on DialogFooter to add a secondary Close button to the actions. The footer stacks its actions in reverse order on small screens and aligns them to the end on wider screens.
Examples
Without close button
A dialog with no corner close button that adds a Close action to the footer instead.
Destructive
A dialog that confirms deleting a project, with a destructive action and a Cancel button.
data-open and data-closed drive the fade and zoom animation.
The window is sm:max-w-md by default; override it with className on DialogContent.
When a close button is shown, the first child of the content gets extra end padding so it does not run under the button.
Uses the popover, secondary and muted-foreground colors, with a bg-black/50 backdrop.
Dialog opens a centered card over a dimmed scrim. DialogContent renders through a portal (inside a full-window overlay on iOS), scales and fades in, and shows a round close button in the top-right corner by default. Tapping the scrim closes the dialog.
Use it for short forms and information that the user can dismiss freely. When the user must make an explicit choice, use Alert Dialog instead.
Installation
$ pnpm dlx shadcn@latest add @esuiss-rn/dialog
See Installation for the components.json setup. Or use the suiss/ui CLI:
$ pnpm dlx @esuiss/ui@latest add dialog
$ pnpm dlx @esuiss/ui@latest add dialog --platform swift
Add the pieces it builds on: Button, Icon, Text, and the primitives components/primitives/dialog.tsx, components/primitives/use-render.tsx, and the animated view components/ui/animated-view.tsx from the next step.
Copy the animated view into components/ui/animated-view.tsx:
import { cn } from "cn"import { XIcon } from "phosphor-react-native"import * as React from "react"import { Platform, View, type ViewProps } from "react-native"import { FadeIn, FadeOut, Keyframe, ReduceMotion,} from "react-native-reanimated"import { FullWindowOverlay as RNFullWindowOverlay } from "react-native-screens"import * as DialogPrimitive from "@/components/primitives/dialog"import { resolveClassName } from "@/components/primitives/use-render"import { AnimatedView } from "@/components/ui/animated-view"import { Button } from "@/components/ui/button"import { Icon } from "@/components/ui/icon"
Add the pieces it builds on: Button, Icon, Text, and the primitives components/primitives/dialog.tsx, components/primitives/use-render.tsx, and the animated view components/ui/animated-view.tsx from the next step.
Copy the animated view into components/ui/animated-view.tsx:
Add the pieces it builds on: Button, Icon, Text, and the primitives components/primitives/dialog.tsx, components/primitives/use-render.tsx, and the animated view components/ui/animated-view.tsx from the next step.
Copy the animated view into components/ui/animated-view.tsx:
import SwiftUIimport UIKitstruct RGBA: Sendable { let red: CGFloat let green: CGFloat let blue: CGFloat let alpha: CGFloat init(_ hex: UInt32, _ alpha: CGFloat = 1) { red = CGFloat((hex >> 16) & 0xFF) / 255 green = CGFloat((hex >> 8) & 0xFF) / 255 blue = CGFloat(hex & 0xFF) / 255 self.alpha = alpha }
swift/Theme/Tokens.swift
import SwiftUIpublic enum EsuissColor { public static let background = Color.adaptive(light: .init(0xFFFFFF), dark: .init(0x000000)) public static let foreground = Color.adaptive(light: .init(0x1D1D1F), dark: .init(0xF5F5F7)) public static let card = Color.adaptive(light: .init(0xFFFFFF), dark: .init(0x1D1D1F)) public static let cardForeground = Color.adaptive(light: .init(0x1D1D1F), dark: .init(0xF5F5F7)) public static let popover = Color.adaptive(light: .init(0xFFFFFF), dark: .init(0x1D1D1F)) public static let popoverForeground = Color.adaptive(light: .init(0x1D1D1F), dark: .init(0xF5F5F7)) public static let primary = Color.adaptive(light: .init(0x0071E3), dark: .init(0x2997FF)) public static let primaryForeground = Color.adaptive(light: .init(0xFFFFFF), dark: .init(0xFFFFFF)) public static let secondary = Color.adaptive(light: .init(0x000000, 0.04), dark: .init(0xFFFFFF, 0.1)) public static let secondaryForeground = Color.adaptive(light: .init(0x1D1D1F), dark: .init(0xF5F5F7)) public static let muted = Color.adaptive(light: .init(0xF5F5F7), dark: .init(0x161617)) public static let mutedForeground = Color.adaptive(light: .init(0x6E6E73), dark: .init(0xA1A1A6)) public static let accent = Color.adaptive(light: .init(0x000000, 0.06), dark: .init(0xFFFFFF, 0.1))
package com.esuiss.ui.themeimport androidx.compose.runtime.Immutableimport androidx.compose.ui.graphics.Colorimport androidx.compose.ui.unit.dp@Immutabledata class Colors( val background: Color, val foreground: Color, val card: Color, val cardForeground: Color, val popover: Color, val popoverForeground: Color, val primary: Color, val primaryForeground: Color,
Compose DialogTrigger and DialogContent with a DialogHeader (DialogTitle, DialogDescription), your content, and a DialogFooter. Footer buttons stack vertically with the first child at the bottom, and sit in a row aligned to the end on wider screens. Use DialogClose with render to turn any Button into a close button.
Set showCloseButton={false} on the content to hide the corner button, or pass closeLabel to show a text button such as "Done" instead of the icon. The header adds end padding so the title does not run under it. DialogFooter takes showCloseButton to append a secondary Close button.
Use open and onOpenChange for controlled state, or defaultOpen for uncontrolled.
Examples
Default
Web
Web preview
import { View } from "react-native"import { Button } from "@/components/ui/button"import { Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger } from "@/components/ui/dialog"import { Input } from "@/components/ui/input"import { Label } from "@/components/ui/label"import { Text } from "@/components/ui/text"function EditProfileContent() { return ( <DialogContent> <DialogHeader> <DialogTitle>Edit profile</DialogTitle> <DialogDescription> Make changes to your profile here. Tap save when you're done. </DialogDescription> </DialogHeader> <View className="gap-4"> <View className="gap-2"> <Label nativeID="dialog-name">Name</Label> <Input accessibilityLabelledBy="dialog-name" defaultValue="Pedro Duarte" /> </View> <View className="gap-2"> <Label nativeID="dialog-username">Username</Label> <Input accessibilityLabelledBy="dialog-username" defaultValue="@peduarte" autoCapitalize="none" /> </View> </View> <DialogFooter> <DialogClose render={<Button variant="secondary" />}> <Text>Cancel</Text> </DialogClose> <Button> <Text>Save changes</Text> </Button> </DialogFooter> </DialogContent> )}export function DialogExample() { return ( <View className="items-start"> <Dialog> <DialogTrigger render={<Button variant="outline" />}> <Text>Edit profile</Text> </DialogTrigger> <EditProfileContent /> </Dialog> </View> )}
import EsuissUIimport SwiftUIstruct DialogEditProfile: View { @State private var open: Bool init(open: Bool = false) { _open = State(initialValue: open) } var body: some View { Button("Edit profile") { open = true } .buttonStyle(.variant(.outline)) .dialog(isPresented: $open) { DialogEditProfileContent() } }}struct DialogEditProfileContent: View { @State private var name = "Pedro Duarte" @State private var username = "@peduarte" var body: some View { DialogContent { DialogHeader { DialogTitle("Edit profile") DialogDescription("Make changes to your profile here. Tap save when you're done.") } VStack(alignment: .leading, spacing: 16) { VStack(alignment: .leading, spacing: 8) { FieldLabel("Name") .accessibilityHidden(true) TextField("Name", text: $name) .textFieldStyle(.input) } VStack(alignment: .leading, spacing: 8) { FieldLabel("Username") .accessibilityHidden(true) TextField("Username", text: $username) .textFieldStyle(.input) .textInputAutocapitalization(.never) } } DialogFooter { DialogClose { Text("Cancel").frame(maxWidth: .infinity) } .buttonStyle(.variant(.secondary)) Button {} label: { Text("Save changes").frame(maxWidth: .infinity) } .buttonStyle(.variant()) } } }}
import androidx.compose.foundation.layout.Arrangementimport androidx.compose.foundation.layout.Columnimport androidx.compose.foundation.layout.fillMaxWidthimport androidx.compose.foundation.text.KeyboardOptionsimport androidx.compose.runtime.Composableimport androidx.compose.runtime.getValueimport androidx.compose.runtime.mutableStateOfimport androidx.compose.runtime.saveable.rememberSaveableimport androidx.compose.runtime.setValueimport androidx.compose.ui.Modifierimport androidx.compose.ui.semantics.contentDescriptionimport androidx.compose.ui.semantics.semanticsimport androidx.compose.ui.text.input.KeyboardCapitalizationimport androidx.compose.ui.unit.dpimport com.esuiss.ui.components.Buttonimport com.esuiss.ui.components.ButtonVariantimport com.esuiss.ui.components.Dialogimport com.esuiss.ui.components.DialogDescriptionimport com.esuiss.ui.components.DialogFooterimport com.esuiss.ui.components.DialogHeaderimport com.esuiss.ui.components.DialogTitleimport com.esuiss.ui.components.Inputimport com.esuiss.ui.components.Label@Composablefun DialogExample() { DialogDefault(defaultOpen = false)}@Composablefun DialogDefault(defaultOpen: Boolean) { var open by rememberSaveable { mutableStateOf(defaultOpen) } var name by rememberSaveable { mutableStateOf("Pedro Duarte") } var username by rememberSaveable { mutableStateOf("@peduarte") } Column { Button("Edit profile", onClick = { open = true }, variant = ButtonVariant.Outline) Dialog(open = open, onOpenChange = { open = it }) { DialogHeader { DialogTitle("Edit profile") DialogDescription("Make changes to your profile here. Tap save when you're done.") } Column(verticalArrangement = Arrangement.spacedBy(16.dp)) { Column(verticalArrangement = Arrangement.spacedBy(8.dp)) { Label("Name") Input(name, { name = it }, Modifier.semantics { contentDescription = "Name" }) } Column(verticalArrangement = Arrangement.spacedBy(8.dp)) { Label("Username") Input( username, { username = it }, Modifier.semantics { contentDescription = "Username" }, keyboardOptions = KeyboardOptions(capitalization = KeyboardCapitalization.None), ) } } DialogFooter { Button("Cancel", onClick = { open = false }, modifier = Modifier.fillMaxWidth(), variant = ButtonVariant.Secondary) Button("Save changes", onClick = {}, modifier = Modifier.fillMaxWidth()) } } }}
Destructive
A destructive action with a cancel button in the footer.
Web
Web preview
import { View } from "react-native"import { Button } from "@/components/ui/button"import { Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger } from "@/components/ui/dialog"import { Text } from "@/components/ui/text"export function DialogDestructiveExample() { return ( <View className="items-start"> <Dialog> <DialogTrigger render={<Button variant="destructive" />}> <Text>Delete account</Text> </DialogTrigger> <DialogContent> <DialogHeader> <DialogTitle>Delete account</DialogTitle> <DialogDescription> This will permanently delete your account and all of its data. </DialogDescription> </DialogHeader> <DialogFooter> <DialogClose render={<Button variant="secondary" />}> <Text>Cancel</Text> </DialogClose> <DialogClose render={<Button variant="destructive" />}> <Text>Delete</Text> </DialogClose> </DialogFooter> </DialogContent> </Dialog> </View> )}
import EsuissUIimport SwiftUIstruct DialogDestructive: View { @State private var open = false var body: some View { Button("Delete account") { open = true } .buttonStyle(.variant(.destructive)) .dialog(isPresented: $open) { DialogContent { DialogHeader { DialogTitle("Delete account") DialogDescription("This will permanently delete your account and all of its data.") } DialogFooter { DialogClose { Text("Cancel").frame(maxWidth: .infinity) } .buttonStyle(.variant(.secondary)) DialogClose { Text("Delete").frame(maxWidth: .infinity) } .buttonStyle(.variant(.destructive)) } } } }}
import androidx.compose.foundation.layout.Columnimport androidx.compose.foundation.layout.fillMaxWidthimport androidx.compose.runtime.Composableimport androidx.compose.runtime.getValueimport androidx.compose.runtime.mutableStateOfimport androidx.compose.runtime.saveable.rememberSaveableimport androidx.compose.runtime.setValueimport androidx.compose.ui.Modifierimport com.esuiss.ui.components.Buttonimport com.esuiss.ui.components.ButtonVariantimport com.esuiss.ui.components.Dialogimport com.esuiss.ui.components.DialogDescriptionimport com.esuiss.ui.components.DialogFooterimport com.esuiss.ui.components.DialogHeaderimport com.esuiss.ui.components.DialogTitle@Composablefun DialogDestructive() { var open by rememberSaveable { mutableStateOf(false) } Column { Button("Delete account", onClick = { open = true }, variant = ButtonVariant.Destructive) Dialog(open = open, onOpenChange = { open = it }) { DialogHeader { DialogTitle("Delete account") DialogDescription("This will permanently delete your account and all of its data.") } DialogFooter { Button("Cancel", onClick = { open = false }, modifier = Modifier.fillMaxWidth(), variant = ButtonVariant.Secondary) Button("Delete", onClick = { open = false }, modifier = Modifier.fillMaxWidth(), variant = ButtonVariant.Destructive) } } }}
Without close button
No corner button, with a Close button in the footer.
Web
Web preview
import { View } from "react-native"import { Button } from "@/components/ui/button"import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger } from "@/components/ui/dialog"import { Text } from "@/components/ui/text"export function DialogWithoutCloseButtonExample() { return ( <View className="items-start"> <Dialog> <DialogTrigger render={<Button variant="outline" />}> <Text>Show terms</Text> </DialogTrigger> <DialogContent showCloseButton={false}> <DialogHeader> <DialogTitle>Terms updated</DialogTitle> <DialogDescription> We have updated our terms of service. Please review the changes before continuing. </DialogDescription> </DialogHeader> <DialogFooter showCloseButton> <Button> <Text>Accept</Text> </Button> </DialogFooter> </DialogContent> </Dialog> </View> )}
import EsuissUIimport SwiftUIstruct DialogWithoutCloseButton: View { @State private var open = false var body: some View { Button("Show terms") { open = true } .buttonStyle(.variant(.outline)) .dialog(isPresented: $open) { DialogContent(showCloseButton: false) { DialogHeader { DialogTitle("Terms updated") DialogDescription("We have updated our terms of service. Please review the changes before continuing.") } DialogFooter(showCloseButton: true) { Button {} label: { Text("Accept").frame(maxWidth: .infinity) } .buttonStyle(.variant()) } } } }}
import androidx.compose.foundation.layout.Columnimport androidx.compose.foundation.layout.fillMaxWidthimport androidx.compose.runtime.Composableimport androidx.compose.runtime.getValueimport androidx.compose.runtime.mutableStateOfimport androidx.compose.runtime.saveable.rememberSaveableimport androidx.compose.runtime.setValueimport androidx.compose.ui.Modifierimport com.esuiss.ui.components.Buttonimport com.esuiss.ui.components.ButtonVariantimport com.esuiss.ui.components.Dialogimport com.esuiss.ui.components.DialogDescriptionimport com.esuiss.ui.components.DialogFooterimport com.esuiss.ui.components.DialogHeaderimport com.esuiss.ui.components.DialogTitle@Composablefun DialogWithoutCloseButton() { var open by rememberSaveable { mutableStateOf(false) } Column { Button("Show terms", onClick = { open = true }, variant = ButtonVariant.Outline) Dialog(open = open, onOpenChange = { open = it }, showCloseButton = false) { DialogHeader { DialogTitle("Terms updated") DialogDescription("We have updated our terms of service. Please review the changes before continuing.") } DialogFooter(showCloseButton = true) { Button("Accept", onClick = {}, modifier = Modifier.fillMaxWidth()) } } }}
With close label
A text close button through closeLabel.
Web
Web preview
import { View } from "react-native"import { Button } from "@/components/ui/button"import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, DialogTrigger } from "@/components/ui/dialog"import { Text } from "@/components/ui/text"export function DialogWithCloseLabelExample() { return ( <View className="items-start"> <Dialog> <DialogTrigger render={<Button variant="outline" />}> <Text>Open settings</Text> </DialogTrigger> <DialogContent closeLabel="Done"> <DialogHeader> <DialogTitle>Notification settings</DialogTitle> <DialogDescription> Choose how you want to be notified about activity in your workspace. </DialogDescription> </DialogHeader> </DialogContent> </Dialog> </View> )}
import EsuissUIimport SwiftUIstruct DialogWithCloseLabel: View { @State private var open = false var body: some View { Button("Open settings") { open = true } .buttonStyle(.variant(.outline)) .dialog(isPresented: $open) { DialogContent(closeLabel: "Done") { DialogHeader { DialogTitle("Notification settings") DialogDescription("Choose how you want to be notified about activity in your workspace.") } } } }}
import androidx.compose.foundation.layout.Columnimport androidx.compose.runtime.Composableimport androidx.compose.runtime.getValueimport androidx.compose.runtime.mutableStateOfimport androidx.compose.runtime.saveable.rememberSaveableimport androidx.compose.runtime.setValueimport com.esuiss.ui.components.Buttonimport com.esuiss.ui.components.ButtonVariantimport com.esuiss.ui.components.Dialogimport com.esuiss.ui.components.DialogDescriptionimport com.esuiss.ui.components.DialogHeaderimport com.esuiss.ui.components.DialogTitle@Composablefun DialogWithCloseLabel() { var open by rememberSaveable { mutableStateOf(false) } Column { Button("Open settings", onClick = { open = true }, variant = ButtonVariant.Outline) Dialog(open = open, onOpenChange = { open = it }, closeLabel = "Done") { DialogHeader { DialogTitle("Notification settings") DialogDescription("Choose how you want to be notified about activity in your workspace.") } } }}
API Reference
01
Dialog
The root that manages the open state.
Prop
Type
Default
Description
open
boolean
-
Whether the dialog is open, for controlled use.
defaultOpen
boolean
false
Whether the dialog starts open when uncontrolled.
onOpenChange
(open: boolean, details) => void
-
Called when the dialog opens or closes.
modal
boolean
true
Blocks interaction with the rest of the screen while open.
disablePointerDismissal
boolean
false
Keeps the dialog open when the overlay is tapped.
02
DialogOverlay
Dimmed scrim that closes the dialog when tapped.
Also accepts the props of React.ComponentProps<typeof DialogPrimitive.Backdrop>.
03
DialogContent
Centered card with an optional close button.
Prop
Type
Default
Description
portalHost
string
-
Name of the portal host to render into.
showCloseButton
boolean
true
Shows the corner close button. Defaults to true.
closeLabel
string
-
Text for the close button instead of the icon.
Also accepts the props of React.ComponentProps<typeof DialogPrimitive.Popup>.
04
DialogHeader
Column for the title and description.
Also accepts the props of ViewProps.
05
DialogFooter
Area for actions.
Prop
Type
Default
Description
showCloseButton
boolean
false
Appends a secondary Close button.
Also accepts the props of ViewProps.
06
DialogTitle
Dialog heading.
Also accepts the props of React.ComponentProps<typeof DialogPrimitive.Title>.
07
DialogDescription
Supporting text.
Also accepts the props of React.ComponentProps<typeof DialogPrimitive.Description>.
Accessibility
The popup has the dialog role, is labelled by the title and is marked as modal for VoiceOver.
Focus moves to the title when the dialog opens.
The Android back button and the VoiceOver escape gesture close it.
The icon close button is labelled "Close".
Notes
On iOS the content is wrapped in a full-window overlay so it appears above native navigation.
Animations respect the system Reduce Motion setting.
Uses bg-popover, bg-black/50 and bg-secondary tokens; light and dark follow the system appearance.
Web
Web preview
import { View } from "react-native"import { Button } from "@/components/ui/button"import { Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger } from "@/components/ui/dialog"import { Input } from "@/components/ui/input"import { Label } from "@/components/ui/label"import { Text } from "@/components/ui/text"function EditProfileContent() { return ( <DialogContent> <DialogHeader> <DialogTitle>Edit profile</DialogTitle> <DialogDescription> Make changes to your profile here. Tap save when you're done. </DialogDescription> </DialogHeader> <View className="gap-4"> <View className="gap-2"> <Label nativeID="dialog-name">Name</Label> <Input accessibilityLabelledBy="dialog-name" defaultValue="Pedro Duarte" /> </View> <View className="gap-2"> <Label nativeID="dialog-username">Username</Label> <Input accessibilityLabelledBy="dialog-username" defaultValue="@peduarte" autoCapitalize="none" /> </View> </View> <DialogFooter> <DialogClose render={<Button variant="secondary" />}> <Text>Cancel</Text> </DialogClose> <Button> <Text>Save changes</Text> </Button> </DialogFooter> </DialogContent> )}export function DialogOpenByDefaultExample() { return ( <View className="items-start"> <Dialog defaultOpen> <DialogTrigger render={<Button variant="outline" />}> <Text>Edit profile</Text> </DialogTrigger> <EditProfileContent /> </Dialog> </View> )}
import EsuissUIimport SwiftUIstruct DialogOpenByDefaultExample: View { var body: some View { DialogEditProfile(open: true) }}struct DialogEditProfile: View { @State private var open: Bool init(open: Bool = false) { _open = State(initialValue: open) } var body: some View { Button("Edit profile") { open = true } .buttonStyle(.variant(.outline)) .dialog(isPresented: $open) { DialogEditProfileContent() } }}struct DialogEditProfileContent: View { @State private var name = "Pedro Duarte" @State private var username = "@peduarte" var body: some View { DialogContent { DialogHeader { DialogTitle("Edit profile") DialogDescription("Make changes to your profile here. Tap save when you're done.") } VStack(alignment: .leading, spacing: 16) { VStack(alignment: .leading, spacing: 8) { FieldLabel("Name") .accessibilityHidden(true) TextField("Name", text: $name) .textFieldStyle(.input) } VStack(alignment: .leading, spacing: 8) { FieldLabel("Username") .accessibilityHidden(true) TextField("Username", text: $username) .textFieldStyle(.input) .textInputAutocapitalization(.never) } } DialogFooter { DialogClose { Text("Cancel").frame(maxWidth: .infinity) } .buttonStyle(.variant(.secondary)) Button {} label: { Text("Save changes").frame(maxWidth: .infinity) } .buttonStyle(.variant()) } } }}
import androidx.compose.foundation.layout.Arrangementimport androidx.compose.foundation.layout.Columnimport androidx.compose.foundation.layout.fillMaxWidthimport androidx.compose.foundation.text.KeyboardOptionsimport androidx.compose.runtime.Composableimport androidx.compose.runtime.getValueimport androidx.compose.runtime.mutableStateOfimport androidx.compose.runtime.saveable.rememberSaveableimport androidx.compose.runtime.setValueimport androidx.compose.ui.Modifierimport androidx.compose.ui.semantics.contentDescriptionimport androidx.compose.ui.semantics.semanticsimport androidx.compose.ui.text.input.KeyboardCapitalizationimport androidx.compose.ui.unit.dpimport com.esuiss.ui.components.Buttonimport com.esuiss.ui.components.ButtonVariantimport com.esuiss.ui.components.Dialogimport com.esuiss.ui.components.DialogDescriptionimport com.esuiss.ui.components.DialogFooterimport com.esuiss.ui.components.DialogHeaderimport com.esuiss.ui.components.DialogTitleimport com.esuiss.ui.components.Inputimport com.esuiss.ui.components.Label@Composablefun DialogOpenByDefaultExample() { DialogDefault(defaultOpen = true)}@Composablefun DialogDefault(defaultOpen: Boolean) { var open by rememberSaveable { mutableStateOf(defaultOpen) } var name by rememberSaveable { mutableStateOf("Pedro Duarte") } var username by rememberSaveable { mutableStateOf("@peduarte") } Column { Button("Edit profile", onClick = { open = true }, variant = ButtonVariant.Outline) Dialog(open = open, onOpenChange = { open = it }) { DialogHeader { DialogTitle("Edit profile") DialogDescription("Make changes to your profile here. Tap save when you're done.") } Column(verticalArrangement = Arrangement.spacedBy(16.dp)) { Column(verticalArrangement = Arrangement.spacedBy(8.dp)) { Label("Name") Input(name, { name = it }, Modifier.semantics { contentDescription = "Name" }) } Column(verticalArrangement = Arrangement.spacedBy(8.dp)) { Label("Username") Input( username, { username = it }, Modifier.semantics { contentDescription = "Username" }, keyboardOptions = KeyboardOptions(capitalization = KeyboardCapitalization.None), ) } } DialogFooter { Button("Cancel", onClick = { open = false }, modifier = Modifier.fillMaxWidth(), variant = ButtonVariant.Secondary) Button("Save changes", onClick = {}, modifier = Modifier.fillMaxWidth()) } } }}
components/ui/dialog.tsx
"use client"import { Dialog as DialogPrimitive } from "@base-ui/react/dialog"import { XIcon } from "@phosphor-icons/react"import type * as React from "react"import { Button } from "@/components/ui/button"import { useScrollbarGap } from "@/hooks/use-scrollbar-gap"import { cn } from "@/lib/utils"function Dialog({ ...props }: DialogPrimitive.Root.Props) { return <DialogPrimitive.Root data-slot="dialog" {...props} />}function DialogTrigger({ ...props }: DialogPrimitive.Trigger.Props) { return <DialogPrimitive.Trigger data-slot="dialog-trigger" {...props} />}
import { Button } from "@/components/ui/button"import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger,} from "@/components/ui/dialog"export default function DialogWithoutCloseButton() { return ( <Dialog> <DialogTrigger render={<Button variant="outline" />}> Show terms </DialogTrigger> <DialogContent showCloseButton={false}> <DialogHeader> <DialogTitle>Terms updated</DialogTitle> <DialogDescription> We have updated our terms of service. Please review the changes before continuing. </DialogDescription> </DialogHeader> <DialogFooter showCloseButton> <Button>Accept</Button> </DialogFooter> </DialogContent> </Dialog> )}
import { Button } from "@/components/ui/button"import { Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger,} from "@/components/ui/dialog"export default function DialogDestructive() { return ( <Dialog> <DialogTrigger render={<Button variant="destructive" />}> Delete project </DialogTrigger> <DialogContent> <DialogHeader> <DialogTitle>Delete this project?</DialogTitle> <DialogDescription> This permanently removes the project and all of its deployments. This action cannot be undone. </DialogDescription> </DialogHeader> <DialogFooter> <DialogClose render={<Button variant="secondary" />}> Cancel </DialogClose> <Button variant="destructive">Delete</Button> </DialogFooter> </DialogContent> </Dialog> )}
import { cn } from "cn"import { XIcon } from "phosphor-react-native"import * as React from "react"import { Platform, View, type ViewProps } from "react-native"import { FadeIn, FadeOut, Keyframe, ReduceMotion,} from "react-native-reanimated"import { FullWindowOverlay as RNFullWindowOverlay } from "react-native-screens"import * as DialogPrimitive from "@/components/primitives/dialog"import { resolveClassName } from "@/components/primitives/use-render"import { AnimatedView } from "@/components/ui/animated-view"import { Button } from "@/components/ui/button"import { Icon } from "@/components/ui/icon"
import { XIcon } from "phosphor-react-native"import * as React from "react"import { Platform, StyleSheet, useWindowDimensions, View, type ViewProps,} from "react-native"import { FadeIn, FadeOut, Keyframe, ReduceMotion,} from "react-native-reanimated"import { FullWindowOverlay as RNFullWindowOverlay } from "react-native-screens"