Magic Modallatest
Getting started

Your first modal

Show a component, close it from inside, and await typed data.

Define the data returned by the interaction. Pass that type to both show and useMagicModal.

import { Pressable, Text, View } from "react-native";
import { MagicModalHideReason, magicModal, useMagicModal } from "magic-modal";

type Confirmation = {
  confirmed: boolean;
};

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

  return (
    <View>
      <Text accessibilityRole="header">Delete this project?</Text>
      <Pressable
        accessibilityRole="button"
        onPress={() => hide({ confirmed: true })}
      >
        <Text>Delete</Text>
      </Pressable>
      <Pressable
        accessibilityRole="button"
        onPress={() => hide({ confirmed: false })}
      >
        <Text>Keep it</Text>
      </Pressable>
    </View>
  );
}

export async function confirmDeletion() {
  const result = await magicModal.show<Confirmation>(ConfirmationModal, {
    accessibilityLabel: "Confirm project deletion",
  });

  if (result.reason !== MagicModalHideReason.INTENTIONAL_HIDE) {
    return false;
  }

  return result.data.confirmed;
}

show() hands back a handle that is itself the promise, so awaiting it gives the result directly. The older const { promise } = magicModal.show(...) form still works; promise is an alias of the handle. Keep the handle when the calling code needs modalID, update, or hide while the modal stays open.

The promise also resolves when the backdrop, swipe gesture, system-dismiss action, or hideAll closes the modal. System dismissal includes Android back, web Escape, and the native accessibility escape action. Checking reason narrows the union, so data only exists after an intentional hide.