Displays a modal overlay with animated transitions and gesture-based dismissal.
import { Dialog, useDialog, useDialogAnimation } from 'heroui-native';
<Dialog>
<Dialog.Trigger>...</Dialog.Trigger>
<Dialog.Portal>
<Dialog.Overlay>...</Dialog.Overlay>
<Dialog.Content>
<Dialog.Close>...</Dialog.Close>
<Dialog.Title>...</Dialog.Title>
<Dialog.Description>...</Dialog.Description>
</Dialog.Content>
</Dialog.Portal>
</Dialog>
- Dialog: Root component that manages open state and provides context to child components.
- Dialog.Trigger: Pressable element that opens the dialog when pressed.
- Dialog.Portal: Renders dialog content in a portal with centered layout and animation control.
- Dialog.Overlay: Background overlay that appears behind the dialog content, typically closes dialog when pressed.
- Dialog.Content: Main dialog container with gesture support for drag-to-dismiss.
- Dialog.Close: Close button that dismisses the dialog when pressed.
- Dialog.Title: Dialog title text with semantic heading role.
- Dialog.Description: Dialog description text that provides additional context.
Simple dialog with title, description, and close button.
<Dialog isOpen={isOpen} onOpenChange={setIsOpen}>
<Dialog.Trigger asChild>
<Button>Open Dialog</Button>
</Dialog.Trigger>
<Dialog.Portal>
<Dialog.Overlay />
<Dialog.Content>
<Dialog.Close />
<Dialog.Title>...</Dialog.Title>
<Dialog.Description>...</Dialog.Description>
</Dialog.Content>
</Dialog.Portal>
</Dialog>
Configure open and close animations with spring or timing. The closeDelay should typically match your closing animation duration.
<Dialog
isOpen={isOpen}
onOpenChange={setIsOpen}
closeDelay={200} // Match this with closing animation duration
animation={{
entering: {
type: 'spring',
config: { damping: 130, stiffness: 1100 },
},
exiting: {
type: 'timing',
config: { duration: 200 }, // Should match closeDelay
},
}}
>
<Dialog.Trigger>...</Dialog.Trigger>
<Dialog.Portal>
<Dialog.Overlay />
<Dialog.Content>...</Dialog.Content>
</Dialog.Portal>
</Dialog>
Replace the default overlay with custom content.
<Dialog isOpen={isOpen} onOpenChange={setIsOpen}>
<Dialog.Trigger>...</Dialog.Trigger>
<Dialog.Portal>
<Dialog.Overlay className="bg-transparent">
<BlurView style={StyleSheet.absoluteFill} />
</Dialog.Overlay>
<Dialog.Content>...</Dialog.Content>
</Dialog.Portal>
</Dialog>
Handle long content with scroll views.
<Dialog isOpen={isOpen} onOpenChange={setIsOpen}>
<Dialog.Trigger>...</Dialog.Trigger>
<Dialog.Portal>
<Dialog.Overlay />
<Dialog.Content>
<Dialog.Close />
<Dialog.Title>...</Dialog.Title>
<View className="h-[300px]">
<ScrollView>...</ScrollView>
</View>
</Dialog.Content>
</Dialog.Portal>
</Dialog>
Dialog with text inputs and keyboard handling.
<Dialog isOpen={isOpen} onOpenChange={setIsOpen}>
<Dialog.Trigger>...</Dialog.Trigger>
<Dialog.Portal>
<Dialog.Overlay />
<KeyboardAvoidingView behavior="padding">
<Dialog.Content>
<Dialog.Close />
<Dialog.Title>...</Dialog.Title>
<TextField>...</TextField>
<Button onPress={handleSubmit}>Submit</Button>
</Dialog.Content>
</KeyboardAvoidingView>
</Dialog.Portal>
</Dialog>
import { Button, Dialog } from 'heroui-native';
import { View } from 'react-native';
import { useState } from 'react';
export default function DialogExample() {
const [isOpen, setIsOpen] = useState(false);
return (
<Dialog isOpen={isOpen} onOpenChange={setIsOpen}>
<Dialog.Trigger asChild>
<Button variant="primary">Open Dialog</Button>
</Dialog.Trigger>
<Dialog.Portal>
<Dialog.Overlay />
<Dialog.Content>
<Dialog.Close />
<View className="mb-5 gap-1.5">
<Dialog.Title>Confirm Action</Dialog.Title>
<Dialog.Description>
Are you sure you want to proceed with this action? This cannot be
undone.
</Dialog.Description>
</View>
<View className="flex-row justify-end gap-3">
<Dialog.Close asChild>
<Button variant="ghost" size="sm">
Cancel
</Button>
</Dialog.Close>
<Button size="sm">Confirm</Button>
</View>
</Dialog.Content>
</Dialog.Portal>
</Dialog>
);
}
| prop | type | default | description |
|---|
children | React.ReactNode | - | Dialog content and trigger elements |
isOpen | boolean | - | Controlled open state of the dialog |
isDefaultOpen | boolean | false | Initial open state when uncontrolled |
closeDelay | number | 300 | Delay in milliseconds before dialog closes (should match closing animation duration) |
isDismissKeyboardOnClose | boolean | true | Whether to dismiss keyboard when dialog closes |
animation | DialogRootAnimation | - | Animation configuration |
onOpenChange | (value: boolean) => void | - | Callback when open state changes |
...ViewProps | ViewProps | - | All standard React Native View props are supported |
Animation configuration for dialog root component. Can be:
false or "disabled": Disable only root animations
"disable-all": Disable all animations including children
true or undefined: Use default animations
object: Custom animation configuration
| prop | type | default | description |
|---|
entering.value | SpringAnimationConfig | TimingAnimationConfig | { type: 'timing',
config: { duration: 200,
easing: Easing.out(Easing.ease) } } | Animation configuration for opening |
exiting.value | SpringAnimationConfig | TimingAnimationConfig | { type: 'timing',
config: { duration: 150,
easing: Easing.bezier(0.4, 0, 1, 1) } } | Animation configuration for closing |
| prop | type | default | description |
|---|
type | 'spring' | - | Animation type (must be 'spring') |
config | WithSpringConfig | - | Reanimated spring animation configuration |
| prop | type | default | description |
|---|
type | 'timing' | - | Animation type (must be 'timing') |
config | WithTimingConfig | - | Reanimated timing animation configuration |
| prop | type | default | description |
|---|
children | React.ReactNode | - | Trigger element content |
asChild | boolean | - | Render as child element without wrapper |
...PressableProps | PressableProps | - | All standard React Native PressableProps props are supported |
| prop | type | default | description |
|---|
children | React.ReactNode | - | Portal content (overlay and dialog) |
className | string | - | Additional CSS classes for portal container |
style | StyleProp<ViewStyle> | - | Additional styles for portal container |
hostName | string | - | Optional portal host name for specific container |
forceMount | boolean | - | Force mount when closed for animation purposes |
| prop | type | default | description |
|---|
children | React.ReactNode | - | Custom overlay content |
className | string | - | Additional CSS classes for overlay |
style | ViewStyle | - | Additional styles for overlay container |
animation | DialogOverlayAnimation | - | Animation configuration |
isCloseOnPress | boolean | true | Whether pressing overlay closes dialog |
forceMount | boolean | - | Force mount when closed for animation purposes |
...PressableProps | PressableProps | - | All standard React Native Pressable props are supported |
Animation configuration for dialog overlay component. Can be:
false or "disabled": Disable all animations
true or undefined: Use default animations
object: Custom animation configuration
| prop | type | default | description |
|---|
opacity.value | [number, number, number] | [0, 1, 0] | Opacity values [idle, open, close] |
| prop | type | default | description |
|---|
children | React.ReactNode | - | Dialog content |
className | string | - | Additional CSS classes for content container |
style | StyleProp<ViewStyle> | - | Additional styles for content container |
onLayout | (event: LayoutChangeEvent) => void | - | Layout event handler |
animation | DialogContentAnimation | - | Animation configuration |
isSwipeable | boolean | true | Whether the dialog content can be swiped to dismiss |
forceMount | boolean | - | Force mount when closed for animation purposes |
...Animated.ViewProps | Animated.ViewProps | - | All Reanimated Animated.View props are supported |
Animation configuration for dialog content component. Can be:
false or "disabled": Disable all animations
true or undefined: Use default animations
object: Custom animation configuration
| prop | type | default | description |
|---|
opacity.value | [number, number, number] | [0, 1, 0] | Opacity values [idle, open, close] |
scale.value | [number, number, number] | [0.97, 1, 0.97] | Scale values [idle, open, close] |
| prop | type | default | description |
|---|
children | React.ReactNode | - | Custom close button content |
className | string | - | Additional CSS classes for close button |
iconProps | DialogCloseIconProps | - | Configuration for default close icon |
hitSlop | number | 12 | Hit slop area for the close button |
asChild | boolean | - | Render as child element without wrapper |
...TouchableOpacityProps | TouchableOpacityProps | - | All standard React Native TouchableOpacity props are supported |
| prop | type | description |
|---|
size | number | Icon size (default: 18) |
color | string | Icon color (default: theme color muted) |
| prop | type | default | description |
|---|
children | React.ReactNode | - | Title content |
className | string | - | Additional CSS classes for title |
...TextProps | TextProps | - | All standard React Native Text props are supported |
| prop | type | default | description |
|---|
children | React.ReactNode | - | Description content |
className | string | - | Additional CSS classes for description |
...TextProps | TextProps | - | All standard React Native Text props are supported |
Hook to access dialog primitive context.
const { isOpen, onOpenChange } = useDialog();
| property | type | description |
|---|
isOpen | boolean | Current open state |
onOpenChange | (value: boolean) => void | Function to change open state |
Hook to access dialog animation context for advanced customization.
const { dialogState, progress, isDragging, isGestureReleaseAnimationRunning } =
useDialogAnimation();
| property | type | description |
|---|
dialogState | 'idle' | 'open' | 'close' | Internal dialog state |
progress | SharedValue<number> | Animation progress (0=idle, 1=open, 2=close) |
isDragging | SharedValue<boolean> | Whether dialog is being dragged |
isGestureReleaseAnimationRunning | SharedValue<boolean> | Whether gesture release animation is running |