Skip to main content

Native Modules

Your app may need to call operating-system APIs that React Native does not provide. You can write the integration yourself with a Turbo Native Module. This guide builds one, in Rust or in C++.

The steps are:

  1. define a typed specification in TypeScript or Flow;
  2. let Codegen turn the specification into native interfaces;
  3. write your app code against the specification; and
  4. implement the generated interface in native code.

We will build a small NativeLocalStorage module that keeps strings by key.

1. Define the specification​

The specification is a TypeScript file whose name starts with Native. It describes what JavaScript can call, with React Native's own types:

NativeLocalStorage.ts
import type { TurboModule } from 'react-native';
import { TurboModuleRegistry } from 'react-native';

export interface Spec extends TurboModule {
setItem(value: string, key: string): void;
getItem(key: string): string | null;
removeItem(key: string): void;
clear(): void;
}

export default TurboModuleRegistry.getEnforcing<Spec>('NativeLocalStorage');

The string passed to getEnforcing is the name of the module. Rust modules lists how each spec type maps to Rust, and C++ modules lists the C++ types.

2. Where the code lives​

A module lives either in your app, or in a library that other apps install.

Put the specification and the implementation in your app's desktop/ folder, and list the specification in desktop/build.rs, so that Codegen runs for it:

desktop/build.rs
fn main() {
rn_gpui_module_build::generate(&["src/NativeLocalStorage.ts"]);
// ... the lines the template already has
}

Rust is the language for modules inside an app. For C++, write a library.

3. Use the module in JavaScript​

The default export of the specification is the module, so import it like any module:

App.tsx
import React from 'react';
import { Button, Text, View } from 'react-native';
import NativeLocalStorage from './desktop/src/NativeLocalStorage';

export default function App() {
const [value, setValue] = React.useState<string | null>(null);
return (
<View>
<Button
title="Save"
onPress={() => {
NativeLocalStorage.setItem('hello', 'greeting');
setValue(NativeLocalStorage.getItem('greeting'));
}}
/>
<Text>{value ?? 'Nothing saved yet'}</Text>
</View>
);
}

For a library the import is the library's package name.

4. Implement the module​

Codegen turns the specification into a trait (Rust) or a base class (C++) named after the file, here NativeLocalStorageSpec. Your code implements it, so a mistake against the specification is a compile error, and not a surprise at run time.

desktop/src/local_storage.rs
use std::collections::HashMap;
use std::sync::Mutex;

use rn_gpui_module::ModuleError;

use crate::{NativeLocalStorageEvents, NativeLocalStorageSpec};

#[derive(Default)]
pub struct LocalStorage {
items: Mutex<HashMap<String, String>>,
}

impl NativeLocalStorageSpec for LocalStorage {
fn new(_events: NativeLocalStorageEvents) -> Self {
Self::default()
}

fn set_item(&self, value: String, key: String) {
self.items.lock().unwrap().insert(key, value);
}

fn get_item(&self, key: String) -> Result<Option<String>, ModuleError> {
Ok(self.items.lock().unwrap().get(&key).cloned())
}

fn remove_item(&self, key: String) {
self.items.lock().unwrap().remove(&key);
}

fn clear(&self) {
self.items.lock().unwrap().clear();
}
}

include_specs!() in main.rs (or lib.rs, in a library) pulls in the generated trait, the events handle and the value types:

desktop/src/main.rs
rn_gpui_module::include_specs!();

mod local_storage;
use local_storage::LocalStorage;

fn main() {
// App-local modules are registered before the app starts.
react_native_gpui::register_module(native_local_storage_module::<LocalStorage>());
// ... the rest of the template's main
}

A library registers its modules in a function called rn_gpui_register, which autolinking calls. See Create a library.

Run it​

npx react-native run-desktop

Codegen runs as part of the build, the module is compiled into the app, and NativeLocalStorage is available to JavaScript.

Next steps​