Dialog
The Dialog component presents content above the app, supporting focus trapping, scroll locking, and multiple presentation styles (modal, confirmation, bottom sheet).
Source
LLM docs
Package
Examples
6
Properties
46
Hooks
4
Playground
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>
);
}React Native design for iOS, Android, and Web.
Quick Links
Resources