BottomSheet
Bottom sheet overlay powered by @gorhom/bottom-sheet.
Web preview — components render natively on iOS & Android
import { BottomSheet } from "@/components/ui/bottom-sheet";
import { useRef } from "react";
import GorhomBottomSheet from "@gorhom/bottom-sheet";
export function MyScreen() {
const sheetRef = useRef<GorhomBottomSheet>(null);
return (
<>
<Button onPress={() => sheetRef.current?.expand()}>
Open Sheet
</Button>
<BottomSheet ref={sheetRef} snapPoints={["25%", "50%"]}>
<Text>Sheet content goes here</Text>
</BottomSheet>
</>
);
}Installation#
npx @aniui/cli add bottom-sheetThis component requires additional dependencies:
npx expo install @gorhom/bottom-sheet react-native-gesture-handler react-native-reanimatedYou also need to wrap your app with GestureHandlerRootView from react-native-gesture-handler.
Usage#
app/index.tsx
import { BottomSheet } from "@/components/ui/bottom-sheet";
import { useRef } from "react";
import GorhomBottomSheet from "@gorhom/bottom-sheet";
export function MyScreen() {
const sheetRef = useRef<GorhomBottomSheet>(null);
return (
<>
<Button onPress={() => sheetRef.current?.expand()}>
Open Sheet
</Button>
<BottomSheet ref={sheetRef} snapPoints={["25%", "50%"]}>
<Text>Sheet content goes here</Text>
</BottomSheet>
</>
);
}Props#
PropTypeDefault
snapPoints(string | number)[]["25%", "50%"]classNamestring—childrenReact.ReactNoderequiredAlso accepts all @gorhom/bottom-sheet props. Use a ref to control the sheet imperatively with expand(), close(), and snapToIndex().
Accessibility#
- Powered by
@gorhom/bottom-sheetwith backdrop dismiss. - Sheet content is focusable and supports screen reader navigation.
Source#
components/ui/bottom-sheet.tsx
import React, { forwardRef, useCallback } from "react";
import { View } from "react-native";
import {
BottomSheetModal,
BottomSheetBackdrop,
BottomSheetView,
} from "@gorhom/bottom-sheet";
import { cn } from "@/lib/utils";
import { useThemeColors } from "@/components/ui/theme-provider";
import { useFoldFeatures } from "@/components/ui/fold";
export interface BottomSheetProps {
className?: string;
children: React.ReactNode;
snapPoints?: (string | number)[];
/**
* On an unfolded iPhone Duo / foldable, clamp the sheet to one pane instead
* of spanning the full screen width. Defaults to "left" (the OS treats the
* left pane as primary). Set to "full" to opt out and keep the pre-fold
* screen-wide behavior. Ignored on non-fold devices.
*/
foldPane?: "left" | "right" | "full";
}
export const BottomSheet = forwardRef<BottomSheetModal, BottomSheetProps>(
({ className, children, snapPoints = ["25%", "50%"], foldPane = "left", ...props }, ref) => {
const colors = useThemeColors();
const fold = useFoldFeatures();
const renderBackdrop = useCallback(
(backdropProps: React.ComponentProps<typeof BottomSheetBackdrop>) => (
<BottomSheetBackdrop {...backdropProps} disappearsOnIndex={-1} appearsOnIndex={0} opacity={0.5} />
),
[]
);
// Fold-aware width clamp: on an unfolded Duo, inherit one pane's width
// (foldX, which is window.width / 2). Pre-fold / non-Duo renders behave
// identically to today because fold.foldX is null.
const foldClamp =
fold.foldX && foldPane !== "full"
? { width: fold.foldX, left: foldPane === "right" ? fold.foldX : 0 }
: undefined;
return (
<BottomSheetModal
ref={ref}
snapPoints={snapPoints}
enablePanDownToClose
backdropComponent={renderBackdrop}
backgroundStyle={{ backgroundColor: colors.background }}
handleIndicatorStyle={{ backgroundColor: colors.mutedForeground }}
containerStyle={foldClamp}
{...props}
>
<BottomSheetView>
<View className={cn("p-4", className)}>{children}</View>
</BottomSheetView>
</BottomSheetModal>
);
}
);
BottomSheet.displayName = "BottomSheet";

