React Native Flow

← Retour au blog

March 14, 2026·10 min read

Deep links with Expo Router: a mental map

Deep linking is three problems: OS opens your app, your router picks a screen, your screen reads params. Expo Router collapses some of that—until marketing sends a URL with UTM junk you still have to parse. Separate “routing concerns” from “analytics junk” and you will stop fighting one-off URL hacks every launch.

Schemes vs universal links

Custom schemes are easy to test; universal links need hosted association files and correct entitlements. Pick based on email/SMS use cases, not just dev convenience.

Staging and prod should use different hosts or path prefixes so you don’t open prod from a QA link.

Dynamic segments

File names with `[id]` map cleanly—validate ids before hitting the API. Reject garbage early with a friendly error screen instead of a spinner forever.

Preserve query strings your analytics team cares about without polluting your navigation state.

Cold start vs warm

Cold start navigation can race with auth hydration. Gate the initial route until you know if the user is signed in, or you’ll flash the wrong stack.

Log deep link failures in a structured way; they’re impossible to reproduce from screenshots alone.

Playbook for the next “link is broken” report

Capture the full URL, OS version, and whether the app was killed or backgrounded. Reproduce with `npx uri-scheme` or platform tools before you change router code.

Keep a living doc: which domains you own, where association files live, and who updates them when IT rotates certs. Most “random” failures are hosting or entitlement drift, not JavaScript.

Universal links and team responsibilities

Universal links require hosted association files, correct entitlements, and DNS control—often split across mobile, web, and infra teams. Document owners and renewal dates for certs and domains. Staging environments should mirror production paths with obvious host prefixes to avoid accidental cross-environment opens. Marketing campaigns frequently append query strings; decide which params the app consumes versus ignores, and strip sensitive ones from logs. Align UTM handling with analytics pipelines so you do not double-count. When links fail, users blame the app—have support runbooks with test URLs and expected routes.

Custom URL schemes versus HTTPS

Custom schemes are easy for dev testing but rough for email clients and security scanners. Prefer HTTPS universal links for user-facing shares. Keep custom schemes as fallbacks during transitions, but plan deprecation once universal links stabilize. Test on devices without your app installed to ensure web fallbacks work—Expo Router can map web routes separately. Document differences between Android App Links and iOS universal links verification steps; they are not interchangeable checklists.

Dynamic segments, query params, and validation

Treat dynamic segments as untrusted input. Validate ids before fetching; handle 404-style empty states gracefully. For query params, parse defensively—types may arrive as strings even when numeric. Reject unexpected combinations early with user-friendly errors rather than infinite spinners. Preserve params your analytics needs without stuffing them into navigation state unnecessarily—separate analytics side effects via a listener layer. For auth-gated content, ensure deep links do not leak existence of resources to logged-out users if that matters to your threat model.

Cold start, splash, and navigation readiness

Deep links on cold start race with splash hiding, font loading, and session restoration. Gate navigation until you know auth state; avoid flashing private stacks. If using OTA updates, ensure deep link handling respects runtime version constraints. Time-bound slow operations with fallbacks—better a polite error than a stuck splash. Log timing metrics for deep link resolution to catch regressions when app complexity grows.

Testing deep links effectively

Maintain a spreadsheet of canonical URLs per environment, expected screens, and required auth. Automate opening links via `npx uri-scheme` or platform tools in CI smoke tests where feasible. Test from email, SMS, notes, and browser—referrer policies differ. After OS upgrades, re-verify association files; Apple and Google occasionally tighten validation. Include negative tests: malformed ids, tampered signatures if applicable, revoked sessions.

Expo Router specifics to remember

File-based routing maps paths to modules—keep route components thin. Use layouts for shared chrome and error boundaries for segment failures. Group routes with parentheses for organization without affecting URLs. Watch for duplicate route names during refactors; Expo’s diagnostics help but do not replace careful review. When using server components patterns from web, remember React Native constraints differ—stick to RN-compatible data loading patterns.

Operational playbook

When links break in production, verify association files, entitlements, app version distribution, and whether marketing used a new domain without updating files. Roll forward fixes quickly; deep link outages directly impact campaigns. Post-incident, add monitoring on association file HTTP status codes. Educate stakeholders that deep linking is a system, not a mobile-only toggle.

Shipping and reliability habits (1)

Images and maps look simple in mocks and expensive in production. Decode sizes should match display sizes; static map thumbnails are not interchangeable with interactive MapView gestures. Quotas, API keys, and offline behavior need explicit fallbacks—addresses as text, retry buttons, and calm error copy. Monitor vendor dashboards for spikes that indicate bugs or abuse.

Error boundaries catch render failures, not native crashes or async mistakes. Pair them with platform crash reporting and structured client logs. Fallback UI should include build identifiers and humane copy—never raw stack traces for end users. Test fallbacks with screen readers; a broken error screen is still broken UX.

Platform differences worth rehearsing (2)

FlatList performance is configuration as much as code. Stable keys, reasonable `windowSize`, and memoized rows beat switching to a different list primitive blindly. Nested virtualized lists are a last resort—redesign first. Profile with production-like data volumes; dev placeholders lie.

Splash screens and launch gates should reflect honest readiness: fonts, theme, session, critical remote config—not every SDK under the sun. Infinite splash is worse than a slightly longer branded hold. Match native and JS background colors to avoid flashes; respect reduced motion for animations.

Security, privacy, and data handling (3)

Analytics schema governance prevents warehouse disasters: version events, avoid high-cardinality strings, and align names across iOS, Android, and web. Consent gating must stop network calls, not just UI. Separate dev and prod projects to avoid polluting dashboards.

Testing onboarding changes with funnel metrics beats debating opinions. Segment by acquisition channel and platform; back behavior differs. Skip paths must be genuine—dark patterns may win short metrics and destroy brand trust. Localization length tests prevent clipped CTAs in verbose languages.

Performance and measurement discipline (4)

Helper modules concentrate glue code—storage, navigation, permissions—so screens stay readable. Split helpers by topic before files become merge-conflict magnets, and document each module’s contract. Good helpers answer ‘where do we save tokens?’ in one glance—not ‘ask Sarah.’

Shipping React Native features is less about any single API and more about the system around it: typed boundaries, predictable navigation, and telemetry that tells you what broke in production. Prefer boring, explicit modules over clever metaprogramming that the next hire cannot grep. When platform vendors change behavior in point releases, your defense is automated smoke tests on real devices and a short internal changelog of native assumptions you rely on.

Team process and long-term maintenance (5)

Deep links are a cross-team system: marketing URLs, hosted association files, entitlements, router params, and analytics query preservation. Debug with structured logging of raw URLs (scrub secrets) and reproduce cold-start races with auth hydration. Staging and production should be obviously separated—accidentally opening prod from a QA link erodes trust and pollutes data.

Push notifications walk a line between helpful and intrusive. Prime users with context, respect notification channels on Android, and measure opt-outs after campaigns—spikes mean copy or frequency problems. Payload design affects background behavior; test killed and locked-device states. Tokens belong server-side with rotation strategies; never treat the client as authoritative for subscription state.

Shipping and reliability habits (6)

Keyboard and form UX separate polished apps from ‘works on desktop simulators.’ Platform differences in soft input modes matter; test smallest phones and Android gesture navigation. Primary actions must remain reachable when the keyboard is visible—scroll containers and keyboard controllers exist because this problem is universal.

Type-safe navigation pays off when routes multiply. Keep param lists near navigators, validate external URLs, and avoid serializing non-JSON-safe values through params. Renaming routes is a cross-cutting change—update analytics, push payloads, and E2E selectors in the same release train.

Platform differences worth rehearsing (7)

Hermes versus JSC is not a lifestyle choice—profile your app. Hermes usually wins on startup; some libraries still assume JSC quirks. Engine toggles are not substitutes for fixing quadratic renders in your own code. Upgrade notes matter: Intl support and debugging tooling evolve.

Reanimated and gesture libraries earn their place when profiling proves UI-thread work and your team can maintain native upgrades. Worklets have constraints—read errors carefully. Respect reduced motion and test Android timing differences—identical JS does not guarantee identical feel.

Security, privacy, and data handling (8)

Environment variables should be classified: public-by-design, sensitive-with-mitigations, or never-on-device. `EXPO_PUBLIC_` values are extractable—treat them that way. Align env handling across EAS profiles and local dev; fail fast when keys are missing instead of shipping undefined behavior.

Project structure should make ownership obvious: routes as backbone, feature folders for product areas, thin screens, and shared infrastructure that is deliberately named. Refactor in vertical slices with device-tested releases—big-bang rewrites without tests are how teams lose weeks.

Performance and measurement discipline (9)

Monorepos amplify both leverage and failure modes: duplicate React versions cause mysterious hook errors, and Metro misconfiguration blocks local packages from resolving. Invest in workspace discipline—single React version, documented `watchFolders`, and lint rules preventing packages from importing app navigators accidentally. CI must mirror local installs; ‘works on my laptop’ with different package managers is a time bomb.

Native modules are product decisions disguised as engineering tasks. You inherit Xcode and Gradle upgrades, store review scrutiny, and security obligations. Prefer maintained Expo modules and config plugins before writing JNI or Swift glue from scratch. When you must go native, budget pairing time with platform specialists and write runbooks for on-call—crashes in native code bypass many JS safeguards.

Team process and long-term maintenance (10)

E2E tests should protect revenue paths, not every permutation. Stable selectors (`testID`) beat text that marketing rewrites weekly. Flake management is a feature: quarantine, fix root causes, and keep smoke suites green on CI devices. Five reliable tests beat fifty flaky ones that everyone ignores.

OTA updates are powerful and risky: runtime compatibility, rollback plans, and user-visible behavior changes need governance. Channels should map to release maturity—staging versus production—with access controls on publish credentials. Large assets over cellular need care; silent failures erode trust more than a frank ‘update failed, retry’ message.

Shipping and reliability habits (11)

Metro cache issues masquerade as logic bugs. Establish a documented reset ladder: dev server restart, cache flags, Watchman, derived data, then dependency reinstalls. Compare platforms when only one breaks—native steps diverge. Keep CI caches deterministic with lockfiles and pinned toolchains.

Environment variables should be classified: public-by-design, sensitive-with-mitigations, or never-on-device. `EXPO_PUBLIC_` values are extractable—treat them that way. Align env handling across EAS profiles and local dev; fail fast when keys are missing instead of shipping undefined behavior.

Platform differences worth rehearsing (12)

Security and privacy expectations move faster than roadmaps. Treat analytics, crash, and attribution SDKs as part of your threat model: initialize them deliberately, document data flows, and verify ‘off’ truly stops network calls. Client-side secrets are public secrets—anything shipped in an APK or IPA should be assumed extractable. Pair mobile changes with backend policies so authorization remains consistent across platforms.

Accessibility is compatibility. Labels, focus order, and dynamic type are not polish—they determine whether users can complete tasks at all. Test with VoiceOver and TalkBack on hardware; simulators miss focus bugs. When designs prioritize minimalism, negotiate text alternatives for icon-only controls. Accessibility regressions often follow navigation redesigns—add checklist items to those PRs specifically.

Security, privacy, and data handling (13)

Push notifications walk a line between helpful and intrusive. Prime users with context, respect notification channels on Android, and measure opt-outs after campaigns—spikes mean copy or frequency problems. Payload design affects background behavior; test killed and locked-device states. Tokens belong server-side with rotation strategies; never treat the client as authoritative for subscription state.

Images and maps look simple in mocks and expensive in production. Decode sizes should match display sizes; static map thumbnails are not interchangeable with interactive MapView gestures. Quotas, API keys, and offline behavior need explicit fallbacks—addresses as text, retry buttons, and calm error copy. Monitor vendor dashboards for spikes that indicate bugs or abuse.

Sponsorisé

Promo rapide