This is an Expo project created with
create-expo-app and modified by the TSH team.
To create a new project using this boilerplate template:
-
Use this template: Click the "Use this template" button on the GitHub repository page, or clone the repository:
git clone https://github.com/TheSoftwareHouse/expo-starter-boilerplate.git your-project-name cd your-project-nameAlternative - Clone with clean history: If you want to start with a fresh git history (single commit):
git clone --depth 1 https://github.com/TheSoftwareHouse/expo-starter-boilerplate.git your-project-name cd your-project-name rm -rf .git git init git add . git commit -m "Initial commit"
-
Update project details:
- Open
env.jsand update the template values:BUNDLE_ID: Your iOS Bundle ID / Android Package Name (e.g.,com.yourcompany.yourapp)NAME: Your app's display name (e.g.,My Awesome App)SLUG: Your Expo slug (e.g.,my-awesome-app)EXPO_ACCOUNT_OWNER: Your Expo account usernameEAS_PROJECT_ID: Get by runningnpx eas initSCHEME: Your app's URL scheme for deep links (e.g.,myapp)
- Open
-
Install dependencies:
npm install
-
Configure environment: Copy and configure your environment variables:
cp .env.example .env.local
-
Initialize translations (optional): If you plan to use internationalization:
npx babelsheet2 init
-
Start developing: Your project is ready! You can now start the development server and begin building your app.
-
Install dependencies
npm install
-
Start the app
npm start
In the output, you'll find options to open the app in a
- development build
- Android emulator
- iOS simulator
- Expo Go, a limited sandbox for trying out app development with Expo
You can start developing by editing the files inside the app directory. This project uses file-based routing.
For native development and building production apps, you'll need to set up the native development environment:
- Node.js: Version 22 or newer
- Git: For version control
- Watchman (recommended): For better performance on macOS/Linux
- Install Android Studio: Download and install Android Studio
- Configure Android SDK: Follow the Expo Android Studio setup guide
- Set up environment variables: Configure
ANDROID_HOMEand add platform-tools to your PATH - Create or start an emulator: Set up an Android Virtual Device (AVD) through Android Studio
- Install Xcode: Download from the Mac App Store
- Install Xcode Command Line Tools:
xcode-select --install
- Install iOS Simulator: Follow the Expo iOS Simulator setup guide
- Install CocoaPods (if using bare workflow):
sudo gem install cocoapods
- Expo Development Build - For custom native code
- EAS Build - Cloud-based build service
- React Native Environment Setup - Detailed native setup guide
To check if your development environment is properly configured, you can use these diagnostic tools:
-
Expo Doctor: Diagnose and validate your Expo development environment
npx expo-doctor
-
React Native Doctor: Check your React Native development setup (for bare workflow projects)
npx @react-native-community/cli doctor
-
Manual verification: Try running the project on different platforms
# Start the development server npm start # Test on iOS simulator (press 'i' in terminal or scan QR code) # Test on Android emulator (press 'a' in terminal or scan QR code)
These tools will help identify missing dependencies, incorrect configurations, or environment issues that might prevent your app from running properly.
This project uses babelsheet2 for managing translations. Babelsheet2 is a tool that allows you to sync translations between Google Sheets and your React/React Native applications, making it easy to collaborate with translators and content managers.
-
Initialize babelsheet2 configuration:
npx babelsheet2 init
-
Configure your Google Sheets integration in the generated
babelsheet.jsonfile with your spreadsheet details. -
Fetch translations from your configured Google Sheet:
npm run fetch-translations
The translations will be automatically downloaded and formatted for use in your application.
This project uses Sentry for error tracking and logging in production. The logger service is automatically initialized when the app starts.
-
Copy the environment variables from
.env.exampleto your.envfile:cp .env.example .env
-
Configure your Sentry credentials in the
.envfile:EXPO_PUBLIC_SENTRY_DSN=https://your-sentry-dsn@sentry.io/project-id SENTRY_ORG=your-sentry-organization SENTRY_PROJECT=your-sentry-project SENTRY_AUTH_TOKEN=your-sentry-auth-token
Import and use the logger anywhere in your app:
import { logger } from '@/integrations/logger';
// Log an error
logger.error('Something went wrong');
logger.error(new Error('An error occurred'));
// Log a warning
logger.warning('This is a warning message');
// Log info
logger.info('This is an info message');The logger automatically:
- Sends logs to Sentry in production
- Logs to console in development for easier debugging
- Captures performance traces and user interactions
- Records session replays (with sensitive data masked)
- React Native Integration: Uses
@sentry/react-nativefor optimal mobile performance - Development Mode: Console logging enabled in development
- Production Optimized: Lower sampling rates and optimized settings for production
- Session Replay: Automatically captures user sessions with privacy protection
- Performance Monitoring: Tracks app performance and network requests
- Error Boundary Integration: Automatically captures unhandled errors
This project uses a centralized, type-safe environment variable system with Zod validation, managed through env.js and
app.config.ts.
env.js(root): Loads and validates environment variables, manages app configurationapp.config.ts: TypeScript config that uses validated variables fromenv.jssrc/env.js: Client-side access point - imports via@/envin your code.env.{environment}: Environment-specific variables (development, staging, production)
-
Update app configuration in
env.js:Follow the template instructions to set your:
- Bundle ID, app name, slug
- Expo account owner and EAS project ID
- URL scheme for deep links
-
Create environment file:
cp .env.example .env.development
-
Configure your variables in
.env.development:# API Configuration EXPO_PUBLIC_API_URL=https://your-api.com EXPO_PUBLIC_DEFAULT_LOCALE=en # MMKV Encryption (generate with: node -e "console.log(require('crypto').randomBytes(32).toString('hex'))") EXPO_PUBLIC_AUTH_STORAGE_ENCRYPTION_KEY=your-secure-random-64-character-hex-key # Sentry (optional) EXPO_PUBLIC_SENTRY_DSN=https://your-sentry-dsn@sentry.io/project-id SENTRY_ORG=your-sentry-org SENTRY_PROJECT=your-project SENTRY_AUTH_TOKEN=your-token
-
Start the app - environment variables are automatically validated on startup
The project supports multiple app variants on the same device:
- Development:
.env.development→yourapp-dev://→ "YourApp (Dev)" - Staging:
.env.staging→yourapp-staging://→ "YourApp (Staging)" - Production:
.env.production→yourapp://→ "YourApp"
Switch environments:
APP_ENV=staging npm start
APP_ENV=production npm startAlways import from @/env - ESLint enforces this:
// ✅ Correct - Import from @/env
import { env } from '@/env';
const apiUrl = env.EXPO_PUBLIC_API_URL;
const locale = env.EXPO_PUBLIC_DEFAULT_LOCALE;
const appName = env.NAME; // Dynamically generated based on APP_ENV
const scheme = env.SCHEME; // Environment-specific URL scheme
// ❌ Wrong - Direct process.env access (ESLint error)
const apiUrl = process.env.EXPO_PUBLIC_API_URL;
// ❌ Wrong - Importing root env.js (ESLint error)
import { ClientEnv } from '../../env';
// ✅ Platform detection - Use Platform.OS instead
import { Platform } from 'react-native';
if (Platform.OS === 'ios') {
/* ... */
}For client-side variables (accessible in the app):
-
Add to
.env.developmentwithEXPO_PUBLIC_prefix:EXPO_PUBLIC_YOUR_NEW_VAR=your-value
-
Update the schema in
env.js:const client = z.object({ // ... existing vars EXPO_PUBLIC_YOUR_NEW_VAR: z.string().optional(), // or .min(1) for required });
-
Add to
_clientEnvobject:const _clientEnv = { // ... existing vars EXPO_PUBLIC_YOUR_NEW_VAR: process.env.EXPO_PUBLIC_YOUR_NEW_VAR, };
-
Restart with clear cache:
npm start -- --clear
For build-time variables (app.config.ts only):
- Add to
.env.development(noEXPO_PUBLIC_prefix) - Update
buildTimeschema inenv.js - Add to
_buildTimeEnvobject - Access via
Envinapp.config.tsonly
.env.*files are git-ignored and never committed (only.env.exampleis tracked)- Client-side variables (
EXPO_PUBLIC_prefix):- Validated via Zod schema in
env.js - Embedded in app bundle - accessible at runtime
- Passed to app via
app.config.ts→extrafield →@/env - Examples: API URLs, locale, MMKV key, Sentry DSN
- Validated via Zod schema in
- Build-time variables (no prefix):
- Only available in
app.config.tsand build scripts - Not embedded in app bundle
- Examples: Sentry auth token, EAS project ID
- Only available in
- ESLint enforcement:
- ✅ Allows:
import { env } from '@/env' - ❌ Blocks:
process.env.*insrc/ - ❌ Blocks: Relative imports to root
env.js - ✅ Exception:
Platform.OSfor platform detection
- ✅ Allows:
To learn more about developing your project with Expo, look at the following resources:
- Expo documentation: Learn fundamentals, or go into advanced topics with our guides.
- Learn Expo tutorial: Follow a step-by-step tutorial where you'll create a project that runs on Android, iOS, and the web.
Join our community of developers creating universal apps.
- Expo on GitHub: View our open source platform and contribute.
- Discord community: Chat with Expo users and ask questions.
Anyone and everyone is welcome to contribute. Start by checking out the list of open issues.
However, if you decide to get involved, please take a moment to review the guidelines.
Copyright © 2021-present The Software House. This source code is licensed under the MIT license found in the LICENSE file.
Made with ♥ by The Software House (website, blog) and contributors.