BottomSheet

A smooth, customizable modal that slides up from the bottom

A beautiful modal component that slides up from the bottom of the screen with smooth animations and intuitive gestures. Perfect for displaying additional options, menus, filters, and forms without taking up the full screen.

Overview

BottomSheet handles animations, touch gestures, and swipe-to-close interactions automatically. It provides a native feel with customizable content and styling.

Use it for:

  • Action menus and options
  • Filter and sorting panels
  • Secondary forms and input
  • Share sheets
  • Detailed information panels
  • Bottom navigation menus

Basic Usage

import { BottomSheet, Button } from 'prizmux'
import { useState } from 'react'
import { Text, View } from 'react-native'

export default function App() {
  const [visible, setVisible] = useState(false)

  return (
    <>
      <Button 
        title="Open Sheet" 
        onPress={() => setVisible(true)}
      />
      
      <BottomSheet
        visible={visible}
        onClose={() => setVisible(false)}
        title="Options"
      >
        <Text>Sheet content goes here</Text>
      </BottomSheet>
    </>
  )
}

With Custom Content

import { BottomSheet, Button } from 'prizmux'
import { View, Text } from 'react-native'
import { useState } from 'react'

function ActionSheet() {
  const [visible, setVisible] = useState(false)

  const actions = [
    { label: 'Edit', icon: '✏️', onPress: () => {} },
    { label: 'Share', icon: '📤', onPress: () => {} },
    { label: 'Delete', icon: '🗑️', onPress: () => {} },
  ]

  return (
    <>
      <Button 
        title="Menu" 
        onPress={() => setVisible(true)}
      />
      
      <BottomSheet
        visible={visible}
        onClose={() => setVisible(false)}
        title="Actions"
        showDragHandle={true}
        swipeToClose={true}
      >
        <View style={{ paddingHorizontal: 16, paddingBottom: 20 }}>
          {actions.map((action, idx) => (
            <Button
              key={idx}
              title={action.label}
              icon={<Text>{action.icon}</Text>}
              iconPosition="left"
              fullWidth={true}
              variant="outline"
              style={{ marginBottom: 12 }}
              onPress={() => {
                action.onPress()
                setVisible(false)
              }}
            />
          ))}
        </View>
      </BottomSheet>
    </>
  )
}

With Dismiss on Outside Tap

<BottomSheet
  visible={visible}
  onClose={() => setVisible(false)}
  dismissOnTouchOutside={true}
  showCloseButton={true}
>
  <Text>Tap outside to close</Text>
</BottomSheet>

Without Drag Handle

<BottomSheet
  visible={visible}
  onClose={() => setVisible(false)}
  showDragHandle={false}
  showCloseButton={true}
>
  <Text>Close using the button</Text>
</BottomSheet>

Props Reference

PropTypeDefaultDescription
visiblebooleanfalseControl sheet visibility
onClose() => voidRequiredCalled when sheet is dismissed
titlestring-Optional header title
childrenReactNodeRequiredSheet content
dismissOnTouchOutsidebooleantrueClose when tapping backdrop
showCloseButtonbooleantrueDisplay close button
showDragHandlebooleantrueShow drag handle indicator
swipeToClosebooleantrueEnable swipe-to-close gesture
closeIconReactNode-Custom close button icon

Usage

Basic Bottom Sheet

import { BottomSheet, Button, Text } from 'prizmux'
import { useState } from 'react'

export default function App() {
  const [visible, setVisible] = useState(false)

  return (
    <>
      <Button 
        title="Open Sheet" 
        onPress={() => setVisible(true)}
      />
      
      <BottomSheet
        isVisible={visible}
        onClose={() => setVisible(false)}
        snapPoints={[200]}
      >
        <Text>Sheet content</Text>
      </BottomSheet>
    </>
  )
}

With Multiple Snap Points

<BottomSheet
  isVisible={visible}
  onClose={() => setVisible(false)}
  snapPoints={[100, 300, 600]}
>
  <Text>Drag to snap to different positions</Text>
</BottomSheet>

Action Menu

import { BottomSheet, Button, Text } from 'prizmux'
import { View } from 'react-native'

function ActionMenu() {
  const [visible, setVisible] = useState(false)

  const actions = [
    { label: 'Edit', onPress: handleEdit },
    { label: 'Share', onPress: handleShare },
    { label: 'Delete', onPress: handleDelete },
  ]

  return (
    <>
      <Button 
        title="Options" 
        onPress={() => setVisible(true)}
      />
      
      <BottomSheet
        isVisible={visible}
        onClose={() => setVisible(false)}
        snapPoints={[250]}
      >
        <View style={{ paddingHorizontal: 16, gap: 12 }}>
          {actions.map((action) => (
            <Button
              key={action.label}
              title={action.label}
              variant="outline"
              onPress={() => {
                action.onPress()
                setVisible(false)
              }}
            />
          ))}
        </View>
      </BottomSheet>
    </>
  )
}

Filter Sheet

import { BottomSheet, Button, Text, Checkbox } from 'prizmux'
import { View } from 'react-native'

function FilterSheet() {
  const [visible, setVisible] = useState(false)
  const [filters, setFilters] = useState({
    inStock: true,
    onSale: false,
  })

  return (
    <>
      <Button 
        title="Filter" 
        onPress={() => setVisible(true)}
      />
      
      <BottomSheet
        isVisible={visible}
        onClose={() => setVisible(false)}
        snapPoints={[300]}
      >
        <View style={{ padding: 16, gap: 16 }}>
          <Text variant="title">Filters</Text>
          
          <Checkbox
            label="In Stock"
            value={filters.inStock}
            onChange={(inStock) => 
              setFilters({ ...filters, inStock })
            }
          />
          
          <Checkbox
            label="On Sale"
            value={filters.onSale}
            onChange={(onSale) => 
              setFilters({ ...filters, onSale })
            }
          />
          
          <Button 
            title="Apply Filters"
            onPress={() => setVisible(false)}
          />
        </View>
      </BottomSheet>
    </>
  )
}

Notes

  • BottomSheet automatically handles backdrop dismiss
  • Supports gestures for dismissing and resizing
  • Content is automatically scrollable if it exceeds snap points
  • Handle indicator helps users understand they can drag
  • Works seamlessly with keyboard