React Native Flow

← Back to the blog

March 26, 2026·10 min read

Analytics events: useful data, not creepy data

Every `track()` call is a privacy and performance decision. Name events like an API, forbid raw emails in payloads, and batch when the vendor allows. Future-you will query these tables—future legal will read them too.

Schema discipline

Consistent `snake_case` or `camelCase`—pick one. Document required properties. Version events when meaning changes.

Avoid high-cardinality strings that explode warehouse costs.

PII

Hash or omit identifiers you don’t need. If marketing wants cohorts, use server-side joins when possible.

Honor opt-out flags before initializing SDKs.

Debug vs prod

Disable verbose logging in production builds. Sample events in dev if consoles get noisy.

Test that consent gating actually prevents network calls, not just UI.

A minimal analytics spec worth writing down

One page: allowed event names, required properties, and explicitly banned fields (email, phone, precise location without consent). Link it from your README and code review checklist.

When an event’s meaning changes, ship a new name or version—silent redefinition makes dashboards lie.

Designing schemas that survive data warehouses

Events are APIs between client and analytics infrastructure. Name them with namespaces: `checkout_started` beats `event1`. Version events when semantics change—`order_completed_v2`—so analysts do not mix incompatible payloads. Document required properties, units (milliseconds versus seconds), and enums. Avoid high-cardinality free-text fields in warehouses—they explode costs and slow queries. If you need details, hash or bucket values. Align mobile events with web and backend names where journeys cross surfaces—join keys simplify funnels. For experiments, include variant identifiers but not PII. Consider sampling in high-volume telemetry when statistically valid—especially on low-end devices where logging overhead matters. Establish ownership: who approves new events and retires old ones? Without governance, schemas rot into unusable soup.

Consent, initialization, and what ‘off’ must mean

SDK initialization should await consent when required by law or policy. ‘Off’ must mean no network calls, not just hidden UI—verify with packet captures in QA. Provide user-accessible toggles that match marketing copy—if users opt out of analytics, respect that across sessions and reinstalls unless lawfully allowed otherwise. For children’s modes, disable profiling entirely per platform rules. Document data retention and deletion paths—analytics is part of privacy promises. When using third-party dashboards, understand their subprocessors and data residency—enterprise customers ask. For EU users, align with GDPR lawful bases; for US state laws, map requirements carefully—mobile has no ‘cookie banner’ default, but consent UX still matters.

Debugging and validation workflows

Use separate projects or keys for dev versus prod to avoid polluting production dashboards. Add automated checks that critical events fire in staging smoke tests—silent analytics breaks decisions silently. For complex funnels, validate with server logs occasionally to ensure client events match reality—clock skew and batching can misorder. When incidents occur—double firing, wrong properties—tag releases and hotfix quickly; polluted data ranges confuse weeks of analysis. Train PMs to request metrics with clear hypotheses; engineering prioritizes instrumentation that answers decisions. Document sampling rates and transformations applied in pipelines so analysts do not misinterpret absolute counts.

Performance, batching, and client impact

Batch events when SDKs allow; reduce chatter on cellular networks. Throttle high-frequency signals—scroll depth can be sampled. Disable verbose logging in production builds entirely—string formatting costs and may leak data. Monitor SDK CPU and memory overhead after upgrades; regressions happen. For offline-first apps, queue events with durable storage and backoff—avoid losing everything on crash. Coordinate flush intervals with battery state—aggressive flushing on background is wasteful. Document worst-case event volume per session for capacity planning—marketing spikes can stress ingestion.

Shipping and reliability habits (1)

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.

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.

Platform differences worth rehearsing (2)

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.

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.

Security, privacy, and data handling (3)

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.

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.’

Performance and measurement discipline (4)

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.

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.

Team process and long-term maintenance (5)

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.

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.

Shipping and reliability habits (6)

Expo SDK upgrades are integration projects: `expo doctor`, aligned community packages, regenerated native projects, and device smoke tests for camera, push, and IAP. Freeze unrelated native refactors during the upgrade window and keep rollback paths hot. Document surprises for the next upgrade while memory is fresh.

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.

Platform differences worth rehearsing (7)

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.

Security, privacy, and data handling (8)

Internationalization is a product feature, not a string swap. Plural rules, RTL layout, and locale-aware formatting change behavior—not just copy length. Pseudolocale helps find clipping early, but real Arabic and German QA catches nuance. Avoid concatenating translated fragments; context matters. Document glossary terms so translators do not invent inconsistent product names.

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.

Performance and measurement discipline (9)

WebViews are untrusted browsers inside your app. Validate `postMessage` payloads, lock navigation to expected hosts, and prefer system-browser auth flows when OAuth security demands it. Third-party JavaScript can change without your deploy—treat XSS in web as bridge compromise risk. Clear storage on logout and rate-limit message handlers.

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.

Team process and long-term maintenance (10)

In-app purchases require server validation, restore flows, and support tooling that respects privacy. Sandbox quirks are normal—budget QA time. Subscriptions interact with family sharing, regional pricing, and refunds; engineering must stay aligned with finance and legal narratives users see in receipts.

Privacy nutrition labels and Play Data Safety forms should reflect actual SDK behavior—inventory dependencies each release and remove dead code. Drift between claims and telemetry is legal and store risk, not just embarrassment. Involve legal early when adding analytics or ads.

Shipping and reliability habits (11)

Background execution policies change with OS updates—revalidate after major iOS and Android releases. Misused background modes invite rejection. Persist user work frequently; the OS can kill you anytime after backgrounding. Uploads and timers should tolerate pause and resume without corrupting state.

Design tokens and semantic colors make dark mode and rebrands feasible. Mixing three styling systems doubles migration cost—pick a primary approach and draw boundaries. Runtime CSS-in-JS can cost frame time on hot screens—profile before adopting wholesale.

Platform differences worth rehearsing (12)

Performance work should start with measurement, not instinct. Watch JS thread versus UI thread separately; they bottleneck differently. Lists, images, and animations dominate most regressions—optimize those before micro-optimizing pure functions. Hermes, JSC, and bridge internals evolve; re-profile after every major upgrade instead of trusting last year’s numbers. Battery and thermal throttling on mid devices reveal issues flagship phones hide.

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.

Security, privacy, and data handling (13)

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.

Performance and measurement discipline (14)

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.

Team process and long-term maintenance (15)

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.

Sponsored

Quick promo