React Native 0.87, Instant Paywall A/B Testing, and Buying Mike Hardy a Beer

Issue #5317 August 20266 Minutes
0.rn-1.0-is-coming-meme.jpeg

Nikolai! The Mystery Box Gave Us a Type Error

React Native 0.87 landed last week.

So, like a Call of Duty Zombies mystery box, what did we get?

Background execution as a core primitive?

React Server Components on native?

Navigation in core?

No. We'll have none of that, no.

React Native 0.87 is STRICTLY maintenance.

Jorge Cohen (@JorgeWritesCode) from Meta announced the road to 1.0 at React Universe Conf in Wrocław last September, and this is what that road looks like from the inside: removals, deprecations, and a slow tightening of the API surface.

The Strict TypeScript API is now the default.

We covered it in #11 when it landed as an opt-in preview in 0.80, along with how to switch it on.

You don't need to switch it on now.

When you land on 0.87, you are in.

Here is what that actually means.

The types you have been using were hand-maintained inside the react-native repo, written by humans, drifting quietly from the code they described.

The Strict API replaces them with types generated directly from React Native's source, which is why doc comments now show up on hover for most symbols.

1.symbol-docs-after-0.87-ezgif.com-resize.jpg

This means if @types/react-native is still sitting in your package.json…

Get it the fuck out of there, man!

Metro also jumped from 0.84 to 0.87.

Source map generation is twice as fast, and memory use is halved, which means React Native DevTools opens faster and Metro stops fighting your simulator for RAM.

Now the interesting one.

0.87 adds experimental Swift Package Manager support on iOS, because…

CocoaPods went into maintenance mode in August 2024, and will shut down permanently on December 2, 2026; its package registry stops accepting new submissions permanently.

So Christian Falch (@chrfalch) from Expo built the alternative.

You run one command in your ios/ directory:

cd ios
npx react-native spm --deintegrate

Then you never run it again.

It strips CocoaPods and injects Swift package references into your existing .xcodeproj. You can also reverse it with npx react-native spm deinit.

Install or remove a native package, then build.

Xcode notices the change and re-runs auto-linking, so you no longer need to run pod install after every dependency change.

This means no CocoaPods.

No bundler.

No Ruby.

No 4 pm Friday build panic.

And, before we close this out, Android Reddit folks, put the pitchforks down… please.

Of course…

We were going to cover the Android-related updates…

0.87 is the first React Native release to support AGP 9, which is a major version carrying a number of breaking Gradle changes.

The recommended setup is two lines in android/gradle.properties:

android.builtInKotlin=false
android.newDsl=false

Yes.

React Native adds support for AGP 9 and then advises you to switch off the two headline features of AGP 9.

You can follow the progress on the ecosystem-wide adoption of AGP 9 in RFC #1006.

Now…

Just…

Thirteen releases to go.

👉 React Native 0.87


2.codemagic-advertisement.jpg

Win a Year of Unlimited CI/CD Build Minutes ($3,990 Value)

For years, CodePush was how React Native shipped OTA updates.

Then Microsoft retired App Centre in March 2025, and CodePush went read-only with it.

The Codemagic team ran a fork for 18 months. Then they rewrote the whole thing.

Codemagic Patch is what CodePush should have been:

➡️ CDN-first delivery ➡️ Binary diffs ➡️ Native fingerprinting ➡️ An actual dashboard (Did you say Dashboard!?) ➡️ Easy to self-host, source on GitHub

Patch comes from Codemagic, the CI/CD platform built for mobile teams.

And to celebrate the launch, they're giving away a year of unlimited builds on M2 Macs (their Annual plan, worth $3,990).

To enter, just join the Codemagic Discord. The team is in there and happy to walk you through it, answer questions, and help you figure out if it fits your setup.

👉 Join the Discord before September 14th.

Winner drawn September 15th.


3.trade-offer-meme.jpeg

The Last Library Jumps Off the Bridge

Mike Hardy (@mikehardy) has been maintaining React Native Firebase since back when linking a native module meant running react-native link and singing a prayer to Krishna.

Some of you care about Firebase.

Some of you don't.

But React Native Firebase is an anchor of this ecosystem either way, with 1.3 million weekly downloads.

And if you have worked with React Native Firebase in the past year or two, you know the pain point has been the New Architecture.

You might be thinking…

Are we still talking about the New Architecture?

That was supposed to be a solved problem by now, no?

Mostly it is.

Most libraries made the jump, and it stopped being something we think about day-to-day.

React Native Firebase was the exception.

Until now.

React Native Firebase v26 landed on July 29th, and every package with a native bridge is now a Codegen TurboModule.

The old bridge had no way to hand back a value synchronously, so calls that did nothing but write to memory still came back wrapped in a Promise.

TurboModules reach native code directly through JSI (the C++ layer that lets JavaScript call native functions without serialising anything), so a Promise is no longer necessary.

A bunch of React Native Firebase APIs are now synchronous and match the shapes firebase-js-sdk has used on the web for years.

// Before v26, the bridge wrapped all of these
await logEvent(analytics, 'screen_view', params);
await goOffline(database);
await trace(performance, 'checkout').start();

// v26: nothing to wait for
logEvent(analytics, 'screen_view', params);
goOffline(database);
trace(performance, 'checkout').start();

But Firebase v26 wasn’t just the new architecture; it also came with some long-awaited features.

Mike Hardy, what a lad.

Firestore (Google's serverless NoSQL document database that stores JSON-ish documents in collections and syncs them to clients in real time with offline support) has never been able to group.

You get count, sum, and average, but only across a whole collection, so something as ordinary as total population per state meant pulling every document into a Cloud Function and doing the maths yourself.

Pipelines chain stages instead, each one working on the output of the last:

const result = await db.pipeline()
  .collection('/cities')
  .aggregate({
    accumulators: [
      field('population').sum().as('total')
    ],
    groups: [field('location.state').as('state')]
  })
  .execute();

Same idea underneath as a SQL GROUP BY, and you get array unnesting, regex matching, and filtering on aggregation results in the same syntax. It is Enterprise edition only and still in preview, with no emulator, no real-time listeners, and no cursor pagination.

Then there is Firebase AI.

Which lets you call Gemini from your app without standing up a backend to broker it.

const ai = getAI(getApp());
const model = getGenerativeModel(ai, {
  model: 'gemini-3.1-flash-lite',
});

const result = await model.generateContentStream(
  'Summarise this thread in three lines'
);

It is also the one package exempt from the New Architecture requirement, because it is pure JavaScript with no native module underneath.

Dynamic Links are gone, which is overdue rather than surprising. Google shut the service off on August 25th last year, and every page.link URL has returned a 404 since.

Swift Package Manager support landed in v26.1, for exactly the reason we covered further up.

C’mon, somebody buy Mike Hardy a beer!

👉 Migrating to v26


4.i-write-my-own-for-loops.jpeg

It’s me, knock-knock… where's my money, man?

Paywalls.

Love them or hate them, if you want to make some cash, you need one.

And testing them used to be miserable.

Ship an app version, wait for review, measure, adjust the paywall, ship again, wait for review…

Again.

Or you skip the release and build a Frankenstein Remote Config in Firebase, deciding whether users see Jane-Paywall-Idea4a or Paywall2024-TestB, which control two hardcoded screens living in your bundle forever, and a spreadsheet somewhere that claims to know which one converts.

Why go through all that pain?

RevenueCat Paywalls are built in a web dashboard and rendered natively in your app.

5.revenuecat-paywall-editor.gif

You design the screen in a component-based editor, attach it to an offering, and change it anytime. Copy, layout, pricing, trial length, all of it updates remotely.

The integration is one component:

import RevenueCatUI from 'react-native-purchases-ui';

<RevenueCatUI.Paywall
  onDismiss={() => navigation.goBack()}
/>

Or one call, if you'd rather gate a feature than mount a screen:

const result = await RevenueCatUI
  .presentPaywallIfNeeded({
    requiredEntitlementIdentifier: 'pro',
  });

The part that replaces the Frankenstein Remote Config is Experiments.

Below is an example of RevenueCat A/B testing (i.e., Experiments).

6.revenuecat-experiments-ezgif.com-resize.jpg

You put two to four offerings behind a placement, RevenueCat splits your users, and the dashboard tracks the full subscription lifecycle for each variant.

Not just taps on the buy button, but trial starts, conversions, renewals, and refunds, which is the data that tells you a paywall actually makes more money.

Because your paywall is fetched, not bundled, the variants live in the dashboard too.

Test a price change, a different trial length, or an entirely different design, and the losing variant disappears without a deploy.

It works with Expo and renders native UI on both platforms.

Jane-Paywall-Idea4a can finally rest.

And if you have been sitting on an app idea, Shipaton is running right now.

Ship a new app before September 30th, put one purchase through their SDK, and you are in the running for over $700,000 in cash, a Times Square billboard, and a flight to New York.

Six weeks is enough.

Especially now that the paywall only takes an afternoon.

👉 Shipaton 2026

This section was sponsored by RevenueCat.

7.bye53-ezgif.com-optimize.gif
Gift box

Join 1,500+ React Native developers. Plus perks like free conference tickets, discounts, and other surprises.