MCPcopy Index your code
hub / github.com/GSTJ/react-native-magic-modal

github.com/GSTJ/react-native-magic-modal @7.0.1

Chat with this repo
repository ↗ · DeepWiki ↗ · release 7.0.1 ↗ · + Follow
29 symbols 115 edges 34 files 0 documented · 0%
What it actually does AI analysis from the code graph — generated when you open this
loading…
README

React Native Magic Modal 🦄

React Native Magic Modal Banner

Docs | GitHub | FAQ | Article

[!NOTE]
Simplify your modal management in React Native with the React Native Magic Modal library. Effortlessly control modals, streamline complex flows, and create a seamless user experience.

[!TIP]
Our new version just got released with full support for multiple modals! See the breaking changes.

Features

Highlights

React Native Magic Modal offers a superior experience compared to traditional modal implementations:

Table of Contents

Installation

Add peer dependencies to your project, if you haven't already:

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

Install the package:

yarn add react-native-magic-modal

Quickstart

Insert a MagicModalPortal at the top of your application structure, and a GestureHandlerRootView if you haven't already:

import { MagicModalPortal } from "react-native-magic-modal";
import { GestureHandlerRootView } from "react-native-gesture-handler";

export default function App() {
  return (
    <GestureHandlerRootView>
      <YourAppContent />
      <MagicModalPortal /> {/** After your app component hierarchy */}
    </GestureHandlerRootView>
  );
}

Tip: the root _layout.tsx is usually the best place to put it in a project using expo-router.

Examples

Showcasing modal management on iOS and Android platforms:

iOS Android

Usage

Here's the preferred usage pattern for the library:

import React from "react";
import { View, Text, TouchableOpacity } from "react-native";
import {
  MagicModalPortal,
  magicModal,
  useMagicModal,
  MagicModalHideReason
} from "react-native-magic-modal";
import { GestureHandlerRootView } from "react-native-gesture-handler";

type ConfirmationModalReturn = {
  success: boolean;
};

const ConfirmationModal = () => {
  const { hide } = useMagicModal<ConfirmationModalReturn>();

  return (
    <View>
      <TouchableOpacity onPress={() => hide({ success: true })}>
        <Text>Click here to confirm</Text>
      </TouchableOpacity>
    </View>
  );
};

const ResponseModal = ({ text }) => {
  const { hide } = useMagicModal();

  return (
    <View>
      <Text>{text}</Text>
      <TouchableOpacity onPress={() => hide()}>
        <Text>Close</Text>
      </TouchableOpacity>
    </View>
  );
};

const handleConfirmationFlow = async () => {
  // You can call `show` with or without props, depending on the requirements of the modal.
  const result = await magicModal.show<ConfirmationModalReturn>(() => <ConfirmationModal />).promise;

  // Hide could potentially be a backdrop press, a back button press, or a swipe gesture.
  if (result.reason !== MagicModalHideReason.INTENTIONAL_HIDE) {
    // User cancelled the flow
    return;
  }

  if (result.data.success) {
    return magicModal.show(() => <ResponseModal text="Success!" />).promise;
  }

  return magicModal.show(() => <ResponseModal text="Failure :(" />).promise;
};

export const MainScreen = () => {
  return (
    <GestureHandlerRootView>
      <TouchableOpacity onPress={handleConfirmationFlow}>
        <Text>Start the modal flow!</Text>
      </TouchableOpacity>
      <MagicModalPortal />
    </GestureHandlerRootView>
  );
};

You can also hide modals imperatively outside of the modal context. For that, we provide the global hide method, that requires a modal id:

import { magicModal } from "react-native-magic-modal";

const QuickModal = ({ text }) => {
  return (
    <View>
      <Text>Hey! I'm going to be closed imperatively</Text>
    </View>
  );
};

const handleQuickModal = async () => {
  const { modalId } = magicModal.show(QuickModal);

  // Wait for 2 seconds before closing the modal
  await new Promise((resolve) => setTimeout(resolve, 2000));

  // Note that it's usually preferrable to use the `hide` method from the modal context
  // You can even put it inside useEffects to handle auto-dismissal for you.
  magicModal.hide({ modalId });
};

export const MainScreen = () => {
  return (
    <GestureHandlerRootView>
      <TouchableOpacity onPress={handleQuickModal}>
        <Text>Show a quick modal</Text>
      </TouchableOpacity>
      <MagicModalPortal />
    </GestureHandlerRootView>
  );
};

Refer to the kitchen-sink example for detailed usage scenarios.

Documentation

Access the complete documentation here.

FAQ

Q: Can I have two modals showing up at the same time?

A: Yes. With v4+, you can now have multiple modals showing up at the same time.


Q: Can I use Scrollables inside the modal?

A: Yes, but Scrollables can't be used with swipe gestures enabled, as they conflict. Pass in swipeDirection: undefined on the magicModal.show function to disable gestures on them.

If your use-case is a scrollable bottom-sheet, I recommend going with Gorhom's react-native-bottom-sheet for this use-case temporarily.


Q: Modals are appearing on top of native modal screens, such as the image picker. How can I fix this?

A: This behavior can be disabled by calling magicModal.disableFullWindowOverlay() before showing the modal. This will prevent the modal from appearing on top of native modal screens.

You can also call magicModal.enableFullWindowOverlay() to re-enable it.

Contributors

Special thanks to everyone who contributed to making React Native Magic Modal a robust and user-friendly library. See the full list.

See the contributing guide to learn how to contribute to the repository.

License

React Native Magic Modal is licensed under the MIT License.

Extension points exported contracts — how you extend this code

ModalStackItem (Interface)
(no doc)
packages/modal/src/components/MagicModalPortal/MagicModalPortal.tsx
KeyboardAvoidingModalProps (Interface)
(no doc)
examples/kitchen-sink/src/components/KeyboardAvoidingModal.tsx
IModal (Interface)
(no doc)
packages/modal/src/utils/magicModalHandler.ts
ModalResponse (Interface)
(no doc)
examples/kitchen-sink/src/app/index.tsx

Core symbols most depended-on inside this repo

getMagicModal
called by 4
packages/modal/src/utils/magicModalHandler.ts
hide
called by 4
packages/modal/src/utils/magicModalHandler.ts
useMagicModal
called by 3
packages/modal/src/components/MagicModalProvider.tsx
useInternalMagicModal
called by 2
packages/modal/src/components/MagicModalProvider.tsx
showKeyboardAvoidingModal
called by 2
examples/kitchen-sink/src/components/KeyboardAvoidingModal.tsx
generatePseudoRandomID
called by 1
packages/modal/src/components/MagicModalPortal/MagicModalPortal.tsx
hideCallback
called by 0
packages/modal/src/components/MagicModalPortal/MagicModalPortal.tsx
show
called by 0
packages/modal/src/utils/magicModalHandler.ts

Shape

Function 24
Interface 4
Enum 1

Languages

TypeScript100%

Modules by API surface

packages/modal/src/utils/magicModalHandler.ts7 symbols
examples/kitchen-sink/src/app/index.tsx6 symbols
packages/modal/src/components/MagicModalPortal/MagicModalPortal.tsx3 symbols
examples/kitchen-sink/src/components/KeyboardAvoidingModal.tsx3 symbols
packages/modal/src/components/MagicModalProvider.tsx2 symbols
examples/kitchen-sink/src/components/Toast.tsx2 symbols
examples/kitchen-sink/src/components/ScrollableModal.tsx2 symbols
packages/modal/src/constants/types.ts1 symbols
examples/kitchen-sink/src/components/ExampleModal.tsx1 symbols
examples/kitchen-sink/src/app/_layout.tsx1 symbols
examples/kitchen-sink/plugins/withExpoModulesCoreSwiftStrictConcurrency.js1 symbols

For agents

$ claude mcp add react-native-magic-modal \
  -- python -m otcore.mcp_server <graph>

⬇ download graph artifact