Expo App UIExpo App UI
Components

Custom Modal

An animated modal component with backdrop, bottom sheet support, and smooth animations for Expo React Native. Learn how to use CustomModal with examples and API documentation.

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

On this page

Find this useful?

Buy me a coffee