---
name: native-integration
description: Review React Native native-module, permission, and background-task work. Use when integrating platform APIs, camera, biometrics, files, or reviewing a bridge or Turbo Module boundary.
version: 1.1.0
platforms: [ ios, android ]
react-native-version: 0.76+
tags: [ react-native, native, permissions, turbo-modules ]
title: native-integration
canonical_url: https://skilld.dev/gh/neha/rn-developer-skills/native-integration
last_updated: 2026-10-01T00:04:32.000Z
---

> **Skill from skilld.dev.** Follow the instructions below for this session. You do not need to install anything.
>
> If the user asked to install this Skill, run `npx skilld install neha/rn-developer-skills/native-integration`. Install writes the Skill files into the project, so every session loads them.

# Native Integration Skill

## Applicability

- **Platforms:** iOS and Android
- **React Native:** 0.76+ (New Architecture interop assumed unless a checklist item says otherwise)

## When to Use

- Integrating a native module, Turbo Module, or platform API
- Requesting permissions, or handling camera, biometrics, files, or location
- Adding a background task or a native listener
- Reviewing a change that crosses the JavaScript and native boundary

## Severity

- **Merge-blocking:** a permission prompt this diff adds loops after denial, or a listener this diff adds is not removed on unmount.
- **Should-fix:** usage-string wording and when the prompt appears, as long as denial does not crash.

## Guidance

### Permissions

This skill owns when the prompt is shown and what denial looks like. Analytics consent is owned by [security](../security/SKILL.md).

- [ ] Permission is requested at the moment of use, not on app launch or screen mount
- [ ] Denied, restricted, and revoked states have a path that does not crash or loop the prompt
- [ ] The user can continue without the permission, or is told what is blocked and how to enable it in Settings
- [ ] iOS usage strings explain the specific purpose; Android declares only the permissions the feature uses

**Incorrect:**
```tsx
useEffect(() => {
  requestCameraPermission();
}, []);
```

**Correct:**
```tsx
async function onTakePhoto() {
  const status = await getCameraPermission();
  if (status === 'denied') {
    const next = await requestCameraPermission();
    if (next !== 'granted') {
      setNeedsSettings(true);
      return;
    }
  }
  if (status === 'blocked') {
    setNeedsSettings(true);
    return;
  }
  await openCamera();
}
```

### Native boundary

- [ ] The JavaScript API returns a result or a typed error; it does not throw an unstructured native exception into the screen
- [ ] A missing or old native implementation has a fallback (hide the feature, or use a supported path)
- [ ] Values crossing the bridge are serialisable; functions, class instances, and cyclic objects are not passed through
- [ ] Platform differences are explicit (`Platform.OS` or separate files), not assumed to match
- [ ] New Architecture interop is used when the app is on the New Architecture; the old bridge is not added for a new module without a reason

### Lifecycle

- [ ] Native listeners, sensors, and subscriptions are removed on unmount
- [ ] Background work respects OS limits (time, network, and user-visible purpose) and can be cancelled
- [ ] Work that must finish (upload, payment) is confirmed on the server, not only by a native callback
- [ ] Native UI that presents a modal or activity restores the React Native screen when it dismisses

## Anti-Patterns

| Anti-Pattern | Risk | Fix |
|---|---|---|
| Requesting permission in `useEffect` on mount | Prompt before the user understands why; denial with no recovery | Request from the action that needs it, and handle denial |
| Treating "granted once" as permanent | Revoked or limited permission crashes the next call | Check status before every use |
| Passing a JS callback object into native and never removing it | Leak and calls after unmount | Subscribe with a cleanup function |
| Swallowing native errors as `catch {}` | The screen looks idle while the feature failed | Surface a typed error and a retry or fallback |

## Pitfalls

- Android may deliver a permission result after the activity restarts. Do not assume the component that called `request` is still mounted.
- iOS limited photo access is not the same as full access. Handle the limited set instead of treating it as denied or granted.
- Biometric success proves the device unlocked a key; it does not by itself prove the server accepted the user. Confirm the session server-side.
- A native module that touches UI or sensors must run on the platform's main thread. A background-thread call can crash or silently drop the event.
- File URIs from the camera or picker are often temporary. Copy what you must keep, and do not log the path if it contains user content.
