/universal-bottom-sheet

✨ Bottom Sheet component powered by Gorhom Bottom Sheet + Vaul for native & web platforms.

Primary LanguageTypeScript

Universal Bottom Sheet

A bottom sheet component that combines Gorhom Bottom Sheet and Vaul for seamless and responsive experience across both mobile and web.

Bottom Sheet

Note: This is not a standalone package. It is a wrapper around Gorhom Bottom Sheet and Vaul for use across both mobile and web. You need to install Gorhom Bottom Sheet and Vaul separately to use this component.

Installation

I recommend following the installation guide for Gorhom's Bottom Sheet

npm install @gorhom/bottom-sheet@^4

or

yarn add @gorhom/bottom-sheet@^4

Peer Dependencies

Make sure to install the following peer dependencies

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

or

yarn add react-native-reanimated react-native-gesture-handler

or with expo

npx expo install react-native-reanimated react-native-gesture-handler

Web Support

Make sure to install the following dependencies

npm install vaul

or

yarn add vaul

Styling with NativeWind (Optional)

This example uses NativeWind for styling. You can use any other styling library of your choice.

Follow the installation guide here

Usage

An example usage of component is shown below:

The ui/bottom-sheet module exports the following components:

  • BottomSheetModal
  • BottomSheetModalProvider
  • BottomSheetView
  • BottomSheetTrigger
  • BottomSheetHandle
  • BottomSheetScrollView

The files can be found in the ui/bottom-sheet directory. Copy the files to your project and import them as shown below:

"use client";

import React, { useCallback, useMemo, useRef } from "react";
import { Text } from "ui/text";
import { View } from "ui/view";
import {
  BottomSheetModal,
  BottomSheetView,
  BottomSheetTrigger,
  BottomSheetHandle,
} from "ui/bottom-sheet";
import { Pressable, Platform } from "react-native";
import { useSharedValue } from "react-native-reanimated";

export function Home() {
  const [isOpen, setIsOpen] = React.useState(false);

  const animatedIndex = useSharedValue<number>(0);
  const animatedPosition = useSharedValue<number>(0);
  // ref
  const bottomSheetModalRef = useRef<BottomSheetModal>(null);

  // bottomSheetModalRef
  console.log({ bottomSheetModalRef });

  // variables
  const snapPoints = useMemo(() => [600, "20%", "50%", "70%", "95%"], []);

  // callbacks
  const handlePresentModalPress = useCallback(() => {
    // bottomSheetWebRef.current?.focus();

    if (isOpen) {
      bottomSheetModalRef.current?.dismiss();
      setIsOpen(false);
    } else {
      bottomSheetModalRef.current?.present();
      setIsOpen(true);
    }
  }, [isOpen]);

  const handleSheetChanges = useCallback((index: number) => {
    console.log("handleSheetChanges", index);
  }, []);

  return (
    <View className="flex flex-1 justify-center items-center">
      <View className="p-1 rounded-md">
        {Platform.OS !== "web" && ( // Use this condition if you want to control the modal from outside for only mobile
          <Pressable onPress={handlePresentModalPress}>
            <Text>Present Modal</Text>
          </Pressable>
        )}

        <BottomSheetModal
          ref={bottomSheetModalRef}
          index={1}
          // open={isOpen} Use this prop if you want to control the modal from outside for web
          snapPoints={snapPoints}
          onChange={handleSheetChanges}
          handleComponent={() => (
            <BottomSheetHandle
              className="bg-green-300 mt-2"
              animatedIndex={animatedIndex}
              animatedPosition={animatedPosition}
            />
          )}
        >
          {Platform.OS === "web" && (
            <>
              <BottomSheetTrigger>
                <Text>Present Modal</Text>
              </BottomSheetTrigger>
            </>
          )}
          <BottomSheetView className="flex-1 items-center">
            {Platform.OS === "web" && (
              <BottomSheetHandle
                className="bg-gray-300 mt-2"
                animatedIndex={animatedIndex}
                animatedPosition={animatedPosition}
              />
            )}
            <Text className="mt-10">Awesome 🎉</Text>
          </BottomSheetView>
        </BottomSheetModal>
      </View>
    </View>
  );
}

Author