Custom Modal

An animated modal component with backdrop and smooth animations. Supports both centered modals and bottom sheet style modals with automatic keyboard handling.

Custom Modal Example

Installation

npx expo-app-ui add custom-modal

Basic Usage

Centered Modal

import CustomModal from "@/components/ui/custom-modal";
import { useState } from "react";
import { View, Text } from "react-native";
 
export default function MyComponent() {
  const [visible, setVisible] = useState(false);
 
  return (
    <>
      <Button onPress={() => setVisible(true)} title="Open Modal" />
      
      <CustomModal
        visible={visible}
        onClose={() => setVisible(false)}
      >
        <View style={{ padding: 20 }}>
          <Text>Modal Content</Text>
          <Button 
            title="Close" 
            onPress={() => setVisible(false)} 
          />
        </View>
      </CustomModal>
    </>
  );
}

Bottom Sheet Modal

Create a bottom sheet by setting justifyContent: "flex-end" in the style prop:

import CustomModal from "@/components/ui/custom-modal";
import CustomText from "@/components/ui/custom-text";
import Button from "@/components/ui/button";
import { useState } from "react";
import { StyleSheet, View } from "react-native";
 
export default function Index() {
  const [visible, setVisible] = useState(false);
 
  return (
    <View style={styles.container}>
      <CustomText fontSize={30} color="black">
        Home Screen
      </CustomText>
 
      <Button title="Open Bottom Sheet" onPress={() => setVisible(true)} />
 
      <CustomModal
        visible={visible}
        onClose={() => setVisible(false)}
        style={styles.bottomSheetContainer}
        modalStyle={styles.bottomSheet}
      >
        {/* Handle */}
        <View style={styles.handle} />
 
        <CustomText fontSize={22} style={styles.title}>
          Bottom Sheet Modal
        </CustomText>
 
        <CustomText style={styles.description}>
          This is a clean, modern bottom sheet modal. You can put forms,
          confirmations, or any content here.
        </CustomText>
 
        <View style={styles.actions}>
          <Button title="Cancel" onPress={() => setVisible(false)} />
          <Button title="Confirm" onPress={() => setVisible(false)} />
        </View>
      </CustomModal>
    </View>
  );
}
 
const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: "center",
    alignItems: "center",
  },
  bottomSheetContainer: {
    justifyContent: "flex-end",
  },
  bottomSheet: {
    paddingHorizontal: 20,
    paddingBottom: 24,
    paddingTop: 12,
  },
  handle: {
    width: 40,
    height: 5,
    borderRadius: 3,
    backgroundColor: "#D1D5DB",
    alignSelf: "center",
    marginBottom: 12,
  },
  title: {
    fontWeight: "600",
    textAlign: "center",
    marginBottom: 8,
  },
  description: {
    textAlign: "center",
    color: "#6B7280",
    marginBottom: 20,
  },
  actions: {
    gap: 12,
  },
});

Customization Examples

Custom Colors

<CustomModal
  visible={visible}
  onClose={() => setVisible(false)}
  backgroundColor="#000000"
  backdropColor="rgba(0, 0, 0, 0.8)"
  borderRadius={20}
>
  <Text style={{ color: "#FFFFFF" }}>Dark Modal</Text>
</CustomModal>

No Backdrop

<CustomModal
  visible={visible}
  onClose={() => setVisible(false)}
  noBackdrop
  style={{ justifyContent: "flex-end" }}
>
  <Text>Modal without backdrop</Text>
</CustomModal>

Prevent Backdrop Touch from Closing

By default, tapping the backdrop closes the modal. To prevent this:

<CustomModal
  visible={visible}
  onClose={() => setVisible(false)}
  preventBackgroundTouchEvent={true}
>
  <Text>Modal that doesn't close on backdrop touch</Text>
</CustomModal>

Props

PropTypeDefaultDescription
visiblebooleanRequiredControls modal visibility
onClose() => voidRequiredCallback when modal should close
preventBackgroundTouchEventbooleanfalseIf true, prevents backdrop touch from closing modal. By default, backdrop touch closes the modal.
childrenReact.ReactNodeRequiredModal content
styleStyleProp<ViewStyle>-Style for root container. Set justifyContent: "flex-end" for bottom sheet.
modalStyleStyleProp<ViewStyle>-Style for modal content box
noBackdropbooleanfalseHide backdrop completely
backgroundColorstring"#FFFFFF"Modal background color
backdropColorstring"rgba(0, 0, 0, 0.5)"Backdrop color
borderRadiusnumber15Border radius for modal

Features

  • Smooth Animations: Built with React Native Reanimated for 60fps animations
  • Bottom Sheet Support: Automatically detects bottom sheet style and adjusts behavior
  • Keyboard Handling: Automatically moves bottom sheet up when keyboard appears
  • Android Back Button: Handles Android hardware back button to close modal
  • Customizable: Full control over colors, styling, and behavior
  • Backdrop Touch: By default, tapping backdrop closes the modal (can be disabled)
  • No Dependencies: Uses black and white defaults - no theme dependencies required

Notes

  • The modal automatically detects bottom sheet style when justifyContent: "flex-end" is set in the style prop
  • Bottom sheets automatically handle keyboard appearance and adjust position accordingly
  • The modal uses React Native Reanimated for smooth animations
  • All styling uses black and white defaults - no theme dependencies required
Made by Krish Panchani X Thunder Develops • Built with ❤️ for the Expo React Native community
📦 View on npm