Expert in React Native 0.83+ native modules, Turbo Modules with Codegen, Fabric renderer, JSI (JavaScript Interface), New Architecture migration, bridging JavaScript and native code, iOS Swift modules, Android Kotlin modules, expo config plugins. Activates for native module, native code, bridge, turbo module, JSI, fabric, autolinking, custom native module, ios module, android module, swift, kotlin, objective-c, java native code, codegen, new architecture.
Expert in React Native 0.83+ native modules with New Architecture. Creates Turbo Modules using Codegen, bridges Swift/Kotlin code, and handles JSI, Fabric, and expo config plugins.
/plugin marketplace add anton-abyzov/specweave/plugin install sw-mobile@specweaveThis skill inherits all available tools. When active, it can use any tool Claude has access to.
Specialized in React Native 0.83+ native module integration with New Architecture (enabled by default). Expert in Turbo Modules, JSI, Fabric, Codegen, and modern native development patterns.
What Are Native Modules?
New Architecture (Default in RN 0.76+)
Key Benefits of New Architecture
Installation with Autolinking
# Install module
npm install react-native-camera
# iOS: Install pods (autolinking handles most configuration)
cd ios && pod install && cd ..
# Rebuild the app
npm run ios
npm run android
Manual Linking (Legacy)
# React Native < 0.60 (rarely needed now)
react-native link react-native-camera
Expo Integration
# For Expo managed workflow, use config plugins
npx expo install react-native-camera
# Add plugin to app.json
{
"expo": {
"plugins": [
[
"react-native-camera",
{
"cameraPermission": "Allow $(PRODUCT_NAME) to access your camera"
}
]
]
}
}
# Rebuild dev client
eas build --profile development --platform all
iOS Native Module (Swift)
// RCTCalendarModule.swift
import Foundation
@objc(CalendarModule)
class CalendarModule: NSObject {
@objc
static func requiresMainQueueSetup() -> Bool {
return false
}
@objc
func createEvent(_ name: String, location: String, date: NSNumber) {
// Native implementation
print("Creating event: \(name) at \(location)")
}
@objc
func getEvents(_ callback: @escaping RCTResponseSenderBlock) {
let events = ["Event 1", "Event 2", "Event 3"]
callback([NSNull(), events])
}
@objc
func findEvents(_ resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock) {
// Async with Promise
DispatchQueue.global().async {
let events = self.fetchEventsFromNativeAPI()
resolve(events)
}
}
}
// RCTCalendarModule.m (Bridge file)
#import <React/RCTBridgeModule.h>
@interface RCT_EXTERN_MODULE(CalendarModule, NSObject)
RCT_EXTERN_METHOD(createEvent:(NSString *)name location:(NSString *)location date:(nonnull NSNumber *)date)
RCT_EXTERN_METHOD(getEvents:(RCTResponseSenderBlock)callback)
RCT_EXTERN_METHOD(findEvents:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
@end
Android Native Module (Kotlin)
// CalendarModule.kt
package com.myapp
import com.facebook.react.bridge.*
class CalendarModule(reactContext: ReactApplicationContext) :
ReactContextBaseJavaModule(reactContext) {
override fun getName(): String {
return "CalendarModule"
}
@ReactMethod
fun createEvent(name: String, location: String, date: Double) {
// Native implementation
println("Creating event: $name at $location")
}
@ReactMethod
fun getEvents(callback: Callback) {
val events = WritableNativeArray().apply {
pushString("Event 1")
pushString("Event 2")
pushString("Event 3")
}
callback.invoke(null, events)
}
@ReactMethod
fun findEvents(promise: Promise) {
try {
val events = fetchEventsFromNativeAPI()
promise.resolve(events)
} catch (e: Exception) {
promise.reject("ERROR", e.message, e)
}
}
}
// CalendarPackage.kt
package com.myapp
import com.facebook.react.ReactPackage
import com.facebook.react.bridge.NativeModule
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.uimanager.ViewManager
class CalendarPackage : ReactPackage {
override fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule> {
return listOf(CalendarModule(reactContext))
}
override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> {
return emptyList()
}
}
JavaScript Usage
// CalendarModule.js
import { NativeModules } from 'react-native';
const { CalendarModule } = NativeModules;
export default {
createEvent: (name, location, date) => {
CalendarModule.createEvent(name, location, date);
},
getEvents: (callback) => {
CalendarModule.getEvents((error, events) => {
if (error) {
console.error(error);
} else {
callback(events);
}
});
},
findEvents: async () => {
try {
const events = await CalendarModule.findEvents();
return events;
} catch (error) {
console.error(error);
throw error;
}
}
};
// Usage in components
import CalendarModule from './CalendarModule';
function MyComponent() {
const handleCreateEvent = () => {
CalendarModule.createEvent('Meeting', 'Office', Date.now());
};
const handleGetEvents = async () => {
const events = await CalendarModule.findEvents();
console.log('Events:', events);
};
return (
<View>
<Button title="Create Event" onPress={handleCreateEvent} />
<Button title="Get Events" onPress={handleGetEvents} />
</View>
);
}
Creating a Turbo Module with Codegen
Step 1: Create the TypeScript spec (source of truth for types):
// specs/NativeCalendarModule.ts
import type { TurboModule } from 'react-native';
import { TurboModuleRegistry } from 'react-native';
export interface Spec extends TurboModule {
// Sync method (fast, blocks JS thread)
getConstants(): {
DEFAULT_REMINDER_MINUTES: number;
};
// Async methods (recommended for most cases)
createEvent(name: string, location: string, date: number): Promise<string>;
findEvents(): Promise<string[]>;
deleteEvent(eventId: string): Promise<boolean>;
// Callback-based (legacy pattern, prefer Promise)
getEventsWithCallback(callback: (events: string[]) => void): void;
}
export default TurboModuleRegistry.getEnforcing<Spec>('CalendarModule');
Step 2: Configure Codegen in package.json:
{
"codegenConfig": {
"name": "CalendarModuleSpec",
"type": "modules",
"jsSrcsDir": "specs",
"android": {
"javaPackageName": "com.myapp.calendar"
}
}
}
Step 3: Implement the native side (iOS - Swift):
// CalendarModule.swift
import Foundation
@objc(CalendarModule)
class CalendarModule: NSObject {
@objc static func moduleName() -> String! {
return "CalendarModule"
}
@objc static func requiresMainQueueSetup() -> Bool {
return false
}
@objc func getConstants() -> [String: Any] {
return ["DEFAULT_REMINDER_MINUTES": 15]
}
@objc func createEvent(_ name: String, location: String, date: Double,
resolve: @escaping RCTPromiseResolveBlock,
reject: @escaping RCTPromiseRejectBlock) {
DispatchQueue.global().async {
// Native implementation
let eventId = UUID().uuidString
resolve(eventId)
}
}
@objc func findEvents(_ resolve: @escaping RCTPromiseResolveBlock,
reject: @escaping RCTPromiseRejectBlock) {
DispatchQueue.global().async {
let events = ["Meeting", "Lunch", "Call"]
resolve(events)
}
}
@objc func deleteEvent(_ eventId: String,
resolve: @escaping RCTPromiseResolveBlock,
reject: @escaping RCTPromiseRejectBlock) {
resolve(true)
}
}
Step 4: Implement the native side (Android - Kotlin):
// CalendarModule.kt
package com.myapp.calendar
import com.facebook.react.bridge.*
import com.facebook.react.module.annotations.ReactModule
@ReactModule(name = CalendarModule.NAME)
class CalendarModule(reactContext: ReactApplicationContext) :
NativeCalendarModuleSpec(reactContext) {
companion object {
const val NAME = "CalendarModule"
}
override fun getName(): String = NAME
override fun getConstants(): MutableMap<String, Any> {
return mutableMapOf("DEFAULT_REMINDER_MINUTES" to 15)
}
override fun createEvent(name: String, location: String, date: Double, promise: Promise) {
val eventId = java.util.UUID.randomUUID().toString()
promise.resolve(eventId)
}
override fun findEvents(promise: Promise) {
val events = Arguments.createArray().apply {
pushString("Meeting")
pushString("Lunch")
pushString("Call")
}
promise.resolve(events)
}
override fun deleteEvent(eventId: String, promise: Promise) {
promise.resolve(true)
}
}
Benefits of Turbo Modules
Custom Native View (iOS - Swift)
// RCTCustomViewManager.swift
import UIKit
@objc(CustomViewManager)
class CustomViewManager: RCTViewManager {
override static func requiresMainQueueSetup() -> Bool {
return true
}
override func view() -> UIView! {
return CustomView()
}
@objc func setColor(_ view: CustomView, color: NSNumber) {
view.backgroundColor = RCTConvert.uiColor(color)
}
}
class CustomView: UIView {
override init(frame: CGRect) {
super.init(frame: frame)
self.backgroundColor = .blue
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
Custom Native View (Android - Kotlin)
// CustomViewManager.kt
class CustomViewManager : SimpleViewManager<View>() {
override fun getName(): String {
return "CustomView"
}
override fun createViewInstance(reactContext: ThemedReactContext): View {
return View(reactContext).apply {
setBackgroundColor(Color.BLUE)
}
}
@ReactProp(name = "color")
fun setColor(view: View, color: Int) {
view.setBackgroundColor(color)
}
}
JavaScript Usage
import { requireNativeComponent } from 'react-native';
const CustomView = requireNativeComponent('CustomView');
function MyComponent() {
return (
<CustomView
style={{ width: 200, height: 200 }}
color="red"
/>
);
}
Module Not Found
# iOS: Clear build and reinstall pods
cd ios && rm -rf build Pods && pod install && cd ..
npm run ios
# Android: Clean and rebuild
cd android && ./gradlew clean && cd ..
npm run android
# Clear Metro cache
npx react-native start --reset-cache
Autolinking Not Working
# Verify module in package.json
npm list react-native-camera
# Re-run pod install
cd ios && pod install && cd ..
# Check react-native.config.js for custom linking config
Native Crashes
# iOS: Check Xcode console for crash logs
# Look for:
# - Unrecognized selector sent to instance
# - Null pointer exceptions
# - Memory issues
# Android: Check logcat
adb logcat *:E
# Look for:
# - Java exceptions
# - JNI errors
# - Null pointer exceptions
Ask me when you need help with:
# Create module template
npx create-react-native-module my-module
# Build iOS module
cd ios && xcodebuild
# Build Android module
cd android && ./gradlew assembleRelease
# Test module locally
npm link
cd ../MyApp && npm link my-module
# iOS: Run with Xcode debugger
open ios/MyApp.xcworkspace
# Android: Run with Android Studio debugger
# Open android/ folder in Android Studio
# Print native logs
# iOS
tail -f ~/Library/Logs/DiagnosticReports/*.crash
# Android
adb logcat | grep "CalendarModule"
Use Codegen (New Architecture) for type safety:
// NativeMyModule.ts
import type { TurboModule } from 'react-native';
import { TurboModuleRegistry } from 'react-native';
export interface Spec extends TurboModule {
getString(key: string): Promise<string>;
setString(key: string, value: string): void;
}
export default TurboModuleRegistry.getEnforcing<Spec>('MyModule');
// iOS - Emit events to JavaScript
import Foundation
@objc(DeviceOrientationModule)
class DeviceOrientationModule: RCTEventEmitter {
override func supportedEvents() -> [String]! {
return ["OrientationChanged"]
}
@objc
override static func requiresMainQueueSetup() -> Bool {
return true
}
@objc
func startObserving() {
NotificationCenter.default.addObserver(
self,
selector: #selector(orientationChanged),
name: UIDevice.orientationDidChangeNotification,
object: nil
)
}
@objc
func stopObserving() {
NotificationCenter.default.removeObserver(self)
}
@objc
func orientationChanged() {
let orientation = UIDevice.current.orientation
sendEvent(withName: "OrientationChanged", body: ["orientation": orientation.rawValue])
}
}
// JavaScript - Listen to native events
import { NativeEventEmitter, NativeModules } from 'react-native';
const { DeviceOrientationModule } = NativeModules;
const eventEmitter = new NativeEventEmitter(DeviceOrientationModule);
function MyComponent() {
useEffect(() => {
const subscription = eventEmitter.addListener('OrientationChanged', (data) => {
console.log('Orientation:', data.orientation);
});
return () => subscription.remove();
}, []);
return <View />;
}
// Android - Pass callbacks
@ReactMethod
fun processData(data: String, successCallback: Callback, errorCallback: Callback) {
try {
val result = heavyProcessing(data)
successCallback.invoke(result)
} catch (e: Exception) {
errorCallback.invoke(e.message)
}
}
// JavaScript
CalendarModule.processData(
'input data',
(result) => console.log('Success:', result),
(error) => console.error('Error:', error)
);
// iOS - Synchronous method (blocks JS thread!)
@objc
func getDeviceId() -> String {
return UIDevice.current.identifierForVendor?.uuidString ?? "unknown"
}
// JavaScript - Synchronous call
const deviceId = CalendarModule.getDeviceId();
console.log(deviceId); // Returns immediately
Warning: Synchronous methods block the JS thread. Use only for very fast operations (<5ms).
For Expo projects, use config plugins to modify native code:
// plugins/withCalendarPermission.ts
import { ConfigPlugin, withInfoPlist, withAndroidManifest } from '@expo/config-plugins';
const withCalendarPermission: ConfigPlugin = (config) => {
// iOS: Modify Info.plist
config = withInfoPlist(config, (config) => {
config.modResults.NSCalendarsUsageDescription =
'This app needs calendar access to schedule events';
return config;
});
// Android: Modify AndroidManifest.xml
config = withAndroidManifest(config, (config) => {
const mainApplication = config.modResults.manifest.application?.[0];
if (mainApplication) {
// Add permissions
config.modResults.manifest['uses-permission'] = [
...(config.modResults.manifest['uses-permission'] || []),
{ $: { 'android:name': 'android.permission.READ_CALENDAR' } },
{ $: { 'android:name': 'android.permission.WRITE_CALENDAR' } },
];
}
return config;
});
return config;
};
export default withCalendarPermission;
// app.json
{
"expo": {
"plugins": [
"./plugins/withCalendarPermission"
]
}
}
RN 0.76+ includes an interop layer for Bridge modules in New Architecture:
// For legacy modules that don't support Turbo Modules yet
import { NativeModules, TurboModuleRegistry } from 'react-native';
// This works in New Architecture via interop layer
const LegacyModule = NativeModules.LegacyBridgeModule;
// Or use the Turbo Module if available
const TurboModule = TurboModuleRegistry.get('ModernModule');
// Recommended: Create a wrapper that handles both
export function getCalendarModule() {
// Try Turbo Module first
const turbo = TurboModuleRegistry.get('CalendarModule');
if (turbo) return turbo;
// Fall back to Bridge module via interop
return NativeModules.CalendarModule;
}
Native Module Planning
spec.mdplan.mdtasks.mdTesting Strategy
Documentation
This skill should be used when the user asks to "create a slash command", "add a command", "write a custom command", "define command arguments", "use command frontmatter", "organize commands", "create command with file references", "interactive command", "use AskUserQuestion in command", or needs guidance on slash command structure, YAML frontmatter fields, dynamic arguments, bash execution in commands, user interaction patterns, or command development best practices for Claude Code.
This skill should be used when the user asks to "create an agent", "add an agent", "write a subagent", "agent frontmatter", "when to use description", "agent examples", "agent tools", "agent colors", "autonomous agent", or needs guidance on agent structure, system prompts, triggering conditions, or agent development best practices for Claude Code plugins.
This skill should be used when the user asks to "create a hook", "add a PreToolUse/PostToolUse/Stop hook", "validate tool use", "implement prompt-based hooks", "use ${CLAUDE_PLUGIN_ROOT}", "set up event-driven automation", "block dangerous commands", or mentions hook events (PreToolUse, PostToolUse, Stop, SubagentStop, SessionStart, SessionEnd, UserPromptSubmit, PreCompact, Notification). Provides comprehensive guidance for creating and implementing Claude Code plugin hooks with focus on advanced prompt-based hooks API.