Bottom Sheet

Displays a bottom sheet that slides up from the bottom with animated transitions and swipe-to-dismiss gestures.

Import

import { BottomSheet } from 'heroui-native';

Anatomy

<BottomSheet>
  <BottomSheet.Trigger>...</BottomSheet.Trigger>
  <BottomSheet.Portal>
    <BottomSheet.Overlay>...</BottomSheet.Overlay>
    <BottomSheet.Content>
      <BottomSheet.Close>...</BottomSheet.Close>
      <BottomSheet.Title>...</BottomSheet.Title>
      <BottomSheet.Description>...</BottomSheet.Description>
    </BottomSheet.Content>
  </BottomSheet.Portal>
</BottomSheet>
  • BottomSheet: Root component that manages open state and provides context to child components.
  • BottomSheet.Trigger: Pressable element that opens the bottom sheet when pressed.
  • BottomSheet.Portal: Renders bottom sheet content in a portal with full window overlay.
  • BottomSheet.Overlay: Background overlay that covers the screen, typically closes bottom sheet when pressed.
  • BottomSheet.Content: Main bottom sheet container using @gorhom/bottom-sheet for rendering with gesture support.
  • BottomSheet.Close: Close button that dismisses the bottom sheet when pressed.
  • BottomSheet.Title: Bottom sheet title text with semantic heading role and accessibility linking.
  • BottomSheet.Description: Bottom sheet description text that provides additional context with accessibility linking.

Usage

Basic Bottom Sheet

Simple bottom sheet with title, description, and close button.

<BottomSheet>
  <BottomSheet.Trigger asChild>
    <Button>Open Bottom Sheet</Button>
  </BottomSheet.Trigger>
  <BottomSheet.Portal>
    <BottomSheet.Overlay />
    <BottomSheet.Content>
      <BottomSheet.Close />
      <BottomSheet.Title>...</BottomSheet.Title>
      <BottomSheet.Description>...</BottomSheet.Description>
    </BottomSheet.Content>
  </BottomSheet.Portal>
</BottomSheet>

Detached Bottom Sheet

Bottom sheet that appears detached from the bottom edge with custom spacing.

<BottomSheet>
  <BottomSheet.Trigger>...</BottomSheet.Trigger>
  <BottomSheet.Portal>
    <BottomSheet.Overlay />
    <BottomSheet.Content
      detached={true}
      bottomInset={insets.bottom + 12}
      className="mx-4"
      backgroundClassName="rounded-[32px]"
    >
      ...
    </BottomSheet.Content>
  </BottomSheet.Portal>
</BottomSheet>

Scrollable with Snap Points

Bottom sheet with multiple snap points and scrollable content.

<BottomSheet>
  <BottomSheet.Trigger>...</BottomSheet.Trigger>
  <BottomSheet.Portal>
    <BottomSheet.Overlay />
    <BottomSheet.Content snapPoints={['25%', '50%', '90%']}>
      <ScrollView>...</ScrollView>
    </BottomSheet.Content>
  </BottomSheet.Portal>
</BottomSheet>

Custom Overlay

Replace the default overlay with custom content like blur effects.

import { BottomSheet, useBottomSheetAnimation } from 'heroui-native';
import { StyleSheet } from 'react-native';
import { interpolate, useDerivedValue } from 'react-native-reanimated';
import { AnimatedBlurView } from './animated-blur-view';
import { useUniwind } from 'uniwind';

export const BottomSheetBlurOverlay = () => {
  const { theme } = useUniwind();
  const { progress } = useBottomSheetAnimation();

  const blurIntensity = useDerivedValue(() => {
    return interpolate(progress.get(), [0, 1, 2], [0, 40, 0]);
  });

  return (
    <BottomSheet.Close style={StyleSheet.absoluteFill}>
      <AnimatedBlurView
        blurIntensity={blurIntensity}
        tint={theme === 'dark' ? 'dark' : 'systemUltraThinMaterialDark'}
        style={StyleSheet.absoluteFill}
      />
    </BottomSheet.Close>
  );
};
<BottomSheet>
  <BottomSheet.Trigger>...</BottomSheet.Trigger>
  <BottomSheet.Portal>
    <BottomSheetBlurOverlay />
    <BottomSheet.Content>...</BottomSheet.Content>
  </BottomSheet.Portal>
</BottomSheet>

Text Input with Keyboard Avoidance

Using TextField.Input from heroui-native inside a bottom sheet requires special handling for proper keyboard behavior. You need to:

  1. Pass custom onFocus and onBlur handlers to TextField.Input that communicate with the bottom sheet's internal keyboard state using useBottomSheetInternal hook from @gorhom/bottom-sheet
  2. Use BottomSheetScrollView from @gorhom/bottom-sheet instead of regular ScrollView for proper keyboard avoidance

See the complete example: bottom-sheet-with-text-input.tsx

Example

import { BottomSheet, Button } from 'heroui-native';
import { useState } from 'react';
import { View } from 'react-native';
import { withUniwind } from 'uniwind';
import Ionicons from '@expo/vector-icons/Ionicons';

const StyledIonicons = withUniwind(Ionicons);

export default function BottomSheetExample() {
  const [isOpen, setIsOpen] = useState(false);

  return (
    <BottomSheet isOpen={isOpen} onOpenChange={setIsOpen}>
      <BottomSheet.Trigger asChild>
        <Button variant="secondary">Open Bottom Sheet</Button>
      </BottomSheet.Trigger>
      <BottomSheet.Portal>
        <BottomSheet.Overlay />
        <BottomSheet.Content>
          <View className="items-center mb-5">
            <View className="size-20 items-center justify-center rounded-full bg-green-500/10">
              <StyledIonicons
                name="shield-checkmark"
                size={40}
                className="text-green-500"
              />
            </View>
          </View>
          <View className="mb-8 gap-2 items-center">
            <BottomSheet.Title className="text-center">
              Keep yourself safe
            </BottomSheet.Title>
            <BottomSheet.Description className="text-center">
              Update your software to the latest version for better security and
              performance.
            </BottomSheet.Description>
          </View>
          <View className="gap-3">
            <Button onPress={() => setIsOpen(false)}>Update Now</Button>
            <Button variant="tertiary" onPress={() => setIsOpen(false)}>
              Later
            </Button>
          </View>
        </BottomSheet.Content>
      </BottomSheet.Portal>
    </BottomSheet>
  );
}

You can find more examples in the GitHub repository.

API Reference

BottomSheet

proptypedefaultdescription
childrenReact.ReactNode-Bottom sheet content and trigger elements
isOpenboolean-Controlled open state of the bottom sheet
isDefaultOpenbooleanfalseInitial open state when uncontrolled
isDismissKeyboardOnClosebooleantrueWhether to dismiss keyboard when bottom sheet closes
animationBottomSheetRootAnimation-Animation configuration
onOpenChange(value: boolean) => void-Callback when open state changes
...ViewPropsViewProps-All standard React Native View props are supported

BottomSheetRootAnimation

Animation configuration for bottom sheet root component. Can be:

  • "disable-all": Disable all animations including children
  • undefined: Use default animations
  • object: Custom animation configuration
proptypedefaultdescription
state'disabled' | 'disable-all' | boolean-Disable animations while customizing properties

BottomSheet.Trigger

proptypedefaultdescription
childrenReact.ReactNode-Trigger element content
asChildboolean-Render as child element without wrapper
...TouchableOpacityPropsTouchableOpacityProps-All standard React Native TouchableOpacity props are supported

BottomSheet.Portal

proptypedefaultdescription
childrenReact.ReactNode-Portal content (overlay and bottom sheet)
classNamestring-Additional CSS classes for portal container
styleStyleProp<ViewStyle>-Additional styles for portal container
hostNamestring-Optional portal host name for specific container
forceMountboolean-Force mount when closed for animation purposes

BottomSheet.Overlay

proptypedefaultdescription
childrenReact.ReactNode-Custom overlay content
classNamestring-Additional CSS classes for overlay
styleViewStyle-Additional styles for overlay container
animationBottomSheetOverlayAnimation-Animation configuration
isAnimatedStyleActivebooleantrueWhether animated styles (react-native-reanimated) are active
isCloseOnPressbooleantrueWhether pressing overlay closes bottom sheet
forceMountboolean-Force mount when closed for animation purposes
...PressablePropsPressableProps-All standard React Native Pressable props are supported

BottomSheetOverlayAnimation

Animation configuration for bottom sheet overlay component. Can be:

  • false or "disabled": Disable all animations
  • true or undefined: Use default animations
  • object: Custom animation configuration
proptypedefaultdescription
state'disabled' | boolean-Disable animations while customizing properties
opacity.value[number, number, number][0, 1, 0]Opacity values [idle, open, close]

BottomSheet.Content

proptypedefaultdescription
childrenReact.ReactNode-Bottom sheet content
classNamestring-Additional CSS classes for bottom sheet container
containerClassNamestring-Additional CSS classes for container
contentContainerClassNamestring-Additional CSS classes for content container
backgroundClassNamestring-Additional CSS classes for background
handleClassNamestring-Additional CSS classes for handle
handleIndicatorClassNamestring-Additional CSS classes for handle indicator
contentContainerPropsOmit<BottomSheetViewProps, 'children'>-Props for the content container
animationAnimationDisabled-Animation configuration
...GorhomBottomSheetPropsPartial<GorhomBottomSheetProps>-All @gorhom/bottom-sheet props are supported

Note: You can use all components from @gorhom/bottom-sheet inside the content, such as BottomSheetView, BottomSheetScrollView, BottomSheetFlatList, etc.

BottomSheet.Close

proptypedefaultdescription
childrenReact.ReactNode-Custom close button content
classNamestring-Additional CSS classes for close button
iconPropsBottomSheetCloseIconProps-Configuration for default close icon
hitSlopnumber12Hit slop area for the close button
asChildboolean-Render as child element without wrapper
...PressablePropsPressableProps-All standard React Native Pressable props are supported

BottomSheetCloseIconProps

proptypedescription
sizenumberIcon size (default: 18)
colorstringIcon color (default: theme color muted)

BottomSheet.Title

proptypedefaultdescription
childrenReact.ReactNode-Title content
classNamestring-Additional CSS classes for title
...TextPropsTextProps-All standard React Native Text props are supported

BottomSheet.Description

proptypedefaultdescription
childrenReact.ReactNode-Description content
classNamestring-Additional CSS classes for description
...TextPropsTextProps-All standard React Native Text props are supported

Hooks

useBottomSheet

Hook to access bottom sheet primitive context.

const { isOpen, onOpenChange } = useBottomSheet();
propertytypedescription
isOpenbooleanCurrent open state
onOpenChange(value: boolean) => voidFunction to change open state

useBottomSheetAnimation

Hook to access bottom sheet animation context for advanced customization.

const { bottomSheetState, progress } = useBottomSheetAnimation();
propertytypedescription
bottomSheetState'idle' | 'open' | 'close'Internal bottom sheet state
progressSharedValue<number>Animation progress (0=idle, 1=open, 2=close)

Special Notes

Handling Close Callbacks

It's recommended to use BottomSheet's onOpenChange prop for handling close callbacks, as it reliably fires for all close scenarios (swiping down, pressing overlay, pressing close button, programmatic close, etc.).

<BottomSheet
  isOpen={isOpen}
  onOpenChange={(value) => {
    setIsOpen(value);
    if (!value) {
      // This callback runs whenever the bottom sheet closes
      // regardless of how it was closed
      yourCallbackOnClose();
    }
  }}
>
  ...
</BottomSheet>

Note: BottomSheet.Content's onClose prop (from @gorhom/bottom-sheet) has limitations and will only fire when the bottom sheet is closed by swiping down. It won't fire when closing via overlay press, close button, or programmatic close. For reliable close callbacks, always use BottomSheet's onOpenChange prop instead.

On this page