React Native Roadmap 2026-W30

Week of July 20–July 26, 2026

Items This Week

#TitleLabelLink
1Install dev and production side by side with app variants🟧 EXPORead
2How Posh went from manual weekly mobile releases to continuous delivery with Expo🟧 EXPORead
3From Expo Router to Detour: Deferred deep linking the right way🟧 EXPORead
4EAS Observe moves to general availability on August 20🟧 EXPORead
5React 19.2.8 / 19.1.9 / 19.0.8 Released — DoS security fix⚛️ REACTRead
6shadcn/ui Adds React Aria as a Component Base⚛️ REACTRead
7Maestro MCP: Let Your Agent Use the App It Just Built🟦 RNRead

5-Day Action Plan

 


🟧 Chunk 1 — Configure Expo App Variants for Dev and Production Side by Side

Goal: Allow developers and testers to have both the development and production builds installed simultaneously on the same device, eliminating the need to uninstall and reinstall when switching environments.

Scope:

  • Convert app.json to app.config.js to enable dynamic configuration
  • Read EXPO_PUBLIC_APP_VARIANT environment variable to switch between development and production
  • Assign a unique bundleIdentifier / android.package per variant (e.g. com.myapp.dev)
  • Give each variant a distinct display name (e.g. "MyApp (Dev)") and icon with a visual marker
  • Update eas.json build profiles to inject the correct EXPO_PUBLIC_APP_VARIANT env variable
  • Verify both variants install side-by-side on a physical device or simulator

Out of scope: Staging / QA environment variants, EAS Submit configuration, CI pipeline changes.

Dependencies: Expo SDK 53+; EAS Build account.

Acceptance criteria:

  • Two separate app icons appear on the home screen with distinct names and icons
  • Each variant resolves to the correct API base URL (dev vs prod)
  • Running eas build --profile development and eas build --profile production produce different bundle identifiers

Estimated effort: S

**Copy/paste this prompt:**

Implement the following React Native chunk for your mobile app: Configure Expo app variants so dev and production builds can be installed side by side on the same device.

Goal: Use Expo's app variants pattern (https://expo.dev/blog/app-variants-side-by-side) to allow simultaneous installation of development and production builds.

Files to create or modify:

  • app.config.js — convert from app.json to dynamic config, read process.env.EXPO_PUBLIC_APP_VARIANT
  • eas.json — add env block to each build profile setting EXPO_PUBLIC_APP_VARIANT
  • assets/icon-dev.png — duplicate of the app icon with a visual DEV badge overlay
  • constants/config.ts — export APP_VARIANT and API_BASE_URL derived from the env variable

Step-by-step implementation:

  1. Rename app.json to app.config.js and export a function: export default ({ config }) => ({ ...config, ... }).
  2. At the top, read: const variant = process.env.EXPO_PUBLIC_APP_VARIANT ?? 'production';.
  3. Return variant-specific values:
    • name: variant === 'development' ? 'MyApp (Dev)' : 'MyApp'
    • ios.bundleIdentifier: variant === 'development' ? 'com.myapp.dev' : 'com.myapp'
    • android.package: same pattern
    • icon: variant === 'development' ? './assets/icon-dev.png' : './assets/icon.png'
  4. In eas.json, under build.development.env set EXPO_PUBLIC_APP_VARIANT=development; under build.production.env set EXPO_PUBLIC_APP_VARIANT=production.
  5. In constants/config.ts, export API_BASE_URL = variant === 'development' ? 'https://api.dev.myapp.com' : 'https://api.myapp.com'.
  6. Build both: eas build --profile development --platform ios and eas build --profile production --platform ios.
  7. Install both on the same simulator and verify two distinct icons appear.

Acceptance criteria checklist:

  • Two apps install without overwriting each other on the same device
  • Dev app shows a visual indicator (different icon or "(Dev)" name suffix)
  • Constants.expoConfig.ios.bundleIdentifier returns different values per build
  • API base URL resolves to the correct endpoint at runtime in each variant

🟧 Chunk 2 — Implement Continuous Delivery with EAS Workflows and Expo Updates

Goal: Eliminate manual release steps by automating native builds and OTA updates so every merged PR can ship to users without human intervention — the same pipeline Posh used to go from weekly manual releases to continuous delivery.

Scope:

  • Install and configure expo-updates with channel-based routing
  • Write an EAS Workflow YAML triggered on push to main
  • Add a fingerprint check step: only trigger a full native build when native code changes; otherwise publish an OTA update
  • Configure Slack or email notifications on build failure

Out of scope: App Store submission automation, rollback mechanisms, A/B testing OTA channels.

Dependencies: EAS Build account; expo-updates installed; GitHub (or equivalent CI) connected to EAS.

Acceptance criteria:

  • Merging a JS-only change to main publishes an OTA update without triggering a native build
  • Merging a native-layer change (e.g. new package with native code) triggers a full EAS Build automatically
  • A stakeholder can confirm the update is live on their device within 10 minutes of the merge

Estimated effort: M

**Copy/paste this prompt:**

Implement the following React Native chunk for your mobile app: Automate the release pipeline with EAS Workflows and Expo Updates for continuous delivery (inspired by https://expo.dev/blog/posh-manual-weekly-releases-to-continuous-delivery-with-expo).

Goal: Configure a CI/CD pipeline where JS-only changes ship as instant OTA updates and native changes trigger full EAS Builds — zero manual steps post-merge.

Files to create or modify:

  • .eas/workflows/deploy.yml — new EAS Workflow definition
  • eas.json — ensure channel is configured per build profile
  • app.config.js — ensure expo.updates.url is set correctly

Step-by-step implementation:

  1. Install expo-updates: npx expo install expo-updates.
  2. Run eas update:configure to generate channel configuration.
  3. In eas.json, set "channel": "production" under build.production.
  4. Create .eas/workflows/deploy.yml:
on:
  push:
    branches: [main]
jobs:
  deploy:
    steps:
      - uses: fingerprint
      - if: fingerprint.changed
        run: eas build --profile production --non-interactive
      - if: "!fingerprint.changed"
        run: eas update --branch production --message "OTA ${{ github.sha }}"
  1. In the EAS dashboard under Notifications, configure a Slack webhook for build failures.
  2. Test: merge a one-line JS change → confirm OTA publishes. Merge a new native dependency → confirm full build triggers.

Acceptance criteria checklist:

  • OTA update is live within 10 min of a JS-only merge to main
  • Fingerprint change triggers a full EAS Build automatically
  • Build failure sends a Slack / email notification without any manual check
  • No developer action required post-merge for either path

🟧 Chunk 3 — Implement Deferred Deep Linking with Expo Router

Goal: Ensure users who tap a deep link before installing the app are routed to the correct in-app screen immediately after install completes — reducing drop-off on acquisition campaigns and share flows.

Scope:

  • Capture the initial URL on first launch using expo-linking's getInitialURL()
  • Persist the pending URL in AsyncStorage when no matching screen is rendered yet
  • After authentication or onboarding completes, consume the pending URL and navigate to the target route
  • Clear the pending URL from storage after a single use
  • Write a unit test for the store → navigate → clear lifecycle

Out of scope: Branch.io / AppsFlyer / Firebase Dynamic Links integration, App Clips, Smart App Banners.

Dependencies: Expo Router v4+; expo-linking; @react-native-async-storage/async-storage.

Acceptance criteria:

  • Tapping a deep link on a device without the app installed stores the URL on first launch
  • After install and onboarding, the user lands on the correct screen automatically
  • The deferred URL is cleared from storage after navigation (no infinite redirect loop)
  • A unit test verifies the store → navigate → clear flow independently of the UI

Estimated effort: M

**Copy/paste this prompt:**

Implement the following React Native chunk for your mobile app: Deferred deep linking with Expo Router (https://expo.dev/blog/deferred-deep-linking-the-right-way).

Goal: Route users to the correct screen after install when they tapped a deep link before the app was installed.

Files to create or modify:

  • hooks/useDeferredLink.ts — new hook: captures initial URL → stores in AsyncStorage → navigates post-auth
  • app/_layout.tsx — call useDeferredLink after auth state resolves
  • __tests__/useDeferredLink.test.ts — unit tests for store / navigate / clear cycle

Step-by-step implementation:

  1. npx expo install expo-linking @react-native-async-storage/async-storage
  2. Create hooks/useDeferredLink.ts:
import * as Linking from 'expo-linking';
import AsyncStorage from '@react-native-async-storage/async-storage';
import { useRouter } from 'expo-router';

const KEY = 'pendingDeepLink';

export function useDeferredLink(isAuthenticated: boolean) {
  const router = useRouter();

  useEffect(() => {
    Linking.getInitialURL().then(url => {
      if (url) AsyncStorage.setItem(KEY, url);
    });
  }, []);

  useEffect(() => {
    if (!isAuthenticated) return;
    AsyncStorage.getItem(KEY).then(url => {
      if (!url) return;
      AsyncStorage.removeItem(KEY);
      const { path } = Linking.parse(url);
      if (path) router.replace(`/${path}`);
    });
  }, [isAuthenticated]);
}
  1. In app/_layout.tsx, call useDeferredLink(isAuthenticated) after the auth check resolves.
  2. Write tests mocking Linking.getInitialURL returning a URL, verifying AsyncStorage.setItem is called, then simulating isAuthenticated = true and verifying router.replace is called and the key is removed.
  3. Test manually: paste a deep link into Safari on iOS Simulator before launching the app, launch the app, complete onboarding, and confirm you land on the correct screen.

Acceptance criteria checklist:

  • Deep link URL is persisted across fresh install
  • User lands on the correct screen after completing onboarding/auth
  • Pending URL is removed from AsyncStorage after navigation
  • Unit tests pass for store, navigate, and clear scenarios

🟧 Chunk 4 — Set Up EAS Observe Before General Availability (August 20)

Goal: Enable EAS Observe on your production channel now — before its August 20 GA date — to start capturing real-world crash signals, session data, and performance regressions from production users.

Scope:

  • Enable EAS Observe in the Expo dashboard for the production channel
  • Configure at least one alert rule (JS crash rate threshold)
  • Trigger a test event to confirm data flows into the Observe dashboard
  • Verify a non-technical stakeholder can read session traces without engineering support

Out of scope: Custom dashboards, third-party monitoring integrations (Sentry, Datadog), non-production channel configuration.

Dependencies: EAS account with active production builds; Expo SDK 57 recommended; expo-updates configured.

Acceptance criteria:

  • EAS Observe dashboard shows live session data after a production build is run
  • At least one alert rule is active and fires on a test crash event
  • A stakeholder can navigate to the dashboard and read a session trace independently

Estimated effort: S

**Copy/paste this prompt:**

Implement the following React Native chunk for your mobile app: Enable EAS Observe for production monitoring before its August 20, 2026 GA date (https://expo.dev/changelog/eas-observe-moves-to-general-availability-on-august-20).

Goal: Start capturing production crash and performance data in EAS Observe to catch regressions before users report them.

Files to create or modify:

  • eas.json — confirm "channel": "production" is set in the production build profile
  • app.config.js — ensure expo.updates.url is configured (required for Observe)

Step-by-step implementation:

  1. Open the Expo dashboard → your project → Observe tab.
  2. Click Enable on the production channel.
  3. In eas.json, confirm your production profile has "channel": "production" under updates.
  4. Create an alert rule: Alerts → New Rule → JS Crash Rate > 1% → Notify via email/Slack.
  5. In a dev build, add a temporary throw new Error('test-observe-crash') behind a hidden gesture (e.g. triple-tap), trigger it, and verify the event appears in the Observe timeline within 60 seconds.
  6. Remove the test crash.
  7. Share the dashboard link with a non-technical stakeholder and confirm they can read the session data.

Acceptance criteria checklist:

  • EAS Observe is enabled on the production channel
  • Live session data appears in the dashboard within 60 s of app launch
  • Test crash event is visible in the Observe timeline
  • Alert rule is active and confirmed to notify on threshold breach
  • Non-engineer stakeholder can read session traces without assistance

⚛️ Chunk 5 — Upgrade to React 19.2.8 and Audit Server Action Security

Goal: Apply the React 19.2.8 security patch (fixes a DoS vulnerability on server function endpoints) and audit any exposed server actions in the project to prevent maliciously crafted requests from crashing the server.

Scope:

  • Bump react, react-dom, and @types/react to 19.2.8 in package.json
  • Run the test suite to confirm no regressions
  • Audit all server actions / API route handlers for missing input validation
  • Add zod schema validation to at least one unvalidated server action as a reference implementation

Out of scope: Migrating from React 18 to 19, refactoring existing state management, Next.js or Remix-specific changes beyond the patch.

Dependencies: React 19.x already in use; existing test suite; zod (or equivalent validation library).

Acceptance criteria:

  • react, react-dom, and @types/react are all at version 19.2.8 (or ≥ 19.1.9 / ≥ 19.0.8 for older lines)
  • All existing tests pass after the upgrade
  • At least one server action validates its input with a schema before processing
  • A stakeholder can confirm no new crashes appear in monitoring within 24 hours of deploy

Estimated effort: S

**Copy/paste this prompt:**

Implement the following React Native chunk for your mobile app: Upgrade to React 19.2.8 and add server action input validation to patch the DoS vulnerability (https://react.statuscode.com/issues/484).

Goal: Apply the security patch for the DoS vulnerability in React 19 server function endpoints and harden server actions against maliciously crafted requests.

Files to create or modify:

  • package.json — bump react, react-dom, @types/react to 19.2.8
  • app/actions/exampleAction.ts — add zod schema validation as a reference implementation

Step-by-step implementation:

  1. Update dependencies:
npm install react@19.2.8 react-dom@19.2.8 @types/react@19.2.8
  1. Run npm test and fix any breaking changes (typically none for a patch release).
  2. Install zod if not present: npm install zod.
  3. In each server action file, add input validation:
import { z } from 'zod';

const schema = z.object({
  email: z.string().email(),
  message: z.string().max(1000),
});

export async function submitContactForm(input: unknown) {
  const data = schema.parse(input); // throws ZodError on invalid input
  // ... rest of the action
}
  1. Wrap the schema.parse call in a try/catch and return a structured error response instead of letting it propagate.
  2. Deploy and monitor error rates for 24 hours.

Acceptance criteria checklist:

  • react and react-dom are at 19.2.8 in package-lock.json
  • All existing tests pass after the upgrade
  • At least one server action validates input with a zod schema
  • Invalid input returns a structured error, not a 500 crash
  • No new errors appear in monitoring within 24 h of deploy