RatingModalRate the app
How would you rate the app from zero to five?
Drag down or tap outside to close. Scores 0-3 ask for feedback; scores 4-5 offer a store review.latestOne API for web, iOS, and Android
Mount one MagicModalPortal at the root of your app. Call show() from any async flow and await the typed result on web, iOS, and Android.
add react-native-magic-modalrating-flow.tsx01const rating = await magicModal
.show<RatingAnswer>(RatingModal);02if (rating.reason !==
MagicModalHideReason.INTENTIONAL_HIDE) {
return { interruptedBy: rating.reason };
}03await magicModal.show(
rating.data.score < 4
? FeedbackModal : StoreReviewModal
);04await magicModal
.show(ThanksModal);05return rating.data;RatingModalHow would you rate the app from zero to five?
Drag down or tap outside to close. Scores 0-3 ask for feedback; scores 4-5 offer a store review.The first Magic Modal flow collected feedback after someone liked a post. Several views could start it, await a score, and open the matching follow-up from one shared function.
Every caller that can start the flow repeats the modal tree and its cleanup.
The flow lives outside the UI. 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 or stack a notification above it. The upload example demonstrates the advanced update() API. Each demo mounts through the same MagicModalPortal.
rating-flow.tsconst result = await magicModal
.show(RatingPrompt)The stack is empty.Modal content mounts after show() runs.
No calls yet.Resolved values appear here.
The first two examples compose show(), useMagicModal().hide(), and typed results. The upload example uses update() for externally controlled progress.
Read the useMagicModal reference
delete-post.tsxtype Confirmation = { confirmed: boolean };
function DeletePostModal() {
const { hide } = useMagicModal<Confirmation>();
return (
<Button onPress={() => hide({ confirmed: true })}>
Delete post
</Button>
);
}
const result = await magicModal
.show<Confirmation>(() => (
<DeletePostModal />
));
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() returnsThe returned handle is itself the promise, so await it directly, or target this stack entry by ID. The advanced update() API replaces progress controlled outside the modal.
Promise<HideReturn<T>> & {
modalID: string;
update: (next: React.FC) => void;
hide: (data?: T) => void;
}AVAILABLE IMMEDIATELYawait handle01modalID02updateADVANCEDUSE SPARINGLYupdate() remounts the component and resets local React state. Keep ordinary UI changes inside the modal.
HideReturn<T> recordsA submitted answer, backdrop tap, swipe, Android back press, and hideAll() resolve differently. Only hide(data) returns a payload.
Answer here, tap the backdrop, swipe down, or use a close action above.
promise pendingconst result = await handle;
// waiting for a closePress one action to resolve the promiseThe setup guide mounts the app-root portal and shows how to await a typed HideReturn<T>.
add react-native-magic-modal