sp-react-native-in-app-updates: Play Core patches on Android, a store redirect on iOS
An in-app updater for the native version of your react-native app.
At a glance
- What is it?
- sp-react-native-in-app-updates is a React Native native module that checks the stores for a newer version of your app and prompts the user to update, using embedded in-app updates through Play Core on Android and the iTunes Search API on iOS. The two platforms behave very differently, and the Expo story needs a file replacement shim.
- Who is it for?
- Adopt sp-react-native-in-app-updates if you ship a React Native app on Android and want in-app update prompts that download the patch inside the app instead of sending the user to the store, and you are willing to accept that iOS behaviour is only a redirect. Do not adopt it if your app is Expo Go only, since third party native modules cannot load there, and do not assume the two platforms deliver the same feature.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 20 days ago.
- What is it written in?
- Mainly TypeScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
Two platforms, two completely different update mechanisms
sp-react-native-in-app-updates is a react-native native module that works on both iOS and Android, checks the stores for a new version of your app, and can prompt your user for an update.
What happens after the check is where the platforms diverge. On Android the module uses embedded in-app updates via Play Core, which means it checks for and downloads Google Play patches natively from within the app. On iOS it uses the iTunes Search API by default, which checks and navigates the user to the AppStore, with no extra native dependency.
That is a significant difference in what the user experiences. On Android the update can happen in place, and Play Core supports both a flexible update, where the user keeps using the app and applies the change on restart, and an immediate one, where the app is replaced at once. On iOS the user leaves your app, visits the store, and updates through Apple's own flow. There is no way to deliver a patch inside the app on iOS, so the module does not pretend otherwise.
The iOS path is configurable. react-native-siren is still available on iOS as an opt-in through the iosStrategy: 'siren' option, for anyone who prefers it, and it has to be installed separately if you use that option. The default remains the direct iTunes Search API call with no extra dependency.
The author's reason for building it is stated plainly in the Why section: to their knowledge no other react-native library used Play Core to offer embedded in-app-updates. That is a claim about the gap in the ecosystem rather than a claim about quality, and it explains the shape of the project.
Version comparison is pluggable, and the defaults are sane
The checkNeedsUpdate method returns a NeedsUpdateResponse, and the option table shows how much control you have over the comparison.
The only required option is curVersion, the semver of your current app version. Two optional options change how the comparison is done. toSemverConverter runs right after the store version is fetched, in case you want to change it before it is compared as a semver. customVersionComparator replaces the comparison entirely, and by default the library uses semver behind the scenes to compare the store version with curVersion.
That default matters in practice, because store versions are not always clean semver. A build number, a suffix or a four-part version will compare badly without normalisation, and the two hooks are there so you can handle the format your release process produces without patching the library.
The response has three fields. shouldUpdate is a boolean saying whether there is a newer version on the store. storeVersion is the latest app or Play Store version the library is aware of. And other is an object with other info returned from the store, and the README notes that it differs on Android and iOS, which is expected given that the two platforms answer with different APIs.
There is also a country option, iOS only, defaulting to undefined, which filters by country code while requesting an update. The value should be an ISO 3166-1 country code, and it exists because the same app version can be gated differently in different storefronts.
Starting an update takes a type that differs per platform
The startUpdate method shows a pop-up asking the user whether they want to update, giving them the option to download that update. What it does next is up to the options you pass.
The example in the README is the clearest statement of the platform split. The update type is set to IAUUpdateKind.FLEXIBLE on Android, and the comment says that on iOS the user will be prompted to go to your app store page. In the country specific example, Android uses IAUUpdateKind.IMMEDIATE while iOS gets a title, a message and the two button labels, Update and Cancel, along with the country code for the specific version to look up.
The two update types map to the two Play Core behaviours. A flexible update downloads in the background and applies the new version on the next restart, which is the polite option for an app in active use. An immediate one applies the update as soon as it is downloaded, which is what you want when the current version is broken and continuing to run it is worse than a restart.
The iOS branch of the options is where you control the copy, since the module is the one presenting the dialog. The title, message, buttonUpgradeText and buttonCancelText are yours to set, and the country field is optional.
Because Platform.select is used in the example, the same call works across both platforms with one set of options, and the library falls back to the iOS branch on iOS since there is no Play Core there.
Installation on iOS needs a plist entry and a rebuild
The npm install is one line, the command is npm install sp-react-native-in-app-updates --save, and the README gives it inline rather than as a block.
The iOS configuration is the part that bites. On React Native iOS you may need to add these lines to your Info.plist to be able to launch the store deep link:
<key>LSApplicationQueriesSchemes</key>
<array>
<string>itms-apps</string>
</array>That is the standard iOS mechanism for declaring which URL schemes your app is allowed to query, and without it the deep link into the App Store does not open. For Expo apps the same setting goes into your expo app.json or app.config.json:
"ios": {
"infoPlist": {
"LSApplicationQueriesSchemes": ["itms-apps"]
}
},After that you rebuild the native files with npx expo prebuild --clean and then eas build -p ios. The rebuild is not optional, because the Info.plist change is a native project change.
There is also a dependency to be aware of. The project uses react-native-device-info in the background, and the README says to install it to ensure everything works correctly. The package manifest pins it at 10.3.0 as a direct dependency, and there is a matching resolutions entry, so the version is controlled from the library side as well.
Expo needs the device info dependency replaced by hand
The Expo situation is the most awkward part of the documentation, and it exists for a specific reason.
First, the constraint. This library contains native code, so it works in any Expo app whose native project is actually built, meaning development builds, production and EAS builds, and expo prebuild workflows alike. The only thing it does not work in is Expo Go, whose prebuilt sandbox cannot load third party native modules. Since v2 the native module is resolved when the library is imported, following the standard TurboModule pattern, so importing it inside Expo Go fails at startup rather than on first use. That is a deliberate change, and it means the failure is loud and early instead of showing up later.
Second, the workaround. In order to make it work using Expo you need to replace the react-native-device-info dependency. You create a react-native-device-info.js file in the root with the following content, which requires the expo-constants dependency, and if you target iOS you also need to add a bundleIdentifier to the ios section of your Expo config:
import Constants from "expo-constants"
export const getBundleId = () => {
return Constants.expoConfig?.ios?.bundleIdentifier ?? '';
}
export const getVersion = () => {
return Constants.expoConfig?.version
}
export default {
getBundleId,
getVersion,
};Then you add an alias to the module-resolver configuration in babel.config.js:
plugins: [
[
'module-resolver',
{
root: ['.'],
alias: {
'react-native-device-info': './react-native-device-info.js'
}
}
]
]The reason for the shim is that the library needs the app version and the bundle identifier, and the version of react-native-device-info that works under Expo is not the one it depends on. The alias redirects the import to your local file at build time, and the file reads the values from the Expo config instead.
A small module with a conventional build setup
The package manifest describes a well-organised library, and the details of it tell you what to expect when you contribute or upgrade.
The package is sp-react-native-in-app-updates at version 2.0.0, MIT licensed, and authored by Ioannis Kokkinidis. The entry points are split the way a React Native library is normally split, with main pointing at lib/commonjs/index for CommonJS consumers, react-native and source both at src/index, and types at src/index.d.ts. The published files field includes src, lib, android and cpp, and excludes the example, build directories and test fixtures.
The build uses bob, invoked from the prepare script, and releases go through release-it with the conventional changelog plugin. The lint and typecheck scripts are separate from the test script, which is jest. There is a bootstrap script that runs the example app, and a pods script that runs pod-install in the example directory, which is the usual shortcut for iOS dependency installation in a React Native example.
The example directory is a full app rather than a snippet, with App.tsx, an app.json, babel.config.js, metro.config.js, jest.config.js, android and ios directories and its own package.json, so you can run the module end to end before wiring it into your own project.
On the maintenance side, the repository is not archived, the last push was on 2026-10-03, and it publishes no GitHub releases, so the CHANGELOG.md in the repository is the version history. The Android side supports both the old bridge architecture and the New Architecture with TurboModules, with no extra setup needed either way, which is the compatibility detail that matters most for a native module in 2026.
Editorial conclusion
Adopt sp-react-native-in-app-updates if you ship a React Native app on Android and want in-app update prompts that download the patch inside the app instead of sending the user to the store, and you are willing to accept that iOS behaviour is only a redirect. Do not adopt it if your app is Expo Go only, since third party native modules cannot load there, and do not assume the two platforms deliver the same feature. Before you install, add the LSApplicationQueriesSchemes entry to your Info.plist on iOS, and if you use Expo, follow the documented react-native-device-info replacement and the babel module-resolver alias, because the library reads the app version and bundle identifier through that dependency.
Frequently asked questions
How do I install sp-react-native-in-app-updates?
Run `npm install sp-react-native-in-app-updates --save`. On iOS you also need to add the LSApplicationQueriesSchemes entry with itms-apps to your Info.plist, and for Expo apps to the infoPlist section of your app.json or app.config.json, then rebuild the native files.
Does sp-react-native-in-app-updates work in Expo Go?
No. The library contains native code, so it works in Expo apps whose native project is built, including development builds, EAS builds and expo prebuild workflows, but not in Expo Go, whose prebuilt sandbox cannot load third party native modules. Since v2 importing it inside Expo Go fails at startup.
How does the update differ between Android and iOS?
On Android the module uses embedded in-app updates via Play Core, so it can check and download Google Play patches from inside the app, with flexible or immediate update types. On iOS it uses the iTunes Search API by default and navigates the user to the App Store, or you can opt into react-native-siren with the iosStrategy option.
Can I compare store versions my own way?
Yes. checkNeedsUpdate takes an optional toSemverConverter that runs right after the store version is fetched, and an optional customVersionComparator that replaces the default semver based comparison. The only required option is curVersion, the semver of your current app version.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/sudoplz-sp-react-native-in-app-updates)Was this useful?