React Native and Flutter

Both bindings are thin layers over the native iOS and Android SDKs. App start, native crashes, frames and platform HTTP clients are measured natively; the binding adds screens and requests that only exist in JavaScript or Dart.

React Native

Requirements

  • React Native 0.73 or later. The new architecture and Hermes are supported; the old bridge still works.
  • Expo: supported in development builds and EAS builds through the config plugin. Not available in Expo Go.
  • Current version: 1.6.2.

Installation

npm install @gatepost/react-native
cd ios && pod install

The package resolves the native SDKs from the authenticated repositories described in the iOS and Android guides. Set the repository credentials in gradle.properties and in the CocoaPods environment as those pages describe. For Expo, add @gatepost/react-native to the plugins array in app.json and pass the project key there.

Starting the SDK

Native initialisation happens automatically from the injected key, so app start is measured from process creation. Call start in JavaScript as well to enable JS-side features:

import { Gatepost } from '@gatepost/react-native';

Gatepost.start({
  projectKey: '4f9c2e1b8a7d63e0',
  trackJsErrors: true,
  trackFetch: true,
});

Screens

With React Navigation, attach the tracker to the container so every route becomes a screen:

import { NavigationContainer } from '@react-navigation/native';
import { useGatepostNavigation } from '@gatepost/react-native';

export function App() {
  const navigationRef = useGatepostNavigation();
  return (
    <NavigationContainer ref={navigationRef}>
      {/* ... */}
    </NavigationContainer>
  );
}

Expo Router is supported through the same hook. For other navigation libraries, call Gatepost.screen(name) when a route becomes active.

Requests

fetch and XMLHttpRequest are instrumented in JavaScript, which captures the time the app waited including bridge overhead. Native requests made by third-party modules are recorded by the native SDK. If both layers see the same request it is de-duplicated by request ID.

JavaScript errors

Unhandled JS exceptions and unhandled promise rejections are reported as errors with the JS stack and the last screens. Upload source maps so stacks are readable:

gatepost-cli upload-sourcemap \
  --project-key 4f9c2e1b8a7d63e0 \
  --release 4.12.0 --platform ios \
  --bundle main.jsbundle --map main.jsbundle.map

Custom traces

const trace = Gatepost.trace('checkout.submit');
trace.set('items', cart.length);
try {
  await api.submit(cart);
  trace.end();
} catch (e) {
  trace.end(e);
  throw e;
}

Flutter

Requirements

  • Flutter 3.16 or later, Dart 3.2 or later.
  • Current version: 1.4.0.

Installation

flutter pub add gatepost_flutter

The plugin pulls the native SDKs through the platform build systems, so the repository credentials from the iOS and Android guides apply.

Starting the SDK

import 'package:gatepost_flutter/gatepost_flutter.dart';

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Gatepost.start(
    projectKey: '4f9c2e1b8a7d63e0',
    options: GatepostOptions(trackDartErrors: true),
  );
  runApp(const ShopApp());
}

Wrapping runApp in Gatepost.runZoned additionally captures errors thrown outside the framework's error handler.

Screens

MaterialApp(
  navigatorObservers: [GatepostNavigatorObserver()],
  // ...
)

Routes are named after their RouteSettings.name. For go_router, pass the observer to the router's observers. Rendering metrics come from the native frame timing on both platforms, so slow frames in Flutter are measured the same way as in native views.

Requests

Add the interceptor for dio, or wrap an http.Client:

final dio = Dio()..interceptors.add(GatepostDioInterceptor());

final client = GatepostHttpClient(http.Client());

Custom traces

final trace = Gatepost.trace('checkout.submit');
trace.set('items', cart.length);
try {
  await api.submit(cart);
  trace.end();
} catch (e) {
  trace.end(error: e);
  rethrow;
}

What is measured natively and what is not

SignalMeasured byNote
App startNative SDKFrom process creation to the first frame of the JS or Dart UI.
ScreensBindingRequires the navigation integration; native screens hosting the framework are hidden by default.
FramesNative SDKIdentical to native apps.
HTTP requestsBothDe-duplicated when both layers observe the same request.
Native crashes, ANRsNative SDKIdentical to native apps.
JS or Dart errorsBindingNeeds source maps for readable stacks in React Native. Dart stacks are readable without upload.