All skills

Complete React Native and Expo optimization guide combining Callstack profiling with Vercel code patterns. Covers FPS, TTI, bundle size, memory, lists, animations, state, UI, and React Compiler. Use for any React Native performance, debugging, or best practices task.

Use this Skill: https://skilld.dev/gh/gigs-slc/react-native-skills/react-native

This session only. Nothing lands on disk.

referencesnative-turbo-modules.md

≈1.6k tokens on demand. Your agent reads this file only when SKILL.md points to it.

Skill: Fast Native Modules

Build performant Turbo Modules using modern languages and background threading.

Quick Pattern

Incorrect (sync method blocks JS thread):

@objc func heavyWork() -> NSNumber {
    Thread.sleep(forTimeInterval: 2)  // Blocks JS for 2s!
    return 42
}

Correct (async on background thread):

@objc func heavyWork(
    resolve: @escaping RCTPromiseResolveBlock,
    reject: RCTPromiseRejectBlock
) {
    DispatchQueue.global().async {
        let result = self.compute()
        resolve(result)
    }
}

When to Use

  • Creating new native modules
  • Optimizing existing module performance
  • Heavy computation needs to run off JS thread
  • Cross-platform C++ code needed

Prerequisites

  • React Native Builder Bob for scaffolding
npx create-react-native-library@latest my-library

Step-by-Step Instructions

1. Scaffold with Builder Bob

npx create-react-native-library@latest awesome-library
# Follow prompts: choose Turbo Module, select languages

Creates ready-to-publish library with:

  • iOS (Obj-C/Swift) support
  • Android (Kotlin) support
  • TypeScript definitions
  • Codegen setup

For local modules:

npx create-react-native-library@latest awesome-library --local

2. Enable Swift in iOS Module

Update awesome-library.podspec:

- s.source_files = "ios/**/*.{h,m,mm,cpp}"
+ s.source_files = "ios/**/*.{h,m,mm,cpp,swift}"

Create Swift file in Xcode (accept bridging header prompt).

Update header file for Swift compatibility:

// AwesomeLibrary.h
#import <Foundation/Foundation.h>

#if __cplusplus
#import "ReactCodegen/RNAwesomeLibrarySpec/RNAwesomeLibrarySpec.h"
#endif

@interface AwesomeLibrary : NSObject
#if __cplusplus
<NativeAwesomeLibrarySpec>
#endif
@end

Import header in bridging header:

// AwesomeLibrary-Bridging-Header.h
#import "AwesomeLibrary.h"

Implement in Swift:

// AwesomeLibrary.swift
import Foundation

extension AwesomeLibrary {
    @objc func multiply(_ a: Double, b: Double) -> NSNumber {
        return (a * b) as NSNumber
    }
}

Bridge in Obj-C++:

// AwesomeLibrary.mm
#import "AwesomeLibrary.h"

#if __has_include("awesome_library/awesome_library-Swift.h")
#import "awesome_library/awesome_library-Swift.h"
#else
#import "awesome_library-Swift.h"
#endif

@implementation AwesomeLibrary
RCT_EXPORT_MODULE()
RCT_EXTERN_METHOD(multiply:(double)a b:(double)b);
@end

3. Run on Background Thread (iOS)

@objc func heavyOperation(
    _ input: Double,
    resolve: @escaping RCTPromiseResolveBlock,
    reject: RCTPromiseRejectBlock
) {
    DispatchQueue.global().async {
        // Heavy work on background thread
        let result = self.expensiveComputation(input)
        resolve(result)
    }
}

4. Run on Background Thread (Android)

class AwesomeLibraryModule(reactContext: ReactApplicationContext) :
    NativeAwesomeLibrarySpec(reactContext) {
    
    private val moduleScope = CoroutineScope(Dispatchers.Default + SupervisorJob())
    
    override fun heavyOperation(input: Double, promise: Promise?) {
        moduleScope.launch {
            // Heavy work on coroutine
            val result = expensiveComputation(input)
            promise?.resolve(result)
        }
    }
    
    override fun invalidate() {
        super.invalidate()
        moduleScope.cancel()  // Prevent memory leaks!
    }
}

5. Use C++ for Cross-Platform Code

Create C++ Turbo Module for shared logic:

// MyCppModule.h
#pragma once

#include <ReactCommon/TurboModule.h>

namespace facebook::react {

class MyCppModule : public TurboModule {
public:
    MyCppModule(std::shared_ptr<CallInvoker> jsInvoker);
    
    double multiply(double a, double b);
};

} // namespace facebook::react

Register for iOS auto-linking:

// MyCppModuleRegistration.mm
#include <ReactCommon/CxxTurboModuleUtils.h>

@implementation MyCppModuleRegistration

+ (void)load {
    facebook::react::registerCxxModuleToGlobalModuleMap(
        std::string(facebook::react::MyCppModule::kModuleName),
        [&](std::shared_ptr<facebook::react::CallInvoker> jsInvoker) {
            return std::make_shared<facebook::react::MyCppModule>(jsInvoker);
        }
    );
}

@end

Threading Summary

Method Type Default Thread Best Practice
Sync JS thread Keep fast (<16ms)
Async Native modules thread OK for moderate work
Heavy async Custom background Use DispatchQueue/Coroutines

Language Interop Costs

Interface Overhead Notes
Obj-C ↔ C++ ~0 Compile-time
Swift ↔ C++ ~0 Swift 5.9+ interop
Kotlin ↔ C++ (JNI) Medium Per-call lookup
C++ Turbo Module Low JSI direct access

Tip: C++ Turbo Modules skip JNI at runtime since JS holds direct C++ function references via JSI.

Code Example: Complete Async Operation

// TypeScript interface
export interface Spec extends TurboModule {
    multiply(a: number, b: number): number;  // Sync
    heavyOperation(input: number): Promise<number>;  // Async
}
// Android implementation
override fun heavyOperation(input: Double, promise: Promise?) {
    moduleScope.launch {
        try {
            val result = withContext(Dispatchers.Default) {
                // Simulate heavy work
                delay(1000)
                input * 2
            }
            promise?.resolve(result)
        } catch (e: Exception) {
            promise?.reject("ERROR", e.message)
        }
    }
}
// iOS implementation
@objc func heavyOperation(
    _ input: Double,
    resolve: @escaping RCTPromiseResolveBlock,
    reject: @escaping RCTPromiseRejectBlock
) {
    DispatchQueue.global(qos: .userInitiated).async {
        // Simulate heavy work
        Thread.sleep(forTimeInterval: 1.0)
        let result = input * 2
        resolve(result)
    }
}

Common Pitfalls

  • Sync methods that block: Keep under 16ms or make async
  • Forgetting to cancel coroutine scope: Causes memory leaks
  • Not handling errors in async: Always try/catch with reject
  • Accessing UI from background: Dispatch to main thread

Related Skills

Source: SKILL.md on GitHub

2 alerts6mo4 checks · Risk HIGH
  • Gen Agent Trust Hub7mo

    The skill is a technical React Native guide that includes a high-risk remote code execution pattern for installing a performance tool.

  • Socket6mo

    No alerts

  • Snyk7mo

    Risk: MEDIUM · No issues

  • Runlayer7mo

    7/67 files flagged

Signed by skilld at 1d299ca. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub 2 days ago.

Dormantupdated 8 months ago
Other metadata
metadata
{
  "author": "Callstack + Vercel",
  "version": "1.0.0",
  "tags": "react-native, expo, performance, optimization, patterns"
}

README badge

README badge for gigs-slc/react-native-skills/react-native