React Native NativeReact Native Native

C++ / ObjC++

Write native functions and components in C++ and Objective-C++.

C++ gives you zero-overhead access to platform APIs and any existing C/C++ library. Use .cpp for pure C++, or .mm for Objective-C++ with full iOS framework access.

Setup

No additional setup is required. Xcode provides clang for iOS compilation, and the Android NDK provides it for Android. Both are already available if you have a working React Native development environment.

Functions

Use NATIV_EXPORT(sync) to annotate functions. They become importable from JS.

math_utils.cpp
#include <cmath>
#include <string>

NATIV_EXPORT(sync)
int add(int a, int b) {
    return a + b;
}

NATIV_EXPORT(sync)
double fast_inv_sqrt(double x) {
    float xf = static_cast<float>(x);
    float xhalf = 0.5f * xf;
    int i = *(int*)&xf;
    i = 0x5f3759df - (i >> 1);
    xf = *(float*)&i;
    xf = xf * (1.5f - xhalf * xf * xf);
    return static_cast<double>(xf);
}

NATIV_EXPORT(sync)
std::string greet(const std::string& name) {
    return "Yes " + name + " from C++!";
}

Import from JS just like any module:

App.tsx
import { add, fast_inv_sqrt, greet } from './math_utils';

<Text>add(2, 3) = {String(add(2, 3))}</Text>
<Text>fast_inv_sqrt(4) = {String(fast_inv_sqrt(4))}</Text>
<Text>{greet("Nativ")}</Text>

Async functions

Use NATIV_EXPORT(async) for functions that do heavy work or I/O. They return a Promise to JS and run on a background thread automatically:

async_demo.mm
#include "Nativ.h"
#import <Foundation/Foundation.h>
#include <string>
#include <thread>
#include <chrono>

NATIV_EXPORT(async)
std::string slowGreet(const std::string& name) {
    // Runs on a background thread — won't block the UI
    std::this_thread::sleep_for(std::chrono::seconds(1));
    return "Hello " + name + " (after 1s)!";
}
App.tsx
import { slowGreet } from './async_demo';

const result = await slowGreet('World');
// "Hello World (after 1s)!" — UI stayed responsive the whole time

For OS APIs that are already async (network, location, etc.), the function still runs on a background thread. Use synchronous wrappers like semaphores or completion handlers within the function body.

Main thread dispatch

UIKit APIs must be called from the main thread. Add main to the annotation and the bridge handles the dispatch automatically:

NATIV_EXPORT(sync, main)
double getScreenBrightness() {
    return (double)[UIScreen mainScreen].brightness;
}

Without main, the function runs on the JS thread (which is not the main thread in Fabric). Use main for any function that touches UIKit, AppKit, haptics, or other UI APIs.

iOS platform APIs (ObjC++)

Rename to .mm to use Objective-C++ and access any iOS framework:

device_info.mm
#import <UIKit/UIKit.h>
#import <sys/utsname.h>
#include <string>

NATIV_EXPORT(sync, main)
std::string getColorScheme() {
    UITraitCollection *traits = [UITraitCollection currentTraitCollection];
    switch (traits.userInterfaceStyle) {
        case UIUserInterfaceStyleDark:  return "dark";
        case UIUserInterfaceStyleLight: return "light";
        default: return "unknown";
    }
}

NATIV_EXPORT(sync, main)
double getScreenBrightness() {
    return (double)[UIScreen mainScreen].brightness;
}

NATIV_EXPORT(sync)
std::string getDeviceModel() {
    struct utsname systemInfo;
    uname(&systemInfo);
    return std::string(systemInfo.machine);
}
App.tsx
import { getColorScheme, getScreenBrightness, getDeviceModel } from './device_info';

<Text>colorScheme: {getColorScheme()}</Text>
<Text>brightness: {getScreenBrightness().toFixed(2)}</Text>
<Text>device: {getDeviceModel()}</Text>

Haptic feedback example

haptics.mm
#import <UIKit/UIKit.h>
#include <string>

NATIV_EXPORT(sync)
std::string tapLight() {
    UIImpactFeedbackGenerator *gen =
        [[UIImpactFeedbackGenerator alloc] initWithStyle:UIImpactFeedbackStyleLight];
    [gen prepare];
    [gen impactOccurred];
    return "light";
}

NATIV_EXPORT(sync)
std::string tapMedium() {
    UIImpactFeedbackGenerator *gen =
        [[UIImpactFeedbackGenerator alloc] initWithStyle:UIImpactFeedbackStyleMedium];
    [gen prepare];
    [gen impactOccurred];
    return "medium";
}
App.tsx
import { tapLight, tapMedium } from './haptics';

<Pressable onPress={() => tapMedium()}>
  <Text>Haptic</Text>
</Pressable>

Components (ObjC++)

Use NATIV_COMPONENT with a props struct for native views:

GradientBox.mm
#import <UIKit/UIKit.h>
#import <QuartzCore/QuartzCore.h>
#include "Nativ.h"

struct GradientBoxProps {
    std::string title = "Gradient from ObjC++";
    double corner_radius = 12.0;
};

NATIV_COMPONENT(gradientbox, GradientBoxProps)

static void mount(void* view_ptr, float width, float height, GradientBoxProps props) {
    UIView* view = (__bridge UIView*)view_ptr;

    CAGradientLayer* gradient = [CAGradientLayer layer];
    gradient.frame = CGRectMake(0, 0, width, height);
    gradient.colors = @[
        (id)[UIColor colorWithRed:0.56 green:0.07 blue:0.99 alpha:1.0].CGColor,
        (id)[UIColor colorWithRed:0.89 green:0.59 blue:0.95 alpha:1.0].CGColor,
    ];
    gradient.cornerRadius = props.corner_radius;
    [view.layer addSublayer:gradient];

    UILabel* label = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, width, height)];
    label.text = [NSString stringWithUTF8String:props.title.c_str()];
    label.textColor = [UIColor whiteColor];
    label.textAlignment = NSTextAlignmentCenter;
    [view addSubview:label];
}
App.tsx
import GradientBox from './GradientBox';

<GradientBox
  title="Props from JS!"
  cornerRadius={10}
  style={{ width: '100%', height: 70, borderRadius: 12, overflow: 'hidden' }}
/>

On this page