React Native Overview
Complete guide to React Native project setup, configuration, and development workflow.
Overview
This guide covers React Native project setup from initial scaffolding through build configuration. React Native enables cross-platform mobile development with JavaScript/TypeScript while maintaining native performance for critical banking features like biometric authentication and secure storage.
React Native works by running JavaScript/TypeScript logic and native UI rendering in coordinated runtimes. In modern React Native (0.84+), the New Architecture (Fabric + TurboModules + JSI) is the standard model for communication between JS and native code. This architecture allows developers to write business logic once in JavaScript/TypeScript while leveraging platform-specific UI components that provide native look, feel, and performance.
The framework compiles to truly native applications, not web views. When you render a <View> component, React Native creates an actual UIView on iOS or android.view.View on Android. This is fundamentally different from hybrid frameworks like Cordova that wrap web content in a WebView. The result is applications that perform like native apps because they are native apps, with JavaScript controlling the logic rather than Swift/Kotlin.
For banking applications, this architecture provides the perfect balance: shared business logic (payment processing, validation, offline sync) written once in TypeScript, while security-critical components (biometric authentication, secure storage, certificate pinning) leverage native iOS and Android APIs through either pre-built libraries or custom native modules. See our React Native Security guide for details on integrating native security features.
New Architecture in Practice
- Fabric: New rendering system that improves consistency between iOS and Android behavior and enables better scheduling with React concurrency.
- TurboModules: Faster, lazy-loaded native modules so startup time is not dominated by loading every bridge module up front.
- JSI: JavaScript Interface enabling direct JS/native interaction without the old serialized bridge bottleneck.
For production apps, validate that key dependencies are New Architecture-compatible before upgrading React Native. Incompatibilities usually appear in older native modules, not in app code.
Core Principles
- React Native CLI over Expo: Full native module access for banking SDKs
- TypeScript Required: Type safety prevents runtime errors in production
- Functional Components: Hooks-based architecture, no class components
- Offline-First: Design for unreliable network conditions from the start
- Native Parity: Support iOS and Android equally with platform-specific code when needed
- Performance First: 60 FPS target, optimized builds, fast startup time
- Security Hardened: Secure storage, certificate pinning, ProGuard/obfuscation enabled
React Native Development Lifecycle
Project Setup
React Native CLI (Recommended for Banking)
React Native CLI provides full access to native code, allowing you to integrate banking SDKs, implement custom native modules, and configure build settings precisely. Unlike Expo Go, which runs in a managed environment, React Native CLI generates actual iOS (.xcodeproj) and Android (build.gradle) projects that you control completely.
Banking applications require this control to:
- Configure ProGuard/R8 obfuscation rules for Android to protect code from reverse engineering
- Set up certificate pinning in native network security configurations to prevent man-in-the-middle attacks
- Integrate biometric libraries that require native permissions and entitlements
- Access Keychain (iOS) and Keystore (Android) directly for secure credential storage
# Prerequisites
node --version # 20+ LTS
npm --version # 10+
# Create new React Native project with TypeScript
npx react-native@latest init PaymentApp --template react-native-template-typescript
cd PaymentApp
# Install essential dependencies
npm install @react-navigation/native @react-navigation/native-stack @react-navigation/bottom-tabs
npm install react-native-screens react-native-safe-area-context
npm install @tanstack/react-query
npm install zustand
npm install react-native-keychain
npm install @react-native-async-storage/async-storage
npm install @react-native-community/netinfo
npm install react-native-fast-image
# Development dependencies
npm install --save-dev @testing-library/react-native jest-expo
npm install --save-dev @types/react @types/react-native
npm install --save-dev eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin
npm install --save-dev prettier eslint-config-prettier eslint-plugin-prettier
# Install iOS pods (macOS only)
cd ios && pod install && cd ..
After running these commands, you'll have a fully configured React Native project with native iOS and Android folders. The iOS folder contains Xcode project files where you'll configure signing certificates, entitlements (for biometrics, keychain access), and Info.plist settings. The Android folder contains Gradle build files where you'll set up ProGuard, configure network security, and manage permissions in AndroidManifest.xml. Both platforms' native code is directly accessible for customization, unlike Expo's managed workflow where native code is abstracted away.
Expo (For Rapid Prototyping Only)
# Create Expo project
npx create-expo-app@latest PaymentApp --template blank-typescript
cd PaymentApp
# Install additional dependencies
npx expo install @react-navigation/native @react-navigation/native-stack
npx expo install react-native-screens react-native-safe-area-context
Expo Go does not support many native banking SDKs (biometric authentication with custom UI, certificate pinning, some secure storage modules). For production banking apps, use React Native CLI or Expo with a Custom Development Client + EAS Build when native modules are required.
Recommended Versions
| Package | Version | Reason |
|---|---|---|
| React Native | 0.84+ | New Architecture default, improved startup/runtime performance |
| React | 19.x | Latest React APIs and compiler-era optimizations |
| TypeScript | 5.9+ | Latest type-system improvements and tooling performance |
| Node.js | 22 LTS+ | Current LTS baseline for tooling and CI |
| React Navigation | 7.x | Current major with improved TS ergonomics |
| React Query | 5.x | Server state management, caching |
| Zustand | 5.x | Lightweight client state management |
Project Structure
payment-app/
|-- src/
| |-- components/
| | |-- common/
| | `-- domain/
| |-- screens/
| | |-- auth/
| | |-- payments/
| | `-- profile/
| |-- navigation/
| |-- services/
| | |-- api/
| | |-- auth/
| | `-- storage/
| |-- store/
| |-- hooks/
| |-- utils/
| |-- types/
| |-- assets/
| |-- config/
| `-- App.tsx
|-- android/
|-- ios/
|-- __tests__/
|-- tsconfig.json
|-- metro.config.js
`-- package.json
This structure is organized by feature boundaries and runtime responsibilities. Keep UI components, navigation, data access, and state management separated so teams can evolve layers independently without creating cross-cutting coupling.
Architecture Layers
Development Workflow
Metro Bundler Configuration
Metro is React Native's JavaScript bundler that transforms and bundles your code for deployment. Think of it like Webpack for React Native, but optimized specifically for mobile development patterns. Metro performs several crucial tasks: transpiling TypeScript/JSX to JavaScript, resolving module imports, bundling code into a single JavaScript file, and serving that bundle during development with Fast Refresh support.
The inlineRequires optimization transforms your code to lazy-load modules only when first accessed instead of loading all modules at app startup. For example, if you have a rarely-used transaction history export feature, that code won't be loaded until the user actually navigates to that screen. This dramatically reduces initial bundle evaluation time, getting users to the login screen faster - critical for banking applications with large codebases where slow startup drives user abandonment.
The asset extensions configuration tells Metro which file types to treat as static assets (images, fonts) versus source code. When Metro encounters a .png import, it processes it as an image asset, generating appropriate platform-specific resource handling. For .ts files, it runs the TypeScript compiler and then bundles the resulting JavaScript.
// metro.config.js
const {getDefaultConfig, mergeConfig} = require('@react-native/metro-config');
/**
* Metro configuration
* Banking apps need optimized bundling for performance
*/
const config = {
transformer: {
getTransformOptions: async () => ({
transform: {
experimentalImportSupport: false,
inlineRequires: true, // Improve startup time by lazy-loading modules
},
}),
},
resolver: {
// Support for SVG and other assets
assetExts: ['png', 'jpg', 'jpeg', 'svg', 'gif', 'webp'],
sourceExts: ['js', 'jsx', 'json', 'ts', 'tsx', 'cjs'],
},
};
module.exports = mergeConfig(getDefaultConfig(__dirname), config);
Learn more about bundle optimization in our React Native Performance guide, which covers code splitting, reducing bundle size, and profiling Metro build performance.
TypeScript Configuration
// tsconfig.json
{
"compilerOptions": {
"target": "esnext",
"module": "commonjs",
"lib": ["es2019"],
"allowJs": true,
"jsx": "react-native",
"strict": true, // Enable all strict checks
"noImplicitAny": true,
"strictNullChecks": true,
"strictFunctionTypes": true,
"strictPropertyInitialization": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"moduleResolution": "node",
"allowSyntheticDefaultImports": true,
"esModuleInterop": true,
"skipLibCheck": true,
"resolveJsonModule": true,
"isolatedModules": true,
"baseUrl": "./src",
"paths": {
"@components/*": ["components/*"],
"@screens/*": ["screens/*"],
"@services/*": ["services/*"],
"@hooks/*": ["hooks/*"],
"@utils/*": ["utils/*"],
"@types/*": ["types/*"],
"@assets/*": ["assets/*"]
}
},
"include": ["src/**/*"],
"exclude": ["node_modules", "**/*.spec.ts"]
}
Platform-Specific Code
File Extensions
src/components/
|-- Button.tsx # Shared code
|-- Button.ios.tsx # iOS-specific implementation
`-- Button.android.tsx # Android-specific implementation
Platform API
// utils/platform.ts
import { Platform } from 'react-native';
export const isIOS = Platform.OS === 'ios';
export const isAndroid = Platform.OS === 'android';
// Use Platform.select for inline differences
export const shadowStyle = Platform.select({
ios: {
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.25,
shadowRadius: 3.84,
},
android: {
elevation: 5,
},
});
// Platform-specific dimensions
export const statusBarHeight = Platform.select({
ios: 44,
android: 24,
});
Build Configuration
Development vs Production
Environment Variables
# .env.development
API_URL=https://api-dev.bank.com
API_TIMEOUT=30000
ENABLE_LOGGING=true
ENVIRONMENT=development
# .env.production
API_URL=https://api.bank.com
API_TIMEOUT=10000
ENABLE_LOGGING=false
ENVIRONMENT=production
// config/env.ts
import Config from 'react-native-config';
export const ENV = {
API_URL: Config.API_URL || '',
API_TIMEOUT: parseInt(Config.API_TIMEOUT || '10000', 10),
ENABLE_LOGGING: Config.ENABLE_LOGGING === 'true',
ENVIRONMENT: Config.ENVIRONMENT || 'development',
} as const;
Common Mistakes
Not Using TypeScript Strict Mode
// BAD: Loose TypeScript
const formatAmount = (amount) => { // Implicit any
return `$${amount}`;
};
// GOOD: Strict TypeScript
const formatAmount = (amount: number): string => {
return `$${amount.toFixed(2)}`;
};
Not Handling Platform Differences
// BAD: Assumes all platforms are the same
<View style={{ elevation: 5 }}> {/* Won't work on iOS */}
<Text>Card</Text>
</View>
// GOOD: Platform-specific styles
import { Platform, StyleSheet } from 'react-native';
const styles = StyleSheet.create({
card: {
...Platform.select({
ios: {
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.25,
shadowRadius: 3.84,
},
android: {
elevation: 5,
},
}),
},
});
Ignoring Metro Bundler Optimization
// BAD: No optimization
const config = {};
module.exports = config;
// GOOD: Optimized Metro config
const config = {
transformer: {
getTransformOptions: async () => ({
transform: {
inlineRequires: true, // Faster startup
},
}),
},
};
Code Review Checklist
Project Setup
- TypeScript strict mode enabled in tsconfig.json
- React Native CLI used for production banking apps (not Expo Go)
- All required dependencies installed (navigation, state management, secure storage)
- Native linking completed (iOS pods, Android dependencies)
- .env files configured for different environments
Project Structure
- Clear separation of concerns (components, screens, services, hooks)
- Shared components in common/ directory
- Domain components separate from generic components
- Services layer abstracts API and native modules
- Types directory contains all TypeScript interfaces
Platform Support
- Platform-specific code uses .ios.tsx and .android.tsx when needed
- Platform.select used for inline platform differences
- Styles handle both platforms (shadow vs elevation)
- Safe area insets respected on both platforms
- Status bar configured correctly for each platform
Build Configuration
- Metro bundler optimized (inlineRequires enabled)
- Environment variables configured via react-native-config
- Production builds have minification and obfuscation
- Source maps disabled in production
- Dev menu disabled in production builds
Banking-Specific
- Secure storage configured (react-native-keychain)
- Network detection implemented (@react-native-community/netinfo)
- Offline-first architecture planned from start
- Biometric authentication library included if needed
- Certificate pinning will be implemented in API client
Further Reading
React Native Framework Guidelines
- React Native Navigation - React Navigation patterns and type safety
- React Native UI - Component architecture and styling
- React Native Data - API integration, caching, offline support
- React Native Security - Secure storage, biometrics, certificate pinning
- React Native Performance - Optimization strategies
Cross-Platform Guidelines
- React Guidelines - React patterns and hooks (apply to RN)
- React State Management - Zustand and React Query
- TypeScript Guidelines - TypeScript best practices
- Mobile Overview - Cross-platform mobile patterns
- Mobile Navigation - Navigation architecture
- API Design - RESTful API principles
- OpenAPI Frontend Integration - Type-safe API clients
External Resources
Summary
Key Takeaways
- React Native CLI - Use for production banking apps, not Expo Go
- TypeScript strict mode - Required for type safety and fewer runtime errors
- Structured project - Clear separation: components, screens, services, hooks
- Platform-aware - Handle iOS/Android differences with Platform API
- Optimized Metro - Enable inlineRequires for faster startup
- Environment config - Separate dev/prod configurations
- Offline-first setup - Plan architecture from project start
- Secure by default - Include secure storage and biometric libraries
- Testing ready - Include Testing Library and Jest configuration
- Type-safe navigation - Define route params and screen props upfront
Next Steps: Review React Native Navigation to implement type-safe navigation with React Navigation, then explore React Native UI for component architecture and styling patterns.