Skip to content

Instantly share code, notes, and snippets.

@vhonLopez
Last active July 26, 2025 05:47
Show Gist options
  • Save vhonLopez/1101f1a7c39c96a2fd3297d4fdc53a20 to your computer and use it in GitHub Desktop.
Save vhonLopez/1101f1a7c39c96a2fd3297d4fdc53a20 to your computer and use it in GitHub Desktop.
Global Modal Context
import React, { createContext, useContext, useState, useCallback } from "react";
import { StyleSheet, Dimensions, Pressable } from "react-native";
import Animated, {
useSharedValue,
withTiming,
useAnimatedStyle,
Easing,
runOnJS,
} from "react-native-reanimated";
import { useSafeAreaInsets } from "react-native-safe-area-context";
const { height } = Dimensions.get("window");
const GlobalModalContext = createContext();
export const GlobalModalProvider = ({ children }) => {
const [content, setContent] = useState(null);
const [options, setOptions] = useState({});
const [visible, setVisible] = useState(false);
const insets = useSafeAreaInsets();
// Animations
const backdropOpacity = useSharedValue(0);
const translateY = useSharedValue(-height); // start off-screen
const showModal = useCallback((renderContent, modalOptions = {}) => {
setContent(() => renderContent);
setOptions(modalOptions);
setVisible(true);
// Animate in
backdropOpacity.value = withTiming(1, { duration: 200, easing: Easing.ease });
translateY.value = withTiming(0, { duration: 300, easing: Easing.out(Easing.exp) });
}, []);
const hideModal = useCallback(() => {
// Animate out
backdropOpacity.value = withTiming(0, { duration: 200, easing: Easing.ease });
translateY.value = withTiming(
-height,
{ duration: 300, easing: Easing.in(Easing.exp) },
(finished) => {
if (finished) {
runOnJS(setVisible)(false);
runOnJS(setContent)(null);
runOnJS(setOptions)({});
}
}
);
}, []);
// Backdrop fade style
const backdropStyle = useAnimatedStyle(() => ({
opacity: backdropOpacity.value,
}));
// Slide in modal container
const modalStyle = useAnimatedStyle(() => ({
transform: [{ translateY: translateY.value }],
}));
return (
<GlobalModalContext.Provider value={{ showModal, hideModal }}>
{children}
{visible && (
<Animated.View style={[styles.overlay, backdropStyle]}>
{/* Backdrop click to dismiss */}
<Pressable
style={StyleSheet.absoluteFill}
onPress={options.dismissOnBackdrop !== false ? hideModal : undefined}
/>
{/* Modal container (you control width/height via options.containerStyle) */}
<Animated.View
style={[
styles.modalContainer,
modalStyle,
options.containerStyle, // ✅ FULL CONTROL HERE
{ paddingBottom: insets.bottom, paddingTop: insets.top },
]}
>
{content ? content({ hideModal }) : null}
</Animated.View>
</Animated.View>
)}
</GlobalModalContext.Provider>
);
};
export const useGlobalModal = () => useContext(GlobalModalContext);
const styles = StyleSheet.create({
overlay: {
...StyleSheet.absoluteFillObject,
backgroundColor: "rgba(0,0,0,0.4)",
justifyContent: "center", // Centered
alignItems: "center",
},
modalContainer: {
backgroundColor: "#fff",
borderRadius: 16,
padding: 20,
shadowColor: "#000",
shadowOpacity: 0.2,
shadowRadius: 10,
elevation: 5,
},
});

how to use GlobalModalProvider.js

MAKE SURE TO ADD REANIMATED TO YOUR BABEL PLUGINS IN babel.config.js

INSTALL REANIMATED: npm install react-native-reanimated or yarn add react-native-reanimated

  {
    "plugins": [
      "react-native-reanimated/plugin"
    ]
  }

Usage/Examples

A brief description of what this project does and who it's for

import { useGlobalModal } from "../providers/GlobalModalProvider";
import { Text, Button, View, Dimensions } from "react-native";

const { width, height } = Dimensions.get("window");

export default function ExampleScreen() {
  const { showModal } = useGlobalModal();

  const openModal = () => {
    showModal(
      ({ hideModal }) => (
        <View>
          <Text style={{ fontSize: 18, marginBottom: 10 }}>Custom Modal</Text>
          <Button title="Close" onPress={hideModal} />
        </View>
      ),
      {
        dismissOnBackdrop: true,
        containerStyle: {
          width: width * 0.85,   // ✅ custom width
          height: height * 0.4,  // ✅ custom height
        },
      }
    );
  };

  return <Button title="Open Custom Size Modal" onPress={openModal} />;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment