Dialog

The Dialog component presents content above the app, supporting focus trapping, scroll locking, and multiple presentation styles (modal, confirmation, bottom sheet).

Basics

Call openDialog with variant: 'modal' to show a titled dialog and wire action buttons to closeDialog when the user makes a choice.

Loading demo…

import { Block, Button, Row, Text, useDialog } from '@plocks/ui'; export function Demo() { const { openDialog, closeDialog } = useDialog(); const showBasicDialog = () => { const dialogId = openDialog({ variant: 'modal', title: 'Basic Dialog', content: ( <Block> <Text>This is a basic modal dialog with theme-aware styling.</Text> <Row gap="sm" justify="flex-end" mt="sm"> <Button variant="secondary" onPress={() => closeDialog(dialogId)}> Cancel </Button> <Button variant="filled" onPress={() => closeDialog(dialogId)}> OK </Button> </Row> </Block> ) }); }; return ( <Button onPress={showBasicDialog}>Open Basic Dialog</Button> ); }

Bottom Sheet

Switch the dialog variant to 'bottomsheet' to get swipe-to-dismiss behavior and retain full control with closeDialog handlers.

Loading demo…

import { Block, Button, Text, useDialog } from '@plocks/ui'; export function Demo() { const { openDialog, closeDialog } = useDialog(); const showBottomSheetDialog = () => { const dialogId = openDialog({ variant: 'bottomsheet', content: ( <Block> <Text>This dialog slides up from the bottom with theme-aware styling.</Text> <Button variant="subtle" onPress={() => closeDialog(dialogId)}> Close </Button> </Block> ) }); }; return ( <Button onPress={showBottomSheetDialog}>Open Bottom Sheet</Button> ); }

Variants

Open the modal, bottom sheet, and fullscreen presentations to compare their placement and dismissal.

Loading demo…

import { Button, Row, Text, useDialog } from '@plocks/ui'; const variants = ['modal', 'bottomsheet', 'fullscreen'] as const; export function Demo() { const { openDialog } = useDialog(); return ( <Row gap="sm" wrap="wrap"> {variants.map(variant => ( <Button key={variant} variant="light" onPress={() => openDialog({ variant, title: `${variant} dialog`, content: <Text>Dialog content in the {variant} presentation.</Text>, })}> Open {variant} </Button> ))} </Row> ); }

Confirmation

Pair variant: 'modal' with a destructive button (color="error") to confirm irreversible actions before calling your business logic.

Loading demo…

import { Block, Button, Row, Text, useDialog } from '@plocks/ui'; export function Demo() { const { openDialog, closeDialog } = useDialog(); const showConfirmationDialog = () => { const dialogId = openDialog({ variant: 'modal', title: 'Confirm Action', content: ( <Block> <Text>Are you sure you want to delete this item?</Text> <Text size="sm" c="secondary"> This action cannot be undone. </Text> <Row gap="sm" justify="flex-end" mt="sm"> <Button variant="subtle" onPress={() => closeDialog(dialogId)}> Cancel </Button> <Button variant="filled" color="error" onPress={() => closeDialog(dialogId)}> Delete </Button> </Row> </Block> ) }); }; return ( <Button onPress={showConfirmationDialog}>Show Confirmation</Button> ); }

Form Dialog

Embed inputs in the dialog content, collect values via controlled callbacks, and validate before resolving the promise or calling closeDialog.

Loading demo…

import { useRef } from 'react'; import { TextInput } from 'react-native'; import { Block, Button, Input, Row, useDialog } from '@plocks/ui'; export function Demo() { const { openDialog, closeDialog } = useDialog(); const nameRef = useRef<TextInput>(null); const showFormDialog = () => { const formData = { name: '', email: '' }; const dialogId = openDialog({ variant: 'modal', title: 'Create Account', // Focus the name field once the open transition settles. `autoFocus: true` // picks the first focusable field automatically, but only on web — a ref // works on every platform. autoFocus: nameRef, content: ( <Block> <Input inputRef={nameRef} placeholder="Your name" label="Name" onChangeText={(text) => { formData.name = text; }} /> <Input placeholder="your@email.com" label="Email" keyboardType="email-address" onChangeText={(text) => { formData.email = text; }} /> <Row gap="sm" justify="flex-end" mt="sm"> <Button variant="secondary" onPress={() => closeDialog(dialogId)}> Cancel </Button> <Button variant="filled" onPress={() => { if (!formData.name || !formData.email) return; closeDialog(dialogId); }} > Create account </Button> </Row> </Block> ) }); }; return ( <Button onPress={showFormDialog}>Open Form Dialog</Button> ); }

Title customization

titleProps accepts any <Text> props (ff, fw, lts, tt, size, c, style) and applies them to the dialog header without changing the rest of the chrome. The same prop is also accepted by openDialog({ titleProps }) for imperative dialogs.

Loading demo…

import { Block, Button, Text, useDialog, type DialogConfig } from '@plocks/ui'; export function Demo() { const { openDialog } = useDialog(); const open = (titleProps: DialogConfig['titleProps']) => { openDialog({ variant: 'modal', title: 'Welcome aboard', titleProps, content: <Text>Dialog title styled via `titleProps`.</Text>, }); }; return ( <Block> <Button onPress={() => open(undefined)}>Default</Button> <Button onPress={() => open({ tt: 'uppercase', lts: 1.5, fw: '700', size: 'sm', }) } > Uppercase tracked </Button> <Button onPress={() => open({ ff: 'Georgia, serif', size: 'xl', fw: '600', }) } > Serif headline </Button> <Button onPress={() => open({ c: 'primary', fw: '700', ff: 'monospace', }) } > Brand-coloured monospace </Button> </Block> ); }
Scan to open this page on your phoneplocks.dev/ui/Dialog