Documentation

Expo OTA updates with Pakta

Configure the root layout, native channels and a Release build, then publish an update.

Updated 2026-09-15

On this page

Choose your Expo update workflow

This guide uses Pakta's rn-update client and pakta CLI for Expo OTA updates. If you are comparing expo-updates and EAS Update, first read Expo Updates vs Pakta. Moving from another OTA client requires a new native build with one system responsible for loading the bundle.

Prepare

Use a native Expo app, not Expo Go. This project's iOS automatic integration requires Expo 50 or later. Keep the existing compatible Expo/RN combination.

Install and select an application from the project root:

bash
npx expo install rn-update
npm install -g rn-update-cli
pakta login
pakta selectApp --platform android

Create an application in the console if needed, or run pakta createApp --platform android --name ExpoDemo. Select the matching ios application separately.

Wrap the root layout

For Expo Router, add the Provider to the existing app/_layout.tsx. This minimal Stack example must be merged with your theme, fonts and authentication setup:

app/_layout.tsx
import { Stack } from 'expo-router';
import { Platform } from 'react-native';
import { Pakta, UpdateProvider } from 'rn-update';
import updateConfig from '../update.json';

const configs = updateConfig as Record<string, { appKey: string }>;
const appKey = configs[Platform.OS]?.appKey;
if (!appKey) throw new Error(`Missing appKey for ${Platform.OS}`);

const client = new Pakta({ appKey, updateStrategy: 'silentAndLater' });

export default function RootLayout() {
  return (
    <UpdateProvider client={client}>
      <Stack />
    </UpdateProvider>
  );
}

For src/app/_layout.tsx, import ../../update.json. Without Expo Router, follow the standard root component setup.

Optional: set a channel

Skip this section for default. Create the custom channel in the console first. rn-update includes the Config Plugin; do not create a local plugin.

Add this entry to the existing expo.plugins array in app.json:

app.json · merge into existing configuration
{
  "expo": {
    "plugins": [
      ["rn-update", { "channel": "staging" }]
    ]
  }
}

The channel is written to Android manifest metadata and iOS Info.plist. Use separate build configurations when Android and iOS need different channels. No plugin entry is needed for default.

Build and inspect

Run npx expo prebuild and verify channel in the generated Android manifest or iOS plist. EAS Build runs the same plugin.

Android
npx expo run:android --variant release
iOS · macOS
npx expo run:ios --configuration Release

An existing EAS production workflow also works. Verify hot updates in the Release app.

Upload and publish

  1. Retain the exact APK / IPA installed on the device and register it with uploadApk / uploadIpa; see native registration.

  2. Change a screen label and build an update:

bash
pakta bundle --platform android --expo --output .pakta/output/android.ppk --no-interactive
  1. Follow first release, using the channel and version parsed from the native installer.

  2. Check in the installed Release app, wait for download, fully close and reopen, then verify the changed label.

For iOS use --platform ios and a separate output path. Native plugins, channels and native SDK changes require a new installer, not just a .ppk.