# OTA staging and live preview

Source: https://rerune.io/docs/staging

Preview saved draft translations in a test app before publishing. Enable project staging, opt in your SDK, and understand refresh and cache behavior.

## Two switches control draft delivery

Source: https://rerune.io/docs/staging#how-it-works

OTA staging lets a test app read saved, enabled drafts, including unfinished translations and never-published keys. Enable OTA staging for the project in the dashboard and opt the client into staging mode. Both switches must be on to receive drafts. Unsaved editor changes are not included.

| Project OTA staging | Client staging mode | Content on the next successful sync |
| --- | --- | --- |
| Off | Off | Published translations |
| Off | On | Published translations |
| On | Off | Published translations |
| On | On | Saved, enabled drafts |

> **A project-wide preview:** Staging uses the project's existing read-only OTA Publish ID. It is not a separate branch or a per-reviewer draft. While project staging is enabled, a client with that ID can request drafts by opting in. Keep production clients on their default published mode.

- [OTA updates overview](https://rerune.io/ota-localization-updates)

## Enable preview in the dashboard

Source: https://rerune.io/docs/staging#enable-preview

1. Open your project, select the Keys tab, and turn on OTA staging. Changing this project-wide setting requires project settings permission.
2. Edit and save an enabled translation key. You can preview it before marking the translation finished or publishing the key.
3. Start a test app with a staging-capable SDK and the same project's OTA Publish ID. Enable staging at setup or switch the running client into staging mode.
4. Run an update check and inspect the text in the app. Check a normal published client alongside it to verify that the draft is still limited to staging requests.

Video: [Preview translations before publishing](https://www.youtube.com/watch?v=eA2nHI2BXFY). Watch published and staging clients side by side, edit an unpublished headline, disable staging, and publish the reviewed key. Recorded with Flutter SDK 1.3.0; the walkthrough also explains requests, snapshots, and separate caches.

## Opt in from your SDK

Source: https://rerune.io/docs/staging#sdk-setup

Staging requires at least Flutter 1.3.0, React / React Native / Angular 1.6.0, Android / iOS 1.2.0, or Kotlin Multiplatform 1.0.0. Upgrade to a staging-capable version before using these APIs. Add the option to your existing setup; keep your native localization configuration and UI revision hooks.

Flutter · setup and runtime switching:

```
await ReRune.setup(
  otaPublishId: '<READ_ONLY_OTA_PUBLISH_ID>',
  localizations: reRuneAppLocalizationsConfig,
  staging: true,
);

// After setup, from your QA controls:
await ReRune.setStaging(true);
await ReRune.setStaging(false);
```

React · setup and runtime switching:

```
import { ReRune } from '@rerune/react';

const client = ReRune.setup(
  { otaPublishId: '<READ_ONLY_OTA_PUBLISH_ID>', staging: true },
  nativeOptions,
);

// After setup, from your QA controls:
await client.setStaging(true);
await client.setStaging(false);
```

React Native uses the same staging option and client methods, imported from @rerune/react-native. Keep its existing native resources and AsyncStorage integration.

Angular · existing root provider:

```
import { ReRune } from '@rerune/angular/ngx-translate';

// In the root providers array (application config):
ReRune.setup(
  { otaPublishId: '<READ_ONLY_OTA_PUBLISH_ID>', staging: true },
  nativeOptions,
)
```

Use the ngx-translate or Transloco entry point that matches your app. Inject ReRuneService from that entry point. In your async QA controls, await service.setStaging(true) to preview drafts or service.setStaging(false) to return to published content. For SSR, server and browser must use the same mode; mismatched hydration snapshots are discarded.

Android switches at runtime with ReRune.setStaging(true) and returns with ReRune.setStaging(false). The linked Android example apps include a session-scoped staging toggle.

iOS · setup and runtime switching:

```
// Enable staging when the app starts.
reRuneSetup(
    otaPublishId: "<READ_ONLY_OTA_PUBLISH_ID>",
    staging: true
)

// Or switch modes after setup from an async QA control.
try await reRuneSetStagingMode(true)
let isStaging = reRuneIsStagingModeEnabled
try await reRuneSetStagingMode(false)
```

Call reRuneSetStagingMode only after reRuneSetup; otherwise it throws ReRuneStagingModeError.setupRequired. Read reRuneIsStagingModeEnabled to show the active mode in your QA controls. Production and staging use separate caches, and the next app launch starts with the staging value passed to setup.

- [Flutter staging reference](https://pub.dev/packages/rerune#staging-draft-translations)
- [React staging reference](https://www.npmjs.com/package/@rerune/react#previewing-draft-translations)
- [Angular staging reference](https://www.npmjs.com/package/@rerune/angular#previewing-draft-translations)
- [Android example apps](https://github.com/BasalBit/rerune-android#try-rerune)
- [iOS example apps](https://github.com/BasalBit/rerune-ios/tree/1.7.0/Examples)

## Refresh, snapshots, and caching

Source: https://rerune.io/docs/staging#refresh-and-cache

Live preview updates on a successful SDK check, not on every keystroke or through a push connection. Staging requests add staging=true to manifest and locale requests. Each sync downloads the full snapshot of every language it checks, even when the manifest is unchanged, because draft edits do not advance publication versions. Flutter, Android, iOS, and Kotlin Multiplatform check every published language; React, React Native, and Angular check the languages the app shows.

Locale responses provide full snapshots, with ETags for revalidation. Applying a new snapshot replaces the previous locale document: never-published keys can appear, and deleted or disabled keys disappear from OTA content. Normal main-language and bundled fallback still applies.

The SDKs keep staging and published caches separate. Runtime switching selects the matching cache and checks for updates. The mode selection is session-scoped; the next app startup uses its setup option. Flutter custom cache stores must provide a separate stagingNamespace. React SSR preload must use the same staging option as browser setup.

Staging downloads use the project's normal OTA quota. After each saved edit, refresh the test app and allow its normal reactive consumers or revision observers to redraw the text.

## Return to published text or release the draft

Source: https://rerune.io/docs/staging#publish-or-disable

Turn off OTA staging in the dashboard to make staging requests return published snapshots on their next successful sync. This does not publish or discard the editor drafts. Disconnected test apps can keep their cached preview until they reconnect and sync.

To release the reviewed wording, publish the intended keys. Normal clients receive that publication on their next successful update check. To stop previewing on one client, switch its staging mode to false; other staging clients keep their own selection.

> **App-code changes still need a build:** A never-published key can be delivered in a draft snapshot, but the app still needs code that uses it. New Flutter getters, native resource IDs, screens, or changed placeholder signatures require the corresponding generated code or application release.

- See also: [Publishing and OTA delivery](https://rerune.io/docs/publishing)
- See also: [Set up ReRune in CI/CD](https://rerune.io/docs/ci-cd)

## If the preview does not change

Source: https://rerune.io/docs/staging#troubleshooting

1. Check both switches, the project's OTA Publish ID, and the installed SDK version. A staging client receives published text when project staging is off.
2. Save the editor change, confirm the key is enabled, and check the selected language and variant. An unfinished translation can be previewed, but unsaved content cannot.
3. Trigger a new update check, inspect its errors and OTA quota, and verify the app's revision-driven UI refresh. Existing strings stored outside the render path need to be recalculated.

---

Part of the ReRune documentation (https://rerune.io/docs). Every chapter with a summary: https://rerune.io/llms.txt. Every chapter in one file: https://rerune.io/llms-full.txt. ReRune is not Rerun (rerun.io), the robotics data visualization tool.
