Vercel Reinvents React Native, Sub-Second OTA Updates, and an X Button Finally Doing Its Job

Issue #4914 July 20265 Minutes
0.is-this-react-native.jpg

Did Vercel Just Reinvent React Native?

If we all released Software at the pace of Vercel we would be acquired, sunset, and open-sourced by Friday.

Anyway, they've shipped again.

It’s called native from Vercel Labs.

Not react “native”.

Not “native” base.

Just native.

Mononymous, like Cher.

Native SDK is a toolkit for building native desktop apps.

1.vercel-native-app-example-ezgif.com-resize.jpg

You write views in declarative markup and logic in TypeScript or Zig, and the SDK's own engine draws every pixel into real OS windows.

No browser, no WebView, and no JavaScript runtime anywhere in the binary.

Read that again, because you write TypeScript and it ships without a JavaScript engine.

Hold that thought.

First, here's an entire working UI:

<column gap="12" padding="16">
  <row gap="8" cross="center">
    <text grow="1">Counter</text>
    <button size="sm" variant="ghost" on-press="reset">
      Reset
    </button>
  </row>
  <row gap="8" main="center" cross="center" grow="1">
    <button variant="secondary" on-press="decrement">-</button>
    <text>{count}</text>
    <button variant="primary" on-press="increment">+</button>
  </row>
  <status-bar>total: {count}</status-bar>
</column>

Here's where it stops looking like React Native.

No useState. No useEffect. Nobody in a GitHub thread is asking why it fired twice.

The markup is a dumb template: it can read values {count} and shout messages on-press="increment", and that's all it's allowed to do.

Every message from every corner of the UI funnels into one function, update, which takes the current state and the message and returns the next state. The whole window re-renders from that.

If you've used useReducer or Redux, it's that, as the entire application.

// src/core.ts, compiled to NATIVE CODE at build time
export type Msg =
  | { readonly kind: "increment" }
  | { readonly kind: "decrement" }
  | { readonly kind: "reset" };

export function update(model: Model, msg: Msg): Model {
  switch (msg.kind) {
    case "increment":
      return { ...model, count: model.count + 1 };
    case "decrement":
      return { ...model, count: model.count - 1 };
    case "reset":
      return { ...model, count: 0 };
  }
}

So where did the JavaScript engine go?

At build time, a TypeScript-to-native transpiler compiles that core straight into the executable.

Node is required to build, then vanishes from the binary like Keyser Söze.

It was never really there.

What's left is a small example calculator that is 3.6 MB and opens in roughly 100 milliseconds, according to their own macOS measurements.

You could email it. The calculator. Through Gmail.

About Zig: it's the low-level systems language the entire toolkit is built in, think C with better error messages. You never have to touch it, but if you want to, native init my_app --template zig-core scaffolds the identical counter with its core written in Zig.

It's not a blank-slate toolkit either.

A catalogue of 45-plus built-in components such as buttons, tabs, dialogues, charts, tables, virtual lists, and a markdown renderer ships with considered typography and spacing, so the scaffolded app looks intentional the first time the window opens.

2.vercel-native-components-ezgif.com-resize.jpg

Every app embeds an automation server.

The agent that wrote your app can launch it, read the live widget tree the way it would read a DOM, click the button it just invented, assert on the real state, and screenshot the window to confirm the app isn't lying about any of it.

Then, buried in the Web Content docs, the funniest architecture decision of 2026.

Native SDK includes a Bridge. Yes… a Bridge.

JavaScript in the WebView calls native handlers by sending asynchronous JSON messages, capped at 16 KiB. JavaScript on one side. Native on the other. Serialised JSON in the middle.

This community spent half a decade escaping that exact architecture, and Vercel has rebuilt it from memory like a traumatised architect.

Let's take full inventory, actually.

Declarative views? Check.

Logic driving a native renderer? Check.

An asynchronous serialised JSON bridge to JavaScript? Check.

iOS and Android targets? Experimental, but check.

Congratulations to Vercel on inventing React Native 0.59, an architecture so historic that this community holds conferences about having survived it.

👉 Native SDK


3.maestro-mcp-sponsor-ezgif.com-resize.jpg

Let Your Agent Use the App It Just Built

Your coding agent writes a login screen, tells you it's done, and moves on.

It never actually opened the app.

The Maestro MCP fixes that.

It hands your agent an iOS simulator, Android emulator, or physical Android device to drive.

Your agent reads the screen hierarchy, taps through the flow, grabs screenshots, and confirms the feature it just wrote actually works. Then it saves a repeatable end-to-end test, so the same flow doesn't quietly break next week.

The Maestro Viewer embeds the live device right inside your coding agent, showing every command as it runs.

It all ships inside the Maestro CLI.

Point Claude Code, Cursor, Codex, Copilot, or Gemini at it. For Claude Code, it's one line:

mcp add maestro -- maestro mcp

Your agent stops saying "this should work" and starts checking.

👉 Maestro MCP


4.lgtm-agent-meme.jpeg

Your OTA Update Is Out for Delivery

Zepto (@ZeptoNow) is the Indian quick-commerce company famous for delivering groceries in ten minutes.

At some point, an engineering team in Bengaluru looked at OTA platforms re-shipping the entire JavaScript bundle for a one-line fix, and treated it like a delivery arriving in eleven minutes.

A production Hermes bundle runs about 20 MB, conservatively.

Push a one-character fix to a million installs, and your CDN moves 20 terabytes: roughly $1,700 of data at CloudFront list price, spent shipping 19.999 MB of JavaScript to phones that already had it, over metered mobile data.

OTA at this scale earned its own talk at React Universe Conf 2025.

So Zepto built Delta.

Delta ships the diff instead.

Binary delta patching (computing the byte-level difference between two files) means the device downloads only the bytes that changed between the bundle it has and the bundle it needs.

The delta-cli computes the patch between releases and uploads it to your own S3 bucket, while delta-server (a self-hosted stack of Lambdas behind CloudFront) decides which patch each device receives, keyed on the appId, jsVersion, and bundleVersion from a manifest.json baked into your app at build time.

On the device, the SDK checks for an update, downloads the patch, verifies it, applies it with bspatch (the applying half of bsdiff, a binary diffing tool) to reconstruct the full bundle, and swaps it in on the next launch.

The swap itself is one override:

// MainApplication.kt
class MainApplication : Application(),
  ReactApplication, IDeltaDelegate {

  override val reactNativeHost: ReactNativeHost =
    object : DefaultReactNativeHost(this) {
      // Delta decides which bundle boots
      override fun getJSBundleFile(): String {
        return Delta.getJSBundlePath()
      }
    }

  override fun onCreate() {
    super.onCreate()
    Delta.initialize(this)
  }
}

According to their own production numbers, Zepto saw 80% of users on the latest version within 24 hours, across 15 million update requests per day, with a P90 (P90 is the value that 90% of all measurements fall at or below) patch download time of 305 milliseconds.

305 milliseconds.

Quicker than your ex moving on to someone who actually has their shit together.

Somewhere in Bengaluru, a rider is doing 60 through traffic with a bag of onions, and he is now only the second-fastest thing at the company.

👉 react-native-delta


5.just-use-expo-mate-ezgif.com-resize.jpg

React Native Finally Learns to Let Go

The user starts uploading a 40 MB video on mobile data, changes their mind, and taps the X.

That X is your job.

And fetch has no cancel method.

No .stop(), no .nevermind().

That upload finishes in the background, burns through 40 MB of the user's data plan, and your cancel button is just a placeholder icon.

An AbortController is the only way to kill an in-flight fetch.

You pass its signal into the request, and the X calls abort(). The promise is rejected, the connection dies, and the upload stops transferring bytes.

Your app moves on with its life.

It's the web platform's institutionalised form of ghosting.

AbortController has worked in React Native for years, but it was never React Native's code.

Hermes doesn't ship browser APIs, so at startup, React Native bolts the web globals on in a file called setUpXHR.js, and the AbortController it uses is an npm package abort-controller by Toru Nagashima (@mysticatea).

A package last published 7 years ago.

Which means it predates half the modern API.

AbortSignal.timeout(), AbortSignal.any(), the static AbortSignal.abort(reason), and signal.throwIfAborted() simply did not exist in React Native.

Spotted doing the rounds on X this week, because a core web primitive quietly frozen in 2019 is exactly the kind of thing this ecosystem finds under the sofa.

Now, a commit from @retyui has landed on main.

The unmaintained package (and its event-target-shim sub-dependency) are gone.

In its place, React Native forked the source in-tree, wired it up to React Native's own EventTarget, and added all four missing methods. The Hermes team did the same thing to the promise package years ago, so there's precedent for adopting abandoned dependencies and raising them as your own.

The payoff is that the patterns you already use on the web now JustWork™:

const controller = new AbortController();

fetch(url, {
  // Cancel manually OR after 5s, whichever first
  signal: AbortSignal.any([
    controller.signal,
    AbortSignal.timeout(5000),
  ]),
});

controller.abort(); // the X, doing its one job

AbortSignal.timeout() gives every request a deadline without a hand-rolled setTimeout, and AbortSignal.any() merges signals so one fetch can be cancelled by whichever fires first.

It landed on main against 0.87, so it will be available in the next React Native release.

And somewhere out there, an X is about to do something for the first time in its life.

👉 Abort Controller

6.bye49-ezgif.com-optimize.gif
Gift box

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