Expo Mobile App Development and Deployment: A Comprehensive Guide
Introduction to React Native and Expo
React Native is a framework for building mobile apps using JavaScript and the React library. It allows developers to build mobile apps for iOS and Android using the same codebase, making it a cost-effective and efficient solution for cross-platform development.
Expo is a toolset built around React Native that helps developers to more easily build, test, and deploy mobile apps. It provides a set of pre-built components, APIs, and services that simplify the development process, allowing developers to focus on building their app rather than configuring native build tools.
In this guide, we will explore how to set up your development environment, create a new Expo project, develop your app, and deploy it to app stores. Whether you are new to mobile development or experienced with other frameworks, Expo offers a streamlined approach to building robust mobile applications.
Why Choose Expo?
Simplified Setup
Start developing without complex native build configurations. No Xcode or Android Studio required initially.
Fast Iteration
With hot reloading and the Expo Go app, see changes instantly without rebuilding your app.
Rich Ecosystem
Access to a wide range of pre-built components and APIs for common mobile features like camera, notifications, etc.
OTA Updates
Push updates to your app without going through app store review processes (within certain limitations).
Managed vs. Bare Workflow: Expo offers two workflows: Managed (easier but with some limitations) and Bare (full access to native code). We will focus on the Managed workflow in this guide, but will touch on when and how to eject if needed.
Setting Up Your Development Environment
Prerequisites
- Node.js (version 14 or newer)
- npm or Yarn package manager
- A code editor (VS Code recommended)
- A smartphone with Expo Go app (for testing) or iOS/Android simulators
Installation
First, install the Expo CLI globally on your computer:
npm install -g expo-cli
Or if you are using Yarn:
yarn global add expo-cli
Creating a New Project
To create a new Expo project, run:
expo init MyAwesomeApp
You will be prompted to choose a template. For beginners, the "blank" template is a good starting point. After creating your project, navigate to its directory and start the development server:
cd MyAwesomeApp expo start
This will open a browser window with the Expo DevTools. You can scan the QR code with your phone's camera (iOS) or the Expo Go app (Android) to open your app on your device.
Project Structure and Configuration
Let us look at the key files and directories in a typical Expo project:
MyAwesomeApp/
├── assets/ # Images, fonts, and other static assets
├── node_modules/ # Dependencies (don't edit)
├── .gitignore # Git ignore configuration
├── App.js # Main application component
├── app.json # Expo configuration
├── babel.config.js # Babel configuration
├── package.json # Project dependencies and scripts
└── README.md # Project documentation
Configuring Your App
The app.json
file is where you configure various aspects of your Expo app:
{
"expo": {
"name": "My Awesome App", // App name displayed to users
"slug": "my-awesome-app", // Unique identifier for your app
"version": "1.0.0", // Your app version
"orientation": "portrait", // portrait, landscape, or default
"icon": "./assets/icon.png", // App icon
"splash": { // Splash screen configuration
"image": "./assets/splash.png",
"resizeMode": "contain",
"backgroundColor": "#ffffff"
},
"updates": {
"fallbackToCacheTimeout": 0
},
"assetBundlePatterns": [
"**/*" // Which assets to include
],
"ios": {
"supportsTablet": true,
"bundleIdentifier": "com.yourcompany.myawesomeapp"
},
"android": {
"adaptiveIcon": {
"foregroundImage": "./assets/adaptive-icon.png",
"backgroundColor": "#FFFFFF"
},
"package": "com.yourcompany.myawesomeapp"
},
"web": {
"favicon": "./assets/favicon.png"
}
}
}
This configuration will be used when building your app for production. You will need to fill in your own bundle identifier/package name before submitting to app stores.
Building Your App with Expo
Essential Components and APIs
Expo provides a rich set of components and APIs that make mobile development easier:
UI Components
- View, Text, Image
- TouchableOpacity, Button
- ScrollView, FlatList
- TextInput, Switch
Device APIs
- Camera, ImagePicker
- Location, Sensors
- Notifications, Haptics
- SecureStore, FileSystem
Example: Building a Simple Screen
Here is an example of a simple screen that uses some common Expo components:
import React, { useState, useEffect } from 'react';
import { StyleSheet, Text, View, Button, Image } from 'react-native';
import * as ImagePicker from 'expo-image-picker';
import { StatusBar } from 'expo-status-bar';
export default function App() {
const [image, setImage] = useState(null);
useEffect(() => {
// Request permissions when component mounts
(async () => {
const { status } = await ImagePicker.requestMediaLibraryPermissionsAsync();
if (status !== 'granted') {
alert('Sorry, we need camera roll permissions to make this work!');
}
})();
}, []);
const pickImage = async () => {
let result = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ImagePicker.MediaTypeOptions.Images,
allowsEditing: true,
aspect: [4, 3],
quality: 1,
});
if (!result.cancelled) {
setImage(result.uri);
}
};
return (
<View style={styles.container}>
<Text style={styles.title}>Image Picker Example</Text>
<Button title="Pick an image from camera roll" onPress={pickImage} />
{image && (
<Image source={{ uri: image }} style={styles.image} />
)}
<StatusBar style="auto" />
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
padding: 20,
},
title: {
fontSize: 24,
fontWeight: 'bold',
marginBottom: 20,
},
image: {
width: 300,
height: 300,
marginTop: 20,
borderRadius: 10,
},
});
Installing Expo Packages
To add functionality to your app, you will often need to install additional packages. With Expo, this is straightforward:
expo install expo-image-picker expo-location expo-notifications
The expo install
command ensures compatible versions are installed.
Navigation in Expo Apps
Most apps need to navigate between multiple screens. React Navigation is the standard library for handling navigation in React Native and Expo apps.
Setting Up React Navigation
expo install @react-navigation/native expo install react-native-screens react-native-safe-area-context
Next, install the navigation type you need. For a stack navigator (screens that stack on top of each other):
expo install @react-navigation/stack
Example: Basic Navigation Setup
import React from 'react';
import { NavigationContainer } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack';
import HomeScreen from './screens/HomeScreen';
import DetailsScreen from './screens/DetailsScreen';
const Stack = createStackNavigator();
export default function App() {
return (
<NavigationContainer>
<Stack.Navigator initialRouteName="Home">
<Stack.Screen
name="Home"
component={HomeScreen}
options={{ title: 'Welcome' }}
/>
<Stack.Screen
name="Details"
component={DetailsScreen}
options={{ title: 'Item Details' }}
/>
</Stack.Navigator>
</NavigationContainer>
);
}
Navigating Between Screens
// In HomeScreen.js
import React from 'react';
import { View, Text, Button, StyleSheet } from 'react-native';
export default function HomeScreen({ navigation }) {
return (
<View style={styles.container}>
<Text style={styles.title}>Home Screen</Text>
<Button
title="Go to Details"
onPress={() => navigation.navigate('Details', { itemId: 86 })}
/>
</View>
);
}
// In DetailsScreen.js
import React from 'react';
import { View, Text, Button, StyleSheet } from 'react-native';
export default function DetailsScreen({ route, navigation }) {
const { itemId } = route.params;
return (
<View style={styles.container}>
<Text style={styles.title}>Details Screen</Text>
<Text>Item ID: {itemId}</Text>
<Button title="Go back" onPress={() => navigation.goBack()} />
</View>
);
}
Styling and UI Libraries
Styling in React Native uses a subset of CSS with some differences. You can create stylesheets using the StyleSheet API, or use inline styles for simpler cases.
Basic Styling Examples
import { StyleSheet, View, Text } from 'react-native';
export default function StyledComponent() {
return (
<View style={styles.container}>
<Text style={styles.title}>Hello, Expo!</Text>
<View style={styles.card}>
<Text style={styles.cardText}>This is a styled card</Text>
</View>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#f5f5f5',
padding: 20,
alignItems: 'center',
justifyContent: 'center',
},
title: {
fontSize: 24,
fontWeight: 'bold',
marginBottom: 20,
color: '#333',
},
card: {
backgroundColor: 'white',
borderRadius: 10,
padding: 20,
width: '100%',
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.1,
shadowRadius: 4,
elevation: 3, // for Android shadow
},
cardText: {
fontSize: 16,
color: '#666',
},
});
Popular UI Libraries
Instead of building all UI components from scratch, you can use UI libraries to speed up development:
- React Native Paper - Material Design components for React Native
expo install react-native-paper
- NativeBase - Accessible, utility-first component library
expo install native-base
- UI Kitten - Customizable design system
expo install @ui-kitten/components
- React Native Elements - Cross-platform UI toolkit
expo install react-native-elements
Testing Your Expo App
Testing is crucial for building reliable apps. Expo supports several testing approaches:
1. Manual Testing with Expo Go
The simplest way to test your app is to use the Expo Go app on your physical device. When you runexpo start
, you can scan the QR code to open your app on your device.
2. Simulators and Emulators
You can also test your app on iOS simulators (macOS only) or Android emulators:
- Press
i
in the Expo DevTools to open in iOS Simulator - Press
a
to open in Android Emulator
3. Unit Testing
For unit testing, you can use Jest with React Native Testing Library:
expo install jest-expo jest @testing-library/react-native
Update your package.json
:
"scripts": {
"test": "jest"
},
"jest": {
"preset": "jest-expo",
"transformIgnorePatterns": [
"node_modules/(?!(jest-)?react-native|@react-native|expo(nent)?|@expo(nent)?/.*|react-navigation|@react-navigation/.*|@unimodules/.*|unimodules|sentry-expo|native-base|@ui-kitten)"
]
}
Example Test
// components/Button.test.js
import React from 'react';
import { render, fireEvent } from '@testing-library/react-native';
import Button from './Button';
describe('Button', () => {
it('renders correctly', () => {
const { getByText } = render(<Button title="Press me" />);
expect(getByText('Press me')).toBeTruthy();
});
it('calls onPress when pressed', () => {
const onPressMock = jest.fn();
const { getByText } = render(<Button title="Press me" onPress={onPressMock} />);
fireEvent.press(getByText('Press me'));
expect(onPressMock).toHaveBeenCalledTimes(1);
});
});
4. End-to-End Testing
For E2E testing, you can use Detox with Expo:
npm install -g detox-cli expo install detox
Setting up Detox with Expo requires additional configuration. Refer to the official documentation for detailed instructions.
Building for Production and Deployment
1. Building With Expo Application Services (EAS)
Expo's recommended way to build your app for production is using EAS Build:
npm install -g eas-cli eas login eas build:configure
This will create an eas.json
file in your project. Next, build your app:
eas build --platform ios # For iOS eas build --platform android # For Android
2. Configuring App Stores Metadata
Before submitting to app stores, you need to prepare:
- App icons (configured in app.json)
- Splash screen (configured in app.json)
- App screenshots (taken manually or with automation)
- Privacy policy (required for both stores)
- App description, keywords, category
3. Submitting to App Stores
With EAS Submit, you can submit your app directly to the stores:
eas submit --platform ios # For App Store eas submit --platform android # For Google Play
Note: For iOS, you need an Apple Developer account ($99/year). For Android, you need a Google Play Developer account ($25 one-time fee).
4. Over-the-Air (OTA) Updates
One of Expo's most powerful features is the ability to push updates without going through app store reviews. This allows you to fix bugs and add features quickly.
To publish an update:
expo publish
With EAS Update (the newer approach):
eas update --branch production --message "Fixed login bug"
Important: OTA updates can only modify JavaScript and assets, not native code. If you need to update native code, you will need to submit a new build to the app stores.