Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Expert in React Native native modules, bridging JavaScript and native code, writing custom native modules, using Turbo Modules, Fabric, JSI, autolinking, module configuration, iOS Swift/Objective-C modules, Android Kotlin/Java modules. 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.
.claude/skills/microck-native-modules/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-15 | ✗→✓ | ▲ Improved | 125% | 0% |
| case-21 | ✓→✗ | ▼ Worse | 143% | 0% |
| case-06 | ✓→✓ | = Same ✓ | 656% | 0% |
| case-01 | ✓→✓ | = Same ✓ | 189% | 0% |
| case-02 | ✓→✓ | = Same ✓ | 272% | 0% |
Specialized in React Native native module integration, including custom native module development, third-party native library integration, and troubleshooting native code issues.
What Are Native Modules?
Modern Architecture
Installation with Autolinking
bash# 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)
bash# React Native < 0.60 (rarely needed now) react-native link react-native-camera
Expo Integration
bash# 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)
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) } } }
objectivec// 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)
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) } } }
kotlin// 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
javascript// 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
typescript// NativeCalendarModule.ts (Codegen spec) import type { TurboModule } from 'react-native'; import { TurboModuleRegistry } from 'react-native'; export interface Spec extends TurboModule { createEvent(name: string, location: string, date: number): void; findEvents(): Promise<string[]>; } export default TurboModuleRegistry.getEnforcing<Spec>('CalendarModule');
Benefits of Turbo Modules
Custom Native View (iOS - Swift)
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)
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
javascriptimport { requireNativeComponent } from 'react-native'; const CustomView = requireNativeComponent('CustomView'); function MyComponent() { return ( <CustomView style={{ width: 200, height: 200 }} color="red" /> ); }
Module Not Found
bash# 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
bash# 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
bash# 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:
bash# 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
bash# 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:
typescript// 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');
swift// 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// 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 />; }
kotlin// 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// JavaScript CalendarModule.processData( 'input data', (result) => console.log('Success:', result), (error) => console.error('Error:', error) );
swift// iOS - Synchronous method (blocks JS thread!) @objc func getDeviceId() -> String { return UIDevice.current.identifierForVendor?.uuidString ?? "unknown" }
javascript// 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).
Native Module Planning
spec.mdplan.mdtasks.mdTesting Strategy
Documentation
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-06 | pass→pass | 3,741 | 4,731 | +26% | 1 | 1 | 0% | 554 | 4,187 | +656% | 0 | 0 | — |
case-01 | pass→pass | 9,263 | 8,252 | -11% | 1 | 1 | 0% | 1,686 | 4,871 | +189% | 0 | 0 | — |
case-02 | pass→pass | 7,361 | 8,043 | +9% | 1 | 1 | 0% | 1,257 | 4,673 | +272% | 0 | 0 | — |
case-03 | pass→pass | 5,442 | 4,434 | -19% | 1 | 1 | 0% | 869 | 4,006 | +361% | 0 | 0 | — |
case-04 | pass→pass | 7,971 | 5,730 | -28% | 1 | 1 | 0% | 1,520 | 4,479 | +195% | 0 | 0 | — |
case-05 | pass→pass | 12,311 | 9,057 | -26% | 1 | 1 | 0% | 2,261 | 4,982 | +120% | 0 | 0 | — |
case-07 | pass→pass | 9,494 | 8,875 | -7% | 1 | 1 | 0% | 1,819 | 4,852 | +167% | 0 | 0 | — |
case-08 | pass→pass | 5,407 | 4,316 | -20% | 1 | 1 | 0% | 888 | 4,077 | +359% | 0 | 0 | — |
case-09 | fail→fail | 13,649 | 20,857 | +53% | 1 | 1 | 0% | 2,412 | 5,647 | +134% | 0 | 0 | — |
case-10 | pass→pass | 8,076 | 6,350 | -21% | 1 | 1 | 0% | 1,435 | 4,390 | +206% | 0 | 0 | — |
case-11 | pass→pass | 12,533 | 8,591 | -31% | 1 | 1 | 0% | 2,363 | 5,059 | +114% | 0 | 0 | — |
case-12 | pass→pass | 8,572 | 10,496 | +22% | 1 | 1 | 0% | 1,285 | 4,526 | +252% | 0 | 0 | — |
case-13 | pass→pass | 9,805 | 11,548 | +18% | 1 | 1 | 0% | 1,956 | 5,771 | +195% | 0 | 0 | — |
case-14 | pass→pass | 11,339 | 10,885 | -4% | 1 | 1 | 0% | 1,837 | 5,071 | +176% | 0 | 0 | — |
case-15 | fail→pass | 12,263 | 6,917 | -44% | 1 | 1 | 0% | 2,004 | 4,500 | +125% | 0 | 0 | — |
case-16 | pass→pass | 4,532 | 5,433 | +20% | 1 | 1 | 0% | 791 | 4,226 | +434% | 0 | 0 | — |
case-17 | pass→pass | 6,920 | 6,068 | -12% | 1 | 1 | 0% | 1,041 | 4,240 | +307% | 0 | 0 | — |
case-18 | pass→pass | 10,402 | 7,304 | -30% | 1 | 1 | 0% | 1,497 | 4,754 | +218% | 0 | 0 | — |
case-19 | fail→fail | 13,459 | 15,638 | +16% | 1 | 1 | 0% | 2,278 | 5,935 | +161% | 0 | 0 | — |
case-20 | pass→pass | 20,863 | 15,715 | -25% | 1 | 1 | 0% | 4,084 | 6,838 | +67% | 0 | 0 | — |
case-21 | pass→fail | 11,235 | 10,059 | -10% | 1 | 1 | 0% | 2,139 | 5,206 | +143% | 0 | 0 | — |
case-22 | pass→pass | 15,978 | 18,152 | +14% | 1 | 1 | 0% | 3,206 | 7,136 | +123% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted. The headline lift of 0 percentage points is the difference between those two pass rates over the 22 comparable cases. 1 case got worse with the skill loaded, and it is included in that figure.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.