Expo 50 app with expo-router, Tamagui, NativeWind, Zustand, and Reanimated 3

The Flutter Madhur app evaluated GetX and Appwrite under real conditions. This build evaluated the modern React Native ecosystem against the same product shape — same screens, same data model, different technology beneath. The point was not to pick a winner in the abstract; it was to produce a concrete comparison on a real project with a real opinion at the end.
React Native 0.73 with Expo SDK 50. The Expo managed workflow handles the native build layer, which eliminates a significant source of friction for projects that don't need custom native modules. expo-router provides file-system-based navigation — the same mental model as Next.js App Router — which made the routing layer feel immediately familiar coming from web development.
React Native's styling model has historically been one of its roughest edges. Inline styles, StyleSheet objects, no cascade — the mental context switch from web CSS is real, and the tooling fragmentation (Styled Components, Emotion Native, dripsy, NativeWind) doesn't help.
NativeWind applies Tailwind CSS utility classes to React Native components, which removes the context switch entirely. Write the same className patterns used on the web; NativeWind compiles them to the appropriate React Native style objects. For someone whose CSS vocabulary is Tailwind-first, this is the correct solution.
Tamagui adds a typed design token and component layer on top. The combination — NativeWind for utility styling, Tamagui for shared design system components — gives complete coverage: atomic utilities for layout and spacing, pre-built accessible components for interactive patterns.
Zustand for state management: a minimal store with a create() call, hooks to subscribe, no boilerplate. Against GetX's combined state/routing/DI package, Zustand is more surgical — it handles exactly state and nothing else. The comparison informed the conclusion that lightweight single-purpose libraries compose better than opinionated all-in-one packages when the team already has routing and DI patterns from the framework.
Reanimated 3 handles physics-based animations and shared-element transitions. The worklet model — animation logic runs on the UI thread, not the JS bridge — is the correct architecture for smooth animations on React Native. The comparison point against Flutter's animation system is real: Flutter animates on the UI thread by default; React Native's classic Animated API runs on the JS thread and can drop frames under load. Reanimated 3 closes that gap.
The Flutter vs React Native conclusion after building both: React Native with the modern Expo ecosystem has closed most of its historical disadvantages. The choice comes down to team background and specific native module requirements, not a categorical technical inferiority.
Offline-first Flutter app with GetX state management, Appwrite backend, and Lottie animations
Full-stack React Native fashion app with GPU canvas product rendering, AI background removal, and Turborepo monorepo
Full-stack React Native + Next.js travel app with Hono API, Prisma, MSW mock layer, and 68 commits in a week
Did this resonate?