RatingModalRate the app
How would you rate the app from zero to five?
Scores 0 through 3 open feedback. Scores 4 and 5 open the store prompt.latestA modal stack for React Native
Mount one MagicModalPortal at the app root. Call show() from any async flow and await the typed result.
rating-flow.tsx01const rating = await magicModal
.show<RatingAnswer>(RatingModal).promise;02await magicModal.show(
rating.data.score < 4
? FeedbackModal : StoreReviewModal
).promise;03await magicModal
.show(ThanksModal).promise;04return rating;RatingModalHow would you rate the app from zero to five?
Scores 0 through 3 open feedback. Scores 4 and 5 open the store prompt.The first Magic Modal flow asked for an app rating after a like. It could start from several screens and branch to feedback or the store. Both paths ended with a thank-you.
Every screen that can start the flow repeats the modal tree and its cleanup.
The flow lives outside the screens. Each caller awaits the same sequence.
The portal stays empty until show() runs.
Concurrent prompts keep their own place in the stack.
startRatingFlow()The shared flow owns the modal components and visibility state.
Start the rating flow and stack a notification over it. Close the top entry and the first promise is still waiting underneath.
Start the rating flow in the phone frame, or run update() in the web panel. This page mounts one MagicModalPortal for both.
rating-flow.tsconst result = await magicModal
.show(RatingPrompt)
.promiseThe stack is empty.Modal content mounts after show() runs.
No calls yet.Resolved values appear here.
Each action resolves or updates the current modal. The result decides what opens next.
delete-post.tsxconst result = await magicModal
.show<Confirmation>(() => (
<DeletePostModal />
))
.promise;
if (
result.reason === INTENTIONAL_HIDE &&
result.data.confirmed
) {
await deletePost(postID);
}This action cannot be undone.
Promise pendingModal calls can come from anywhere in the app. Each show() gets an ID and promise, so one flow cannot overwrite the modal that was already open.
post.tsxcomment.tsxproduct.tsxnotifications.tsrating(); branch(); thanks();magicModal.show() returnsAwait the close result, target this stack entry by ID, or replace its component while the same promise stays pending.
{
promise: Promise<HideReturn<T>>;
modalID: string;
update: (next: React.FC) => void;
}AVAILABLE IMMEDIATELYpromise01modalID02update03UPDATE REMOUNTSupdate() starts a fresh component mount. Local React state resets.
HideReturn<T> recordsA submitted answer, backdrop tap, swipe, Android back press, and hideAll() resolve differently. Only hide(data) returns a payload.
Answer from the sheet or dismiss it another way.
promise pendingconst result = await handle.promise;
// waiting for a closePress an action to resolve the promiseApps needed stacked modals, targeted updates, typed close reasons, native overlays, and support for newer gesture APIs.
show() moved visibility state out of the screen and returned a value when the modal closed.
Each entry gained its own ID and promise. This release also added hideAll(), swipe gestures, and the iOS full-window overlay.
update() can replace an open modal in place. Swipe dismissal now supports Gesture Handler 2 and 3.
The setup guide mounts the app-root portal and shows how to await a typed HideReturn<T>.