SDK for React Native & Expo

Make every user run the version you shipped

Teardown gates stale app versions from the dashboard. Block a broken build, require the latest, or nudge a soft update, and the SDK enforces it on the next check. No new native release to change the rule.

$ bun add @teardown/force-updatesWorks with Expo and bare RN. Free to start.
9:41v1.2.0
Update required

A new version is available

This version is no longer supported. Update to keep using the app.

What's new

Fixes a checkout crash and a sync bug on cold start.

Update now
App.tsx
tsx
import { useForceUpdate } from '@teardown/force-updates'function App() {  const { isUpdateRequired } = useForceUpdate()  if (isUpdateRequired) {    return <UpdateRequiredScreen />  }  return <RootNavigator />}

Already have an app? Read the 5-minute setup.

You shipped the fix. Your users are still on the bug.

App store updates are opt-in. A slice of your users runs builds you released months ago, hitting code paths you've already deleted and filing tickets for bugs that no longer exist.

Old clients hammer endpoints you want to retire

You can't change an API contract while months-old builds still call it. With opt-in updates, that long tail never fully goes away.

Critical fixes reach a fraction of users

A crash fix sitting in the store does nothing for the people who won't tap Update for weeks. The users who most need the patch are the slowest to get it.

Hand-rolled version checks rot

A DIY “please update” gate drifts out of sync with what's actually live, ships hardcoded version numbers, and becomes one more thing to maintain.

Three steps to a gated release

Integrate once. From then on, you control which versions can run from the dashboard.

1

Install the SDK

Add the package and the device adapter for your stack. Expo and bare React Native are both first-class.

terminal
bash
bun add @teardown/force-updatesnpx expo install expo-device expo-application
2

Wrap your app

Drop the provider at your root. The SDK identifies the device, checks the version on launch, and re-checks every time the app returns to the foreground.

app/_layout.tsx
tsx
import { TeardownProvider } from '@teardown/force-updates'import { teardown } from './lib/teardown'export default function RootLayout() {  return (    <TeardownProvider core={teardown}>      <RootNavigator />    </TeardownProvider>  )}
3

Set the policy in the dashboard

Mark any version required, recommended, or disabled. No code change, no resubmission. The SDK picks it up on the next check.

Versionsproduction
1.4.0build 142Up to date
1.3.2build 138Recommended
1.2.0build 120Required

Five states. One source of truth.

Every version reports exactly one status. Read it once with useForceUpdate() and decide how hard to push.

  1. up_to_dateUp to date

    The build is current. Your app runs normally.

  2. update_availableUpdate available

    A newer version exists. Surface it on your own terms, or not at all.

  3. update_recommendedRecommended

    Show a dismissible banner that strongly nudges users to update.

  4. update_requiredRequired

    Block the app behind a full-screen takeover until the user updates.

  5. disabledDisabled

    Kill a build entirely. The fastest way to pull a bad release.

Re-checks on foreground

Every time the app resumes, the SDK re-checks the policy on a configurable interval.

Cached for offline

The last known status is persisted, so the gate still works without a connection.

Release notes built in

Notes you write in the dashboard flow straight to useForceUpdate().releaseNotes.

Drop it into the stack you already have

One hook drives the whole flow. Swap adapters to match how your app already does storage, device info, and push, without touching the rest of your code.

App.tsx
tsx
function App() {  const {    isUpdateRequired,    isUpdateRecommended,    releaseNotes,  } = useForceUpdate()  if (isUpdateRequired) return <ForceUpdate notes={releaseNotes} />  if (isUpdateRecommended) return <SoftBanner notes={releaseNotes} />  return <RootNavigator />}
Device info
ExpoBare RN
Storage
MMKVAsyncStorage
Push
ExpoFirebaseWix

Identity

Attach a user to a device with identify() and follow sessions across launches.

Event tracking

Send analytics events through the same client, no second SDK to wire up.

TypeScript-first

Fully typed states and hooks, with discriminated unions for every status.

Questions you're probably asking

Isn't this just OTA, like Expo Updates or CodePush?
OTA ships JavaScript bundles. It can't move a user off an old native binary, and it won't force anyone to do anything. Teardown gates by store version and can hard-block. Run it alongside your OTA pipeline, they solve different problems.
Can't I just check the version myself?
You can. Then you own the version service, the foreground polling, the offline cache, and the release-notes plumbing forever, and every release means editing a hardcoded number. That maintenance is exactly what the SDK is.
Will the app stores allow a forced update?
Yes. Teardown sends users to your store listing to update through the normal flow, it never side-loads anything. You decide when a version is too old to keep running.

Ship your next release like the old ones don't exist

Install the SDK, set a policy, and stop designing around users who never update.

Free tier. No credit card required.