
Introduction
React Native remains one of the most powerful frameworks for building cross-platform mobile apps using JavaScript or TypeScript. Companies like Meta, Microsoft, Shopify, and Discord use React Native to ship iOS and Android apps from a single codebase. In 2025, the ecosystem has matured significantly with the New Architecture (Fabric and TurboModules), improved performance, and better tooling. Whether you’re building for iOS, Android, or even web, setting up your environment properly ensures smooth development from day one. In this comprehensive guide, you’ll learn how to set up a React Native project in 2025 using both Expo and the React Native CLI, including TypeScript configuration, essential libraries, navigation setup, and production-ready folder structure.
Expo vs React Native CLI: Which to Choose
Before diving into setup, understand the trade-offs:
| Feature | Expo | React Native CLI |
|---|---|---|
| Setup time | 5 minutes | 30+ minutes |
| Native code access | Via config plugins | Full access |
| OTA updates | Built-in | Manual setup |
| Build service | EAS Build | Local or CI |
| Web support | Built-in | Manual setup |
| Custom native modules | Supported (dev builds) | Full control |
Recommendation: Start with Expo unless you have specific native module requirements that Expo doesn’t support. You can always “eject” or use development builds later.
Option 1: Setting Up with Expo
Prerequisites
# Check Node.js version (18+ recommended)
node -v
# Install/update npm
npm install -g npm@latest
Create the Project
# Create a new Expo project with TypeScript
npx create-expo-app@latest MyApp --template blank-typescript
# Navigate to project
cd MyApp
# Start development server
npx expo start
Scan the QR code with the Expo Go app on your phone, or press i for iOS simulator, a for Android emulator.
Install Essential Dependencies
# Navigation
npx expo install @react-navigation/native @react-navigation/native-stack
npx expo install react-native-screens react-native-safe-area-context
# State management
npm install zustand
# Data fetching
npm install @tanstack/react-query axios
# Forms
npm install react-hook-form zod @hookform/resolvers
# UI components
npx expo install expo-linear-gradient
npm install react-native-reanimated
# Storage
npx expo install @react-native-async-storage/async-storage
npx expo install expo-secure-store
Configure TypeScript
// tsconfig.json
{
"extends": "expo/tsconfig.base",
"compilerOptions": {
"strict": true,
"baseUrl": ".",
"paths": {
"@/*": ["src/*"],
"@components/*": ["src/components/*"],
"@screens/*": ["src/screens/*"],
"@hooks/*": ["src/hooks/*"],
"@utils/*": ["src/utils/*"]
}
},
"include": ["**/*.ts", "**/*.tsx", ".expo/types/**/*.ts", "expo-env.d.ts"]
}
// babel.config.js - Enable path aliases
module.exports = function (api) {
api.cache(true);
return {
presets: ['babel-preset-expo'],
plugins: [
[
'module-resolver',
{
root: ['./'],
alias: {
'@': './src',
'@components': './src/components',
'@screens': './src/screens',
'@hooks': './src/hooks',
'@utils': './src/utils',
},
},
],
'react-native-reanimated/plugin', // Must be last
],
};
};
Option 2: Setting Up with React Native CLI
Prerequisites
# macOS
brew install node watchman
# iOS development requires:
# - Xcode from App Store
# - Xcode Command Line Tools: xcode-select --install
# - CocoaPods: sudo gem install cocoapods
# Android development requires:
# - Android Studio
# - Android SDK (API 33+)
# - ANDROID_HOME environment variable set
Create the Project
# Create with TypeScript template
npx react-native@latest init MyApp --template react-native-template-typescript
cd MyApp
# Install iOS dependencies
cd ios && pod install && cd ..
# Run on iOS
npx react-native run-ios
# Run on Android
npx react-native run-android
Enable New Architecture (Recommended)
// android/gradle.properties
newArchEnabled=true
// ios/Podfile - Uncomment this line:
# :fabric_enabled => true
# Rebuild after enabling
cd ios && pod install && cd ..
npx react-native run-ios
Production-Ready Folder Structure
src/
├── app/ # App entry and providers
│ ├── App.tsx
│ └── providers.tsx
├── components/ # Shared components
│ ├── ui/
│ │ ├── Button.tsx
│ │ ├── TextField.tsx
│ │ └── Card.tsx
│ └── layout/
│ ├── Screen.tsx
│ └── Header.tsx
├── config/ # Configuration
│ ├── api.ts
│ ├── constants.ts
│ └── theme.ts
├── features/ # Feature modules
│ ├── auth/
│ │ ├── screens/
│ │ ├── hooks/
│ │ ├── api/
│ │ └── store/
│ ├── home/
│ └── profile/
├── hooks/ # Shared hooks
│ ├── useAuth.ts
│ └── useApi.ts
├── navigation/ # Navigation setup
│ ├── RootNavigator.tsx
│ ├── AuthNavigator.tsx
│ └── types.ts
├── stores/ # Global state
│ └── authStore.ts
├── types/ # Shared types
│ └── api.ts
└── utils/ # Utilities
├── storage.ts
└── validators.ts
Setting Up Navigation
// src/navigation/types.ts
export type RootStackParamList = {
Auth: undefined;
Main: undefined;
};
export type AuthStackParamList = {
Login: undefined;
Register: undefined;
ForgotPassword: undefined;
};
export type MainTabParamList = {
Home: undefined;
Profile: undefined;
Settings: undefined;
};
// src/navigation/RootNavigator.tsx
import { NavigationContainer } from '@react-navigation/native';
import { createNativeStackNavigator } from '@react-navigation/native-stack';
import { useAuthStore } from '@/stores/authStore';
import { AuthNavigator } from './AuthNavigator';
import { MainNavigator } from './MainNavigator';
import type { RootStackParamList } from './types';
const Stack = createNativeStackNavigator();
export function RootNavigator() {
const isAuthenticated = useAuthStore((state) => state.isAuthenticated);
return (
{isAuthenticated ? (
) : (
)}
);
}
App Entry Point
// src/app/providers.tsx
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { GestureHandlerRootView } from 'react-native-gesture-handler';
import { SafeAreaProvider } from 'react-native-safe-area-context';
import { ReactNode } from 'react';
const queryClient = new QueryClient({
defaultOptions: {
queries: {
retry: 2,
staleTime: 1000 * 60 * 5, // 5 minutes
},
},
});
export function AppProviders({ children }: { children: ReactNode }) {
return (
{children}
);
}
// src/app/App.tsx
import { StatusBar } from 'expo-status-bar';
import { AppProviders } from './providers';
import { RootNavigator } from '@/navigation/RootNavigator';
export default function App() {
return (
);
}
VS Code Configuration
// .vscode/settings.json
{
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit"
},
"typescript.preferences.importModuleSpecifier": "non-relative"
}
// .vscode/extensions.json
{
"recommendations": [
"dbaeumer.vscode-eslint",
"esbenp.prettier-vscode",
"msjsdiag.vscode-react-native",
"dsznajder.es7-react-js-snippets",
"bradlc.vscode-tailwindcss"
]
}
Common Mistakes to Avoid
Skipping TypeScript: TypeScript catches bugs early and improves IDE support. Always use it for production apps.
Flat folder structure: Dumping everything in components/ doesn’t scale. Use feature-based organization.
Not configuring path aliases: Relative imports like ../../../components are hard to maintain. Set up @/ aliases.
Missing gesture handler setup: Many libraries require GestureHandlerRootView at the root. Add it early.
Ignoring the New Architecture: Enable Fabric and TurboModules for better performance in new projects.
Conclusion
React Native setup in 2025 is more streamlined than ever. Expo provides the fastest path to production with built-in OTA updates, EAS Build, and excellent developer experience. The React Native CLI offers full control when you need custom native code. Either way, invest time in proper TypeScript configuration, path aliases, and folder structure from day one—it pays dividends as your project grows. The essential stack includes React Navigation for routing, Zustand or Redux for state, React Query for server state, and React Hook Form for forms. With this foundation, you’re ready to build production-quality mobile apps. For more on structuring your React Native project, check out our guide on Cross-Platform Project Structure. For the latest React Native features, explore the official documentation.