Building a Production-Ready React Native App
Jaydeep A.
Oct 08, 2025
This post covers a practical, opinionated guide you can use before you write a single line of UI code. Covers a pre-coding checklist (questions to ask), how to bootstrap a bare React Native project safely, essential initial setup (package manager, logging, font scaling, theming, i18n), and a compact, scalable folder structure illustrated with a WhatsApp-like example.
TL;DR (quick overview)
- Pre-coding blueprint: clarify requirements by asking targeted questions, saves days of rework.
- Bootstrapping: prefer npx-driven project init, remove legacy global CLIs to avoid conflicts, use TypeScript template.
- Initial setup: choose package manager, add production-safe log disabling, disable font scaling globally, decide app theme policy (system vs locked), and wire i18next for multi-language support.
- Project structure: keep components small + isolated, split logic/styles, use feature/module organization (example: WhatsApp-style app).
1. Before you write any code : a pre-coding blueprint
Why this matters: engineering time is precious. Clear requirements + targeted questions reduce wrong assumptions, rework, and scope creep. Good pre-coding saves implementation, QA, and release cycles.
What to do?
- Read the requirements doc and Figma designs carefully , pixel details, interactions, edge cases, and states (empty, loading, error).
- Ask lots of clarifying questions (to PMs, designers, backend), don’t assume behavior. For each question state the reason why you need the answer.
Why ask lots of questions?
- Avoid ambiguous UX decisions (e.g., how to handle back navigation on modal).
- Surface hidden edge cases (rate limits, offline behavior, missing API fields).
- Prevent wasted work (implementing feature that won’t be used).
- Align cross-team expectations (analytics, performance budgets).
- Reduce QA cycles by defining acceptance criteria up front.
example clarifying Q&A:
1. Q: What if user opens the app without internet?
A: Show cached data if available, else display offline message with retry option. Sync when connection restores.
Why: defines offline behavior & storage needs.
2. Q: When user selects image from gallery, should I use image cropping or use as-is?
A: Implement image cropping for profile pictures with aspect ratio 1:1, but allow original for other images.
Why: clarifies UX expectations and required libraries.
3. Q: Should dark mode follow system preferences or be toggleable inside the app?
A: App must support explicit toggle (light/dark/auto). Default = auto (follow system).
Why: affects theme architecture and persistence.
2. Bootstrapping a bare React Native project (what to install and why)
-
react-native-cli -
@react-native-community/cli -
@react-native-community/cli-platform-android -
@react-native-community/cli-platform-ios
What problem these CLIs solve:
npx react-native run-android, these CLIs translate that into commands that Android understands.Why uninstall old global CLI versions before creating a new project:
npx ensures you always get the latest, most compatible version for each new project.Helpful commands
# List globally installed packages
npm list -g --depth=0
yarn global list
# Check for specific package
npm list -g react-native-cli
yarn global list | grep react-native-cli
# Remove global CLI to avoid conflicts
npm uninstall -g react-native-cli @react-native-community/cli
yarn global remove react-native-cli @react-native-community/cli
Bootstrapping with npx :
# basic
npx @react-native-community/cli@latest init AwesomeProject
-
npxruns the CLI without permanently installing it globally. -
The
@latestensures you use the newest compatible CLI. -
The
initscaffolds android/ios folders, JS entry, default templates.
npx and not npm or yarn for bootstrapping: npx is like borrowing a tool instead of buying it. You use the exact version you need just once, without cluttering your system. Global installs can cause “it works on my machine” problems because teammates might have different versions.Install a specific RN version
npx @react-native-community/cli@X.Y.Z init PROJECT_NAME --version X.XX.X
Why use specific version: This locks your project to a known good version, ensuring all developers and your build servers use the same React Native version. Prevents surprises when new versions introduce changes that break your app.
3. Initial must-have setup (what, why, when to avoid, and how)
3.1 Choose a package manager : Yarn vs npm
-
Yarn introduced stable installs and deterministic
yarn.lockearlier than npm. Modernnpmimproved a lot (v7+), andpnpmbrings disk/monorepo advantages. Choose what your team uses consistently. -
packageManager field in
package.json(example:"packageManager": "yarn@1.22.19") is a lightweight hint recorded in the project root to declare which manager+version the project expects. It helps CI and contributors avoid accidental use of the wrong manager+version. - When to add it: when you need teams/CI to use a specific manager/version or when using workspaces (monorepos).
- What problem it solves: reduces accidental cross-manager lockfile conflicts and helps reproduce installs on CI/dev machines.
3.2 Production logging : silence console in production
index.js (entry point) add:3.3 Disable font scaling globally
3.4 Theme Mode : Control System Dark Mode
Native-level lock : locks the app to a specific mode.
-
iOS: In
Info.plist
<key>UIUserInterfaceStyle</key>
<string>Light</string> <!-- Light, Dark, or Automatic -->
-
Android: In
styles.xml
<item name="android:forceDarkAllowed">false</item> <!-- disable system dark mode -->
JS-level behaviour : respects system preference or user override. Use React Native’s Appearance API:
const App = () => { const systemTheme = useColorScheme(); // 'light' | 'dark' const [theme,setTheme] = React.useState("auto"); // 'light' | 'dark' | 'auto' const activeTheme = theme === "auto" ? systemTheme : theme; return ( <View style={{ backgroundColor: activeTheme === "dark" ? "#000" : "#fff" }} /> ); };
3.5 Multi-language support with i18next + react-i18next
Use i18next + react-i18next for React Native. VSCode extension i18next-ally helps manage keys and translations.
What i18next-ally solves: shows translation key usages inline, highlights missing keys, and streamlines translators’ workflow in the editor, less guesswork when adding translations.
Example setup:
4. Project Structure & Folder Organization
A clean folder structure makes onboarding, testing, and scaling easier. Below is an example for a messaging app.
Rationale & best practices
- components/common: primitive/atomic components used across the app (Button, Text, Avatar). Keep them small, well-typed, and documented.
- listItems: list row components are feature-specific; isolating them simplifies snapshot testing and perf.
-
screens: a screen directory with
index.tsx + styles.tsisolates layout and styles.index.tsx contains view logic; tests and subcomponents are referenced from here. - services/api: split by domain (auth, chat, messages). Centralize axios config (base URL, interceptors).
- firebase: group firebase modules (auth/firestore/storage) for clarity and mocking in tests.
-
navigation: a single place for navigators and typed route params (
types.ts), prevents magic strings. -
redux (or state layer): slice per domain; keep store config separate. Use typed hooks (e.g.,
useAppDispatch, useAppSelector). - hooks: colocate reusable hooks here; keep them pure and testable.
- config: centralized design tokens (colors, typography) so theme changes are atomic.
-
types: global/shared type definitions. Keep domain types close to services that produce them, and export shared types to
types/for cross-module use.
components file, extract them. Smaller files + single responsibility make review easier and prevent accidental re-render coupling.Conclusion
This blueprint is intentionally pragmatic and opinionated: clarify requirements first, scaffold projects reproducibly with npx + TypeScript, enforce a small set of initial app rules (log silencing, consistent font scaling policy, theme handling, and i18n), and adopt a clear folder structure that scales as your app grows.