Magic Modallatest
Getting started

Set up the portal

Mount one portal near the application root.

MagicModalPortal owns the stack rendered by magicModal.show() and the context used by useMagicModal(). Mount it once after the application content. Native and shared Expo roots place it inside GestureHandlerRootView. A web-only Next.js shell mounts the portal directly.

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

export default function App() {
  return (
    <GestureHandlerRootView style={{ flex: 1 }}>
      <YourApp />
      <MagicModalPortal />
    </GestureHandlerRootView>
  );
}

The gesture root is required on native even if the first modal does not use swipe-to-dismiss. The portal owns the gesture surface, and native gesture handling requires a root view.

Expo Router

The root layout is usually the clearest place for both components:

app/_layout.tsx
import { Stack } from "expo-router";
import { MagicModalPortal } from "magic-modal";
import { GestureHandlerRootView } from "react-native-gesture-handler";

export default function RootLayout() {
  return (
    <GestureHandlerRootView style={{ flex: 1 }}>
      <Stack />
      <MagicModalPortal />
    </GestureHandlerRootView>
  );
}

Keep the portal outside individual screens. It stays mounted across navigation. A flow can start on one screen and resolve after the active screen changes.

Pick your runtime

The portal stays the same. Its root file and bundler setup depend on the runtime.

Continue by showing your first modal.

On this page