Skip to main content
Fabien Fouassier
Back to Writing

React Native Bridging and SDK Integration

Lessons from bridging the STid access control SDK into a React Native app — from native modules to production reliability.

React NativeSDKSeptember 25, 2025·3 min readRead article
React Native Bridging and SDK Integration

Bridging native SDKs into React Native is straightforward when the SDK is stable — but SDK quality matters more than the bridge itself. This article covers how we became the first STid client to ship virtual card badging in a React Native app, and the lessons learned along the way.

Key takeaways

  • Classic NativeModules are reliable even without TurboModules, if designed cleanly
  • Observability (Crashlytics, Performance) helps distinguish bridge errors from SDK errors
  • SDK quality determines integration success more than bridging complexity

Context

When working with React Native, you often need to expose native capabilities that JavaScript alone can't provide. A concrete example from my experience was integrating the STid SDK — a native access control library that enables virtual card badging with a phone. Our goal was to make this available inside a React Native app, so users could unlock doors seamlessly through the app.

React Native version: 0.74

JS engine: Hermes was not enabled

Platforms: iOS 13+ and Android SDK 26+

Architecture: NativeModules (TurboModules didn't exist yet, migration is on the roadmap)

Repo setup: Single repo, no separate package publishing

Bridged SDK

Target: STid Access Control SDK

Purpose: Provide secure building access via virtual cards on mobile

Challenge: The SDK was originally native-only (Swift on iOS, Java on Android), with no React Native support.

On iOS, a constraint at the time required us to use the exact same Xcode version as STid's build environment — which slowed upgrades until resolved years later.

Bridge Design

Implementation: Classic NativeModule with RCT_EXPORT_METHOD

Data crossing the bridge: Simple JSON

Events: Emitted with RCTDeviceEventEmitter

API shape: Aligned with the app's TypeScript typings, though mostly inferred (better typing expected with TurboModules migration)

Bridge type: Async only (no JSI for high-frequency calls)

iOS Integration

Language: Swift (SDK was Swift-based)

Lifecycle: Lifecycle and permissions delegated to JS where possible; native handled the rest

Challenges: No major bridging difficulties — most issues came directly from the SDK

Android Integration

Language: Java

Lifecycle: Required background services; some crashes originated from SDK's handling of background tasks

Permissions: Implemented natively and requested in JS per Android's rules

Challenges: Same as iOS — SDK-specific crashes rather than bridge integration errors

Reliability & Observability

Feature toggles: Available at app feature level, not within the native bridge itself

Testing: Mostly manual, since physical readers were required. SDK wasn't emulator-friendly, limiting automation

Performance: Monitored with Firebase Performance — no bottlenecks observed

Crash reporting: Firebase Crashlytics captured both native and JS crashes, clearly distinguished

Lessons Learned

Plan migration paths early — especially as React Native evolves

Classic NativeModules are reliable — even without TurboModules, if designed cleanly

Observability is essential — Crashlytics and Performance monitoring help differentiate between bridge errors and SDK errors

SDK quality matters most — bridging can be straightforward when the SDK is stable, but SDK quality matters more than the bridge itself

The Win

We turned two native SDKs into a fully functional React Native integration — becoming the first STid client to support this use case in RN. Testing was slow due to hardware requirements and SDK quirks, but the result shipped and worked reliably in production.

Future: Migrating to TurboModules will bring stronger type safety, more consistent typing in TypeScript, and improved integration with the new RN architecture.