Skip to content

Store review

@symbiote-native/store-review wraps expo-store-review — prompting the platform’s native in-app review flow — so every SymbioteNative adapter can reach it, not just React. Like device and local auth, every export here is a one-shot async call with no per-instance state, so there is no hook/composable/service to wrap — the React, Vue, Angular, Svelte, and Solid entry points are plain re-exports of the same core.

OS platform Support
iOS ✅ live
Android ✅ live
Framework adapter Support
React ✅ live
Vue ✅ live
Angular ✅ live
Svelte ✅ live
Solid ✅ live
Terminal window
npm install @symbiote-native/store-review

Scaffolding or extending a SymbioteNative app? npx @symbiote-native/cli new --store-review (or add --store-review in an existing app) installs and wires this for you — see @symbiote-native/cli.

expo-store-review and expo-modules-core come along as regular dependencies, pinned to exact versions — never install either yourself, and never add the expo meta-package to your project (it bundles its own Metro/Babel pipeline, which conflicts with this project’s own).

expo-store-review needs no runtime permission on either platform.

All five adapters — React, Vue, Angular, Svelte, Solid — re-export the exact same functions; there is no per-adapter hook/composable/service to reach for, since nothing here holds live state or a subscription.

import { requestReview } from '@symbiote-native/store-review/react';
export default function RateAppButton() {
return (
<button
title="Rate this app"
onPress={() =>
requestReview({
iosAppStoreUrl: 'https://apps.apple.com/app/id123456789',
androidPlayStoreUrl: 'https://play.google.com/store/apps/details?id=com.example.app',
})
}
/>
);
}
Signature Description
isAvailableAsync(): Promise<boolean> Whether the platform has the capabilities to use requestReview()’s native flow. iOS resolves true unless the app is distributed through TestFlight; Android resolves true when the Play Store app is installed
requestReview(options?: IStoreReviewUrlOptions): Promise<void> Opens a native modal letting the user pick a star rating without leaving the app, when the native flow is available. Otherwise falls back to opening the store URL supplied via options through Linking
hasAction(options?: IStoreReviewUrlOptions): Promise<boolean> Whether requestReview() is capable of directing the user to some kind of store review flow — either the native flow is available, or a store URL was supplied via options
Field Type Description
iosAppStoreUrl string | undefined The App Store URL to open as a fallback on iOS when the native review flow is unavailable (e.g. TestFlight builds)
androidPlayStoreUrl string | undefined The Play Store URL to open as a fallback on Android when the native flow is unavailable (e.g. below Android 5.0)
  • requestReview() prefers the native in-app review flow (iOS SKStoreReviewController, Android’s Play Core in-app review API) and only falls back to opening the supplied store URL via Linking when the native flow is unavailable — matching upstream’s own fallback order.
  • A missing URL on the fallback path logs a warning, never throws — matching upstream’s own console.warn-and-continue behavior, just with different wording (pointing at the IStoreReviewUrlOptions argument instead of app.json).
  • A resolved requestReview() does not mean a prompt appeared — neither store reports that back, by design, so there is nothing to branch on. Don’t gate UI on it.
  • On Android the prompt only appears for a build installed from Google Play — internal test track, internal app sharing, or production. A sideloaded debug build completes the whole Play Core flow and shows nothing. iOS does show it in debug builds, so an app that works on iOS and looks dead on Android is behaving correctly.
  • Both stores enforce a quota, which is why upstream’s guidance is to trigger the prompt after real engagement rather than from a button.
  • The prompt never shows on TestFlight. Apple suppresses it there by design; test a release build outside TestFlight.
  • isAvailableAsync is false. Simulators and older OS versions report it. It is only a capability check: the store decides whether a prompt appears.
  • How often can I ask? iOS caps the system prompt at about three a year, so do not wire it to a button. Ask after a positive moment.
  • It resolves but nothing appears. The user may have In-App Ratings and Reviews turned off in the store settings.
  • Rejects with E_NO_SCENE. iOS needs a foreground-active window scene.
  • Android emulator. Reported not to work; test on a device with Play services.

Sources: expo/expo#38163, expo/expo#35497, expo/expo#28080, expo/expo#31091, Expo Store Review guide.

@symbiote-native/store-review ships zero React/Vue/Angular/Svelte/Solid logic in expo-store-review itself — its functions are hand-ported, verbatim apart from the expo-constants deviation above, into this package’s own core/, resolving the native module through expo-modules-core’s requireNativeModule rather than the expo meta-package this project never installs:

packages/store-review/src/
├── core/ # framework-agnostic: isAvailableAsync/requestReview/hasAction, plus the
│ # IStoreReviewUrlOptions type. native-module.ts resolves the native module via
│ # expo-modules-core's requireNativeModule
├── react/ # @symbiote-native/store-review/react — export * from '../core'
├── vue/ # @symbiote-native/store-review/vue — export * from '../core'
├── angular/ # @symbiote-native/store-review/angular — export * from '../core'
├── svelte/ # @symbiote-native/store-review/svelte — export * from '../core'
└── solid/ # @symbiote-native/store-review/solid — export * from '../core'

Same shape as device’s and local auth’s adapter entries: single-file re-exports with no lifecycle code at all, since every export here is a stateless one-shot async call — there is nothing for a hook, composable, or service to subscribe to or clean up. The native code itself is never vendored or copied — expo-modules-autolinking resolves it straight out of node_modules (see the native setup guide).