Skip to content

ZainNemat/szn-ui-react-native-bottom-sheet

Repository files navigation

React Native Bottom Sheet

A smooth, customizable bottom sheet component for React Native with gesture support and TypeScript definitions specifically built for expo SDK 54 and react native reanimated v4 . Ensure that you're using the latest version as previous versions may be broken or depreciated.

Bottom Sheet Demo

Features

  • 🎯 TypeScript Support - Full TypeScript definitions included
  • 🎨 Customizable - Highly customizable appearance and behavior
  • 📱 Cross Platform - Works on both iOS and Android
  • Gesture Support - Smooth drag-to-dismiss functionality
  • ⌨️ Keyboard Aware - Proper keyboard handling
  • 🎭 Flexible Content - Support for any content with scroll view
  • 🎪 Animation - Smooth enter/exit animations

Installation

npm install szn-ui-react-native-bottom-sheet

Peer Dependencies

This package requires the following peer dependencies:

npm install react-native-gesture-handler react-native-reanimated

Make sure to follow the installation instructions for:

Usage

import React, { useState } from 'react';
import { View, Text, Button } from 'react-native';
import { BottomSheet } from "szn-ui-react-native-bottom-sheet";

const App = (): JSX.Element => {
  const [isVisible, setIsVisible] = useState(false);

  return (
    <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
      <Button title="Open Bottom Sheet" onPress={() => setIsVisible(true)} />
      
      <BottomSheet
        isVisible={isVisible}
        onClose={() => setIsVisible(false)}
      >
        <View style={{ padding: 20 }}>
          <Text>Hello from bottom sheet!</Text>
        </View>
      </BottomSheet>
    </View>
  );
};

export default App;

Props

Prop Type Default Description
isVisible boolean Required Controls the visibility of the bottom sheet
onClose () => void Required Callback when the sheet should be closed
children ReactNode Required Content to render inside the bottom sheet
height number SCREEN_HEIGHT * 0.9 Height of the bottom sheet
backgroundColor string 'white' Background color of the bottom sheet
showDragHandle boolean true Whether to show the drag handle
containerStyle ViewStyle undefined Additional styles for the sheet container
title string undefined Title text to display in the header
showHeader boolean false Whether to show the header with title and cancel button
cancelText string 'Cancel' Text for the cancel button in header

You can customize the sheet's background using either the backgroundColor prop or via containerStyle, but if both are provided, the backgroundColor prop takes precedence.

Examples

With Header

<BottomSheet
  isVisible={isVisible}
  onClose={() => setIsVisible(false)}
  showHeader
  title="Settings"
  cancelText="Done"
>
  <View style={{ padding: 20 }}>
    <Text>Settings content here</Text>
  </View>
</BottomSheet>

If you wish to use your own custom close button instead of the cancel text prop

import React, { useRef, useState } from 'react';
import { View, Text, TouchableOpacity } from 'react-native';
import { BottomSheet, BottomSheetRef } from 'szn-ui-react-native-bottom-sheet';
import { Ionicons } from '@expo/vector-icons';

export default function MyScreen(): JSX.Element {
  const [isVisible, setIsVisible] = useState(false);
  const sheetRef = useRef<BottomSheetRef>(null);

  const handleOpen = () => setIsVisible(true);
  const handleClose = () => setIsVisible(false);

  return (
    <>
      <TouchableOpacity onPress={handleOpen}>
        <Text>Open Bottom Sheet</Text>
      </TouchableOpacity>

      <BottomSheet
        ref={sheetRef}
        isVisible={isVisible}
        onClose={handleClose}
        showHeader={false}
        showDragHandle={true}
        height={600}
      >
        <View className="px-5 pb-3 border-b border-gray-100 flex-row justify-between items-center">
          <Text className="text-lg font-semibold text-gray-900">My Custom Header</Text>
          <TouchableOpacity
            onPress={() => sheetRef.current?.dismiss()}
            className="p-2 bg-gray-100 rounded-full"
          >
            <Ionicons name="close" size={18} color="#374151" />
          </TouchableOpacity>
        </View>

        <Text className="p-5">Sheet Content Here...</Text>
      </BottomSheet>
    </>
  );
}

Custom Height

<BottomSheet
  isVisible={isVisible}
  onClose={() => setIsVisible(false)}
  height={400}
>
  <View style={{ padding: 20 }}>
    <Text>Custom height content</Text>
  </View>
</BottomSheet>

Multi-Step Flow

const [step, setStep] = useState(1);

return (
  <BottomSheet
    isVisible={true}
    onClose={() => {}}
    height={step === 1 ? 200 : step === 2 ? 400 : 600}
  >
    {step === 1 && <StepOne onNext={() => setStep(2)} />}
    {step === 2 && <StepTwo onNext={() => setStep(3)} />}
    {step === 3 && <StepThree />}
  </BottomSheet>
);

Custom Background Color

<BottomSheet
  isVisible={isVisible}
  onClose={() => setIsVisible(false)}
  backgroundColor="#fef2f2"
>
  <View style={{ padding: 20 }}>
    <Text>Light pink background</Text>
  </View>
</BottomSheet>

Custom Styling

<BottomSheet
  isVisible={isVisible}
  onClose={() => setIsVisible(false)}
  containerStyle={{
    backgroundColor: '#f0f0f0',
    borderTopLeftRadius: 30,
    borderTopRightRadius: 30,
  }}
>
  <View style={{ padding: 20 }}>
    <Text>Custom styled content</Text>
  </View>
</BottomSheet>

Full Usage Example

import { useState } from "react";
import {
  KeyboardAvoidingView,
  Platform,
  Pressable,
  ScrollView,
  StyleSheet,
  TextInput,
} from "react-native";
import { BottomSheet } from "szn-ui-react-native-bottom-sheet";
import { ThemedText } from "@/components/themed-text";
import { ThemedView } from "@/components/themed-view";
import ParallaxScrollView from "@/components/parallax-scroll-view";
import { Image } from "expo-image";
import { HelloWave } from "@/components/hello-wave";

export default function HomeScreen() {
  const [isBottomSheetVisible, setIsBottomSheetVisible] = useState(false);
  const [inputValue, setInputValue] = useState("");

  const openBottomSheet = () => setIsBottomSheetVisible(true);
  const closeBottomSheet = () => setIsBottomSheetVisible(false);

  return (
    <>
      <ParallaxScrollView
        headerBackgroundColor={{ light: "#A1CEDC", dark: "#1D3D47" }}
        headerImage={
          <Image
            source={require("@/assets/images/partial-react-logo.png")}
            style={styles.reactLogo}
          />
        }
      >
        <ThemedView style={styles.titleContainer}>
          <ThemedText type="title">Welcome!</ThemedText>
          <HelloWave />
        </ThemedView>

        <ThemedView style={styles.stepContainer}>
          <Pressable style={styles.bottomSheetButton} onPress={openBottomSheet}>
            <ThemedText style={styles.buttonText}>Open Bottom Sheet</ThemedText>
          </Pressable>
        </ThemedView>
      </ParallaxScrollView>

      <BottomSheet isVisible={isBottomSheetVisible} onClose={closeBottomSheet}>
        <KeyboardAvoidingView
          behavior={Platform.OS === "ios" ? "padding" : undefined}
          style={{ flex: 1 }}
        >
          <ScrollView contentContainerStyle={styles.bottomSheetContent}>
            <ThemedText type="subtitle">Bottom Sheet Test</ThemedText>
            <ThemedText>Your bottom sheet package is working!</ThemedText>

            <TextInput
              placeholder="Type something here..."
              value={inputValue}
              onChangeText={setInputValue}
              style={styles.textInput}
              placeholderTextColor="#999"
            />

            <Pressable style={styles.closeButton} onPress={closeBottomSheet}>
              <ThemedText style={styles.buttonText}>Close</ThemedText>
            </Pressable>
          </ScrollView>
        </KeyboardAvoidingView>
      </BottomSheet>
    </>
  );
}

const styles = StyleSheet.create({
  titleContainer: {
    flexDirection: "row",
    alignItems: "center",
    gap: 8,
  },
  stepContainer: {
    gap: 8,
    marginBottom: 8,
  },
  reactLogo: {
    height: 178,
    width: 290,
    bottom: 0,
    left: 0,
    position: "absolute",
  },
  bottomSheetButton: {
    backgroundColor: "#007AFF",
    paddingHorizontal: 20,
    paddingVertical: 12,
    borderRadius: 8,
    alignItems: "center",
    marginVertical: 20,
  },
  buttonText: {
    color: "#FFFFFF",
    fontWeight: "600",
  },
  bottomSheetContent: {
    padding: 20,
    alignItems: "center",
    gap: 16,
  },
  textInput: {
    width: "100%",
    paddingHorizontal: 16,
    paddingVertical: 12,
    borderRadius: 8,
    borderWidth: 1,
    borderColor: "#ccc",
    color: "#000",
  },
  closeButton: {
    backgroundColor: "#FF3B30",
    paddingHorizontal: 20,
    paddingVertical: 10,
    borderRadius: 6,
    alignItems: "center",
  },
});

License

MIT

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

About

A React Native bottom sheet component with gesture support, keyboard awareness, and smooth animations built with Reanimated & Gesture Handler.

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors