React Native Roadmap 2026-W31

Week of July 27–August 2, 2026

Items This Week

#TitleLabelLink
1Building a Real-Time Face Recognition App with VisionCamera🟦 RNRead
2VisionCamera v5.2 β€” zoom, exposure & torch on SkiaCamera🟦 RNRead
3Sign in with Google for React Native🟦 RNRead
4Octane: React's Programming Model, Compiledβš›οΈ REACTRead
5TanStack stopped using RSC on tanstack.comβš›οΈ REACTRead
6Making Referential Stability a Type with StableRefβš›οΈ REACTRead
7React Navigation 8 / Screens 5 progress🟦 RNRead
8Workers / Worklets multi-threading in React Native🟦 RNRead

5-Day Action Plan

Β 


🟦 Chunk 1 β€” Upgrade VisionCamera to v5.2 with SkiaCamera Controls

Goal: Give users precise camera control (zoom, exposure, torch) rendered directly on a Skia canvas surface, with no native module rebuild required β€” shipped as a one-day dependency bump.

Scope:

  • Bump react-native-vision-camera to ^5.2.0 in package.json
  • Ensure @shopify/react-native-skia is at a compatible version
  • Add zoom, exposure, and torch props to the existing <SkiaCamera> usage
  • Wire a slider UI component to each control prop on the camera screen
  • Smoke-test on iOS Simulator and Android Emulator

Out of scope: Recording or photo capture flow changes, frame processor logic, custom Skia overlays.

Dependencies: Existing VisionCamera v5.x integration; React Native 0.73+.

Acceptance criteria:

  • Users can pinch-to-zoom or use a slider to adjust the zoom level live on the <SkiaCamera> preview
  • An exposure slider shifts the brightness of the Skia camera surface in real time
  • A torch toggle button turns the LED on/off without restarting the camera session
  • No previously working camera features regress (photo, video, existing overlays)

Estimated effort: S

**Copy/paste this prompt:**

Implement the following React Native chunk for your mobile app: Upgrade VisionCamera to v5.2 and expose the new zoom, exposure, and torch controls on <SkiaCamera>.

Goal: Use the new SkiaCamera control props introduced in VisionCamera 5.2 (https://github.com/mrousavy/react-native-vision-camera/releases/tag/v5.2.0) to let users adjust zoom, exposure, and torch from the React Native layer.

Files to create or modify:

  • package.json β€” bump react-native-vision-camera to ^5.2.0
  • screens/CameraScreen.tsx β€” add slider and toggle components wired to camera props
  • components/CameraControls.tsx β€” new component encapsulating the three controls

Step-by-step implementation:

  1. Update dependency: npm install react-native-vision-camera@^5.2.0
  2. Run npx pod-install (iOS) and sync Gradle (Android).
  3. In screens/CameraScreen.tsx, import zoom, exposure, and torch from state:
const [zoom, setZoom] = useState(1);
const [exposure, setExposure] = useState(0);
const [torch, setTorch] = useState<'off' | 'on'>('off');

<SkiaCamera
  device={device}
  isActive
  zoom={zoom}
  exposure={exposure}
  torch={torch}
/>
  1. Create components/CameraControls.tsx with:
    • A Slider (or PanGestureHandler) for zoom (range 1–device.maxZoom)
    • A Slider for exposure (range -1 to +1)
    • A Pressable torch toggle button
  2. Pass onZoomChange, onExposureChange, onTorchToggle callbacks from the screen.
  3. Smoke-test on both platforms.

Acceptance criteria checklist:

  • react-native-vision-camera at 5.2.x in package-lock.json
  • Zoom slider updates the live preview without restarting the camera session
  • Exposure slider visibly shifts preview brightness
  • Torch toggle turns the LED on and off
  • No regression on existing camera capture flow

🟦 Chunk 2 β€” Build On-Device Face Detection Pipeline with VisionCamera Frame Output

Goal: Add real-time face detection to a camera screen entirely on-device, so features like event check-in, attendance, or face-aware overlays work without sending frames to a server.

Scope:

  • Set up useFrameOutput hook with 640Γ—480 YUV resolution and dropFramesWhileBusy: true
  • Integrate a YuNet face detector via a TFLite / LiteRT native binding
  • Draw bounding box overlays over detected faces using @shopify/react-native-skia
  • Enforce try/finally frame.dispose() in the worklet callback to prevent pool exhaustion
  • Document the pipeline stages in a FACE_DETECTION.md file

Out of scope: Face recognition/embedding, anti-spoofing, liveness detection, cloud APIs, full ML pipeline beyond detection.

Dependencies: VisionCamera v5+; @shopify/react-native-skia; a TFLite native binding (e.g. react-native-fast-tflite).

Acceptance criteria:

  • Front camera feed renders real-time bounding boxes around detected faces at β‰₯ 15 FPS on a mid-range device
  • Rapid camera switch does not crash the app (pool exhaustion guard verified)
  • No memory leak: frames in the pool never exceed the camera's buffer count
  • A product stakeholder can see the face-detection UI working on a physical device

Estimated effort: M

**Copy/paste this prompt:**

Implement the following React Native chunk for your mobile app: on-device real-time face detection using VisionCamera Frame Output and Skia overlays (reference: https://blog.margelo.com/on-device-face-recognition-react-native).

Goal: Build the first stage of a face-recognition pipeline β€” face detection β€” running entirely on device, with no server calls.

Files to create or modify:

  • screens/FaceDetectionScreen.tsx β€” main screen with camera + overlay
  • hooks/useFaceDetector.ts β€” worklet-safe frame output hook
  • components/FaceOverlay.tsx β€” Skia canvas drawing bounding boxes
  • FACE_DETECTION.md β€” short explanation of the pipeline stages

Step-by-step implementation:

  1. Install dependencies: npx expo install react-native-vision-camera @shopify/react-native-skia react-native-fast-tflite
  2. Download the YuNet model (face_detection_yunet_2023mar.tflite) and place it in assets/models/.
  3. Create hooks/useFaceDetector.ts:
import { useFrameOutput } from 'react-native-vision-camera';

export function useFaceDetector(onFaces: (boxes: Rect[]) => void) {
  return useFrameOutput({
    targetResolution: { width: 640, height: 480 },
    pixelFormat: 'yuv',
    dropFramesWhileBusy: true,
    onFrame(frame) {
      'worklet';
      try {
        // run YuNet inference here, call onFaces with bounding boxes
      } finally {
        frame.dispose(); // non-negotiable
      }
    },
  });
}
  1. In screens/FaceDetectionScreen.tsx, compose <Camera> with the frame output and a Skia canvas overlay.
  2. In components/FaceOverlay.tsx, use <Canvas> + <Rect> from @shopify/react-native-skia to draw bounding boxes.
  3. Test on a physical device: confirm bounding boxes appear and track faces at β‰₯ 15 FPS.

Acceptance criteria checklist:

  • Front camera renders bounding boxes around faces in real time
  • FPS β‰₯ 15 on a Galaxy S21-class device (visible via DevTools overlay)
  • frame.dispose() is called in finally β€” no pool exhaustion after 30 seconds
  • App does not crash on rapid front/back camera switch
  • FACE_DETECTION.md documents the 5 pipeline stages

🟦 Chunk 3 β€” Add Google Sign-In via @thoughtbot/react-native-social-auth

Goal: Let users authenticate with their Google account on both iOS and Android using a modern, type-safe library with a first-party Expo config plugin β€” replacing any manual OAuth plumbing.

Scope:

  • Install @thoughtbot/react-native-social-auth
  • Configure the Expo config plugin in app.config.ts with iOS and Android client IDs
  • Implement a "Sign in with Google" button and handle the token exchange
  • Store the resulting access token with expo-secure-store
  • Implement sign-out (clear token, navigate to login)

Out of scope: Apple Sign-In, additional social providers, backend token verification endpoint.

Dependencies: Expo project; expo-secure-store; Google Cloud project with OAuth credentials.

Acceptance criteria:

  • Tapping "Sign in with Google" opens the native Google sign-in sheet on both platforms
  • Successful sign-in stores the access token in secure storage
  • Sign-out clears the token and returns the user to the login screen
  • Works on both iOS Simulator (with a Google account) and a physical Android device

Estimated effort: S

**Copy/paste this prompt:**

Implement the following React Native chunk for your mobile app: Google Sign-In using @thoughtbot/react-native-social-auth (reference: https://thoughtbot.com/blog/sign-in-with-google-for-react-native).

Goal: Add a production-ready Google Sign-In flow to the app with minimal boilerplate, using the Expo config plugin to handle native configuration automatically.

Files to create or modify:

  • app.config.ts β€” register the config plugin with OAuth client IDs
  • screens/LoginScreen.tsx β€” add the "Sign in with Google" button
  • hooks/useGoogleAuth.ts β€” encapsulate sign-in, sign-out, and token storage
  • constants/auth.ts β€” export typed auth constants

Step-by-step implementation:

  1. Install: npx expo install @thoughtbot/react-native-social-auth expo-secure-store
  2. In app.config.ts:
plugins: [
  ['@thoughtbot/react-native-social-auth', {
    googleIosClientId: process.env.GOOGLE_IOS_CLIENT_ID,
    googleAndroidClientId: process.env.GOOGLE_ANDROID_CLIENT_ID,
  }]
]
  1. Add the client IDs to .env.local (never commit them).
  2. Create hooks/useGoogleAuth.ts:
import { signInWithGoogle } from '@thoughtbot/react-native-social-auth';
import * as SecureStore from 'expo-secure-store';

export function useGoogleAuth() {
  const signIn = async () => {
    const result = await signInWithGoogle();
    if (result.type === 'success') {
      await SecureStore.setItemAsync('accessToken', result.accessToken);
    }
    return result;
  };
  const signOut = async () => {
    await SecureStore.deleteItemAsync('accessToken');
  };
  return { signIn, signOut };
}
  1. In screens/LoginScreen.tsx, add a <Pressable> that calls signIn().
  2. Run npx expo prebuild then test on simulator and Android device.

Acceptance criteria checklist:

  • Native Google sign-in sheet appears on iOS and Android
  • Access token is stored in expo-secure-store after successful sign-in
  • Sign-out removes the token and navigates back to the login screen
  • No OAuth client IDs are committed to the repository
  • Works on both iOS Simulator and a physical Android device

🟦 Chunk 4 β€” Migrate to React Navigation 8 + Screens 5

Goal: Adopt the latest React Navigation 8 API and react-native-screens v5 to benefit from improved native animations, the new usePreventRemove hook, and reduced bundle size β€” as highlighted in TWIR #292.

Scope:

  • Bump @react-navigation/native, @react-navigation/native-stack, and react-native-screens to their v8/v5 releases
  • Resolve any breaking API changes (renamed props, removed options, updated hook signatures)
  • Replace any deprecated navigation.dangerouslyGetParent() calls with the new useNavigation patterns
  • Update the root NavigationContainer if the new version requires config changes
  • Run the full E2E test suite to confirm no navigation regressions

Out of scope: Migrating to Expo Router, adding new navigation stacks, redesigning the navigation structure.

Dependencies: Existing React Navigation v7 setup; react-native-screens v4; React Native 0.73+.

Acceptance criteria:

  • All screens navigate without crashes or visual regressions on iOS and Android
  • Deprecated APIs replaced (TypeScript compiler reports zero navigation-related type errors)
  • Back gesture and swipe animations work correctly on both platforms
  • E2E tests pass after the upgrade

Estimated effort: M

**Copy/paste this prompt:**

Implement the following React Native chunk for your mobile app: migrate from React Navigation 7 to React Navigation 8 + react-native-screens v5 (reference: https://thisweekinreact.com/newsletter/292).

Goal: Upgrade the navigation stack to React Navigation 8 and Screens 5, resolving all breaking changes so the app ships on the latest stable navigation infrastructure.

Files to create or modify:

  • package.json β€” bump navigation packages
  • App.tsx / app/_layout.tsx β€” update NavigationContainer if needed
  • Any screen file using deprecated APIs (search for dangerouslyGetParent, addListener('focus', ...) patterns)
  • __tests__/navigation.test.ts β€” update snapshots if needed

Step-by-step implementation:

  1. Upgrade packages:
npm install @react-navigation/native@^8.0.0 \
  @react-navigation/native-stack@^8.0.0 \
  react-native-screens@^5.0.0 \
  react-native-safe-area-context@latest
  1. Run npx pod-install and sync Gradle.
  2. Check the React Navigation v8 migration guide for breaking changes.
  3. Fix TypeScript errors: run npx tsc --noEmit and resolve each error.
  4. Search for dangerouslyGetParent and replace with useNavigation + useRoute patterns.
  5. Run npx jest β€” fix any failing navigation snapshot tests.
  6. Test back gesture on iOS physical device and Android emulator.

Acceptance criteria checklist:

  • All navigation packages at v8/v5 in package-lock.json
  • Zero TypeScript errors related to navigation types
  • npx jest passes with no navigation test failures
  • Back swipe gesture works on iOS and Android after upgrade
  • No visual regression on any existing screen transition

βš›οΈ Chunk 5 β€” Enforce Referential Stability with the StableRef Type Pattern

Goal: Prevent subtle performance regressions caused by unstable callbacks and objects being passed as props, by encoding memoization requirements directly in the TypeScript type system with Stable<T> β€” making broken memoization a compile-time error rather than a runtime mystery.

Scope:

  • Install stableref library
  • Annotate the 5 most performance-critical component prop types with Stable<T> (typically callbacks in list items, shared context values, and frequently re-rendered form handlers)
  • Fix any type errors surfaced by the new annotations (wrap values with useCallback/useMemo)
  • Add ESLint react-hooks/exhaustive-deps rule to CI to catch future violations
  • Document the pattern in a PERFORMANCE.md file

Out of scope: Full codebase annotation, React Compiler adoption, profiling tooling setup.

Dependencies: TypeScript 5+; React 18+; existing ESLint setup.

Acceptance criteria:

  • At least 5 component props annotated with Stable<T> in production components
  • TypeScript build fails if an unstable value (non-memoized function/object) is passed where Stable<T> is required
  • All existing unit and integration tests pass after the refactor
  • PERFORMANCE.md documents the pattern with a before/after code example

Estimated effort: S

**Copy/paste this prompt:**

Implement the following React Native chunk for your mobile app: enforce memoized props using the StableRef type pattern (reference: https://www.jovidecroock.com/blog/referential-stability-types/).

Goal: Use stableref/react to tag memoized callbacks and objects with Stable<T> in the TypeScript type system, turning unmemoized prop bugs into compile-time errors.

Files to create or modify:

  • package.json β€” add stableref dependency
  • components/VirtualizedList.tsx β€” annotate renderItem, keyExtractor, onEndReached as Stable<T>
  • contexts/AuthContext.tsx β€” annotate context value callbacks as Stable<T>
  • components/FormField.tsx β€” annotate onChange, onBlur as Stable<T>
  • PERFORMANCE.md β€” document the pattern
  • .eslintrc.js β€” ensure react-hooks/exhaustive-deps is set to error

Step-by-step implementation:

  1. Install: npm install stableref
  2. In components/VirtualizedList.tsx:
import type { Stable } from 'stableref';

type Props = {
  renderItem: Stable<(item: Item) => ReactElement>;
  keyExtractor: Stable<(item: Item) => string>;
  onEndReached: Stable<() => void>;
};
  1. Run npx tsc --noEmit β€” fix every location where an unstable value is passed by wrapping with useCallback or useMemo.
  2. Repeat for AuthContext.tsx and FormField.tsx.
  3. In .eslintrc.js, set 'react-hooks/exhaustive-deps': 'error'.
  4. Write PERFORMANCE.md with a before/after example showing how Stable<T> catches the bug at compile time.

Acceptance criteria checklist:

  • stableref installed and imported in at least 3 component files
  • npx tsc --noEmit passes with zero type errors
  • Passing a plain (non-memoized) function to a Stable<T> prop fails the TypeScript build
  • All existing tests pass after wrapping values with useCallback/useMemo
  • PERFORMANCE.md exists with a clear before/after example