Guides

Native Fingerprints, Runtime Versions, or Both?

Choose manual runtime versions, native fingerprints, or hybrid compatibility for React Native OTA updates, with clear rebuild requirements and limits.

6 min read

Written by Founder and maintainer of Bundle Drop

Choosing a compatibility mode is a decision about who maintains the boundary between a native binary and its OTA updates. Bundle Drop supports explicit runtime versions, native fingerprints, and a hybrid of the two. All three enforce the same requirement: an OTA bundle and the installed binary must resolve the same platform runtime identity.

Manual versions remain the default. Native fingerprinting is Beta and opt-in for Expo and bare React Native. Choose manual control when your team deliberately reviews and names compatibility changes, fingerprints when collected native inputs should determine the identity, or hybrid control when you need both automatic detection and an intentional release boundary.

Compare the responsibility, not the label

ApproachWhat determines compatibilityWhat your team maintains
Manual runtime versionsAn explicit iOS or Android stringThe decision to change that string when native compatibility changes
Native fingerprintsAn identity derived from the selected platform's collected native inputsReproducible inputs, dependency locks, and coverage for custom build context
Hybrid compatibilityThe fingerprint and an optional explicit platform boundary togetherThe same reproducibility, plus deliberate changes to the named boundary

A runtime value is still a compatibility identity in every mode. It is not an app-store version, a rollout percentage, or a channel. The runtime-boundary model explains how several native generations can coexist on one channel; this article focuses on choosing how those identities are maintained.

Choose manual versions for deliberate compatibility review

Manual mode uses the platform literals in bundle.drop.config.js. A team might keep ios: "native-4" across compatible JavaScript releases and advance it to "native-5" when a new iOS module changes the native surface. Android can retain its own boundary when that platform has not changed.

This is useful when a release process already has a reliable native-change review or when custom build inputs need a boundary the team explicitly controls. It also leaves responsibility with that review: forgetting to change the string can group incompatible binaries together. A successful upload does not prove that a manual compatibility decision was correct.

Use Runtime Version for the configuration. Review the actual native requirements before preserving a boundary, rather than assuming that a JavaScript filename or unchanged app version proves compatibility.

Choose fingerprints for collected native inputs

In fingerprint mode, the native build embeds a derived identity. Upload independently calculates an identity from the current project. Matching values permit compatible OTA selection; a detected native change produces a different value and separates that bundle from older binaries.

The normal selector is small:

js
runtimeVersion: {
  compatibilitySource: "fingerprint",
},

This removes the manual runtime bump for changes represented by the collector. It does not remove native release engineering. Use the same SDK version, dependency lockfile, native dependencies, configuration, and declared build context for the binary and its uploads. Collection failures stop the operation rather than reusing a previous report or falling back to manual mode.

Fingerprinting has a documented coverage boundary. Whole autolinked package trees can conservatively create a new identity even when the changed files are JavaScript or tooling. Conversely, arbitrary compiler settings, externally resolved native artifacts, and an in-place React Native core patch are not universally detected. Custom inputs need deliberate representation. The canonical setup and coverage guide owns the current options and limits.

Choose hybrid control for a deliberate release boundary

Hybrid compatibility keeps the fingerprint selector and adds optional platform strings:

js
runtimeVersion: {
  compatibilitySource: "fingerprint",
  ios: "native-4",
  android: "native-3",
},

The explicit value contributes to the derived identity; it is not an override that can make different fingerprints compatible. A detected native-input change separates updates even if "native-4" stays unchanged. Changing that string also creates a different identity, even if the collected native inputs are identical. Either change needs a matching native binary before that line has an installed audience.

This is useful when a team wants automatic detection within a deliberately named compatibility generation, or needs to force a boundary for native context outside the collected inputs. For example, a custom compiler setting may require an explicit input or a deliberate boundary change. Keeping a familiar string alone does not prove that uncollected inputs stayed compatible.

Hybrid compatibility is separate from patch delivery with full-bundle fallback. Compatibility decides which target is eligible. Patch or full-bundle delivery decides how that already-compatible target reaches the device. Neither transfer method crosses a runtime boundary.

Keep Expo-owned authority separate

Expo projects can separately choose runtimeVersion: { source: "expo" } to delegate runtime authority to evaluated Expo configuration and use the documented matching-build receipt workflow. This is a different authority path from Bundle Drop's compatibilitySource: "fingerprint".

Do not combine source: "expo" with compatibilitySource, Bundle Drop fingerprint options, or explicit ios and android literals. Choose one authority for a binary and its uploads. The Expo guide explains that workflow without making it a prerequisite for Bundle Drop's manual or native-fingerprint modes.

Change modes through a native release

Enabling fingerprints, returning to manual mode, or changing an explicit boundary requires a new native build. An OTA config change cannot give an older binary a fingerprint or replace its embedded authority. Native code changes still need a native release; fingerprints identify the difference rather than delivering that code.

Before adopting a mode on each platform:

  1. Choose the authority and record the relevant native build inputs.
  2. Build and test a new native binary with that configuration.
  3. Calculate and upload from the matching project context.
  4. Distribute the binary and retain compatible updates for older installed runtime lines.

Use the OTA-versus-store decision guide when the change itself is unclear. Compatibility selection does not change update policies, manual apply control, rollout eligibility, or native recovery.

Choose the mode your release process can maintain accurately. Manual values make the team's compatibility review explicit. Fingerprints derive the boundary from collected inputs. Hybrid control combines that detection with a deliberate platform boundary, while preserving the same requirement to build and distribute matching native code.

About the author

Founder and maintainer of Bundle Drop

George builds Bundle Drop and writes about the compatibility, delivery, rollout, and recovery boundaries behind React Native OTA updates.

GitHub profile

Interested in safer OTA deployments?

Read the implementation guides or connect a React Native project when you are ready to test the release workflow.