In today’s digital age, building mobile applications has become a crucial skill for developers. React Native, a framework developed by Facebook, allows you to build mobile apps using JavaScript and React. This guide will walk you through the process of building a mobile app with React Native, covering everything from setting up your environment to deploying your app. By the end of this guide, you’ll have a solid understanding of how to create and manage a mobile app using React Native.
Setting Up Your Development Environment
Before you start building your app, you need to set up your development environment. This involves installing the necessary tools and libraries.
Installing Node.js and npm

Node.js is a JavaScript runtime that allows you to run JavaScript on your computer. npm (Node Package Manager) is used to manage libraries and dependencies. Download and install Node.js from the official website. After installation, verify it by running node -v
and npm -v
in your terminal.
Installing React Native CLI

React Native CLI is a command-line tool that helps you create and manage React Native projects. Install it globally using npm:
npm install -g react-native-cli
Setting Up Android Studio and Xcode

For Android development, you need to install Android Studio. Follow the setup instructions on the official website, ensuring you install the Android SDK and configure the necessary environment variables.

For iOS development, you need Xcode, which is available on the Mac App Store. Install Xcode and open it to complete the setup process.
Creating a New React Native Project
With your environment set up, create a new React Native project using the CLI:
react-native init MyReactNativeApp
Navigate to the project directory:
cd MyReactNativeApp
Running Your Project
To run your project on an Android emulator or device, use:
react-native run-android
To run your project on an iOS simulator or device, use:
react-native run-ios
If everything is set up correctly, you should see your new React Native app running on your emulator or device.
Building Your First Component
React Native uses components to build the user interface. Components are reusable pieces of UI that you can use to build complex applications.
Creating a Simple Component
Start by creating a simple component. Open App.js
and replace its contents with the following code:
import React from 'react';
import { View, Text, StyleSheet } from 'react-native';
const App = () => {
return (
<View style={styles.container}>
<Text style={styles.text}>Hello, React Native!</Text>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#f5fcff',
},
text: {
fontSize: 20,
textAlign: 'center',
margin: 10,
},
});
export default App;
This code defines a simple App
component that displays a “Hello, React Native!” message. The StyleSheet
API is used to define styles for the component.
Adding Interactivity
Next, add some interactivity to your component. Update App.js
to include a button that updates the state:
import React, { useState } from 'react';
import { View, Text, Button, StyleSheet } from 'react-native';
const App = () => {
const [count, setCount] = useState(0);
return (
<View style={styles.container}>
<Text style={styles.text}>Count: {count}</Text>
<Button title="Increment" onPress={() => setCount(count + 1)} />
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#f5fcff',
},
text: {
fontSize: 20,
textAlign: 'center',
margin: 10,
},
});
export default App;
This code adds a button that increments a counter each time it’s pressed. The useState
hook manages the state of the counter.
Styling Your App

Styling in React Native is similar to styling in CSS, but it uses JavaScript objects instead of CSS syntax.
Using Flexbox for Layout
React Native uses Flexbox for layout. Flexbox is a layout model that allows you to design complex layouts with ease.
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#f5fcff',
},
row: {
flexDirection: 'row',
justifyContent: 'space-around',
width: '100%',
},
button: {
margin: 10,
},
});
This code defines a row
style that arranges its children in a row and spaces them evenly. Use this style in your component:
import React, { useState } from 'react';
import { View, Text, Button, StyleSheet } from 'react-native';
const App = () => {
const [count, setCount] = useState(0);
return (
<View style={styles.container}>
<Text style={styles.text}>Count: {count}</Text>
<View style={styles.row}>
<Button title="Increment" onPress={() => setCount(count + 1)} />
<Button title="Decrement" onPress={() => setCount(count - 1)} />
</View>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#f5fcff',
},
row: {
flexDirection: 'row',
justifyContent: 'space-around',
width: '100%',
},
text: {
fontSize: 20,
textAlign: 'center',
margin: 10,
},
});
export default App;
Now your app has two buttons arranged in a row, allowing you to increment and decrement the counter.
Adding Custom Fonts and Icons
Custom fonts and icons can enhance the look and feel of your app. React Native supports custom fonts and vector icons.
Adding Custom Fonts
To add custom fonts, place your font files in a assets/fonts
directory. Then, link the assets in your react-native.config.js
file:
module.exports = {
assets: ['./assets/fonts'],
};
Use the custom font in your styles:
const styles = StyleSheet.create({
text: {
fontFamily: 'CustomFont',
fontSize: 20,
textAlign: 'center',
margin: 10,
},
});
Using Vector Icons
Install the react-native-vector-icons
library:
npm install react-native-vector-icons
Use the icons in your component:
import React, { useState } from 'react';
import { View, Text, Button, StyleSheet } from 'react-native';
import Icon from 'react-native-vector-icons/Ionicons';
const App = () => {
const [count, setCount] = useState(0);
return (
<View style={styles.container}>
<Text style={styles.text}>Count: {count}</Text>
<View style={styles.row}>
<Icon.Button name="add" backgroundColor="#3b5998" onPress={() => setCount(count + 1)}>
Increment
</Icon.Button>
<Icon.Button name="remove" backgroundColor="#8b0000" onPress={() => setCount(count - 1)}>
Decrement
</Icon.Button>
</View>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#f5fcff',
},
row: {
flexDirection: 'row',
justifyContent: 'space-around',
width: '100%',
},
text: {
fontSize: 20,
textAlign: 'center',
margin: 10,
},
});
export default App;
Navigation in React Native

Navigation is a crucial aspect of mobile apps, allowing users to move between different screens and sections. React Native offers several navigation solutions, with react-navigation
being one of the most popular.
Setting Up React Navigation
First, install the necessary packages for React Navigation:
npm install @react-navigation/native @react-navigation/stack
npm install react-native-screens react-native-safe-area-context
Then, install the dependencies:
npm install @react-native-community/masked-view
npm install react-native-gesture-handler
Configure React Native to use these packages by modifying MainActivity.java
:
import com.facebook.react.ReactActivity;
import com.facebook.react.ReactActivityDelegate;
import com.facebook.react.ReactRootView;
import com.swmansion.gesturehandler.react.RNGestureHandlerEnabledRootView;
public class MainActivity extends ReactActivity {
@Override
protected String getMainComponentName() {
return "MyReactNativeApp";
}
@Override
protected ReactActivityDelegate createReactActivityDelegate() {
return new ReactActivityDelegate(this, getMainComponentName()) {
@Override
protected ReactRootView createRootView() {
return new RNGestureHandlerEnabledRootView(MainActivity.this);
}
};
}
}
Creating a Navigation Stack
Set up a basic stack navigator:
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();
const App = () => {
return (
<NavigationContainer>
<Stack.Navigator initialRouteName="Home">
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="Details" component={DetailsScreen} />
</Stack.Navigator>
</NavigationContainer>
);
};
export default App;
Creating Screens
Create the HomeScreen
and DetailsScreen
components in the screens
directory:
// screens/HomeScreen.js
import React from 'react';
import { View, Text, Button, StyleSheet } from 'react-native';
const HomeScreen = ({ navigation }) => {
return (
<View style={styles.container}>
<Text style={styles.text}>Home Screen</Text>
<Button
title="Go to Details"
onPress={() => navigation.navigate('Details', { itemId: 86, otherParam: 'anything you want here' })}
/>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#f5fcff',
},
text: {
fontSize: 20,
textAlign: 'center',
margin: 10,
},
});
export default HomeScreen;
// screens/DetailsScreen.js
import React from 'react';
import { View, Text, Button, StyleSheet } from 'react-native';
const DetailsScreen = ({ route, navigation }) => {
const { itemId, otherParam } = route.params;
return (
<View style={styles.container}>
<Text style={styles.text}>Details Screen</Text>
<Text>Item ID: {itemId}</Text>
<Text>Other Param: {otherParam}</Text>
<Button title="Go to Home" onPress={() => navigation.navigate('Home')} />
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#f5fcff',
},
text: {
fontSize: 20,
textAlign: 'center',
margin: 10,
},
});
export default DetailsScreen;
Passing Parameters Between Screens
React Navigation allows you to pass parameters between screens. In the HomeScreen
, pass parameters to the DetailsScreen
:
<Button
title="Go to Details"
onPress={() => navigation.navigate('Details', { itemId: 86, otherParam: 'anything you want here' })}
/>
Access these parameters in the DetailsScreen
using route.params
:
const { itemId, otherParam } = route.params;
Navigating Back
Use the navigation
object to navigate back to the previous screen:
<Button title="Go back" onPress={() => navigation.goBack()} />
State Management with Redux

Managing state in a large application can be challenging. Redux is a predictable state container for JavaScript apps that helps you manage the state of your app consistently.
Setting Up Redux
First, install the necessary packages:
npm install redux react-redux
Creating the Redux Store
Create a store.js
file to set up the Redux store:
import { createStore } from 'redux';
import { Provider } from 'react-redux';
import rootReducer from './reducers';
const store = createStore(rootReducer);
const App = () => (
<Provider store={store}>
<NavigationContainer>
<Stack.Navigator initialRouteName="Home">
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="Details" component={DetailsScreen} />
</Stack.Navigator>
</NavigationContainer>
</Provider>
);
export default App;
Defining Actions and Reducers
Create actions and reducers to manage the state. In the actions
directory, create a counterActions.js
file:
export const increment = () => {
return {
type: 'INCREMENT',
};
};
export const decrement = () => {
return {
type: 'DECREMENT',
};
};
In the reducers
directory, create a counterReducer.js
file:
const initialState = {
count: 0,
};
const counterReducer = (state = initialState, action) => {
switch (action.type) {
case 'INCREMENT':
return {
...state,
count: state.count + 1,
};
case 'DECREMENT':
return {
...state,
count: state.count - 1,
};
default:
return state;
}
};
export default counterReducer;
Combine reducers in reducers/index.js
:
import { combineReducers } from 'redux';
import counterReducer from './counterReducer';
const rootReducer = combineReducers({
counter: counterReducer,
});
export default rootReducer;
Connecting Redux to Components
Use the connect
function from react-redux
to connect your components to the Redux store. Update the HomeScreen
to use Redux:
import React from 'react';
import { View, Text, Button, StyleSheet } from 'react-native';
import { connect } from 'react-redux';
import { increment, decrement } from '../actions/counterActions';
const HomeScreen = ({ count, increment, decrement, navigation }) => {
return (
<View style={styles.container}>
<Text style={styles.text}>Count: {count}</Text>
<View style={styles.row}>
<Button title="Increment" onPress={increment} />
<Button title="Decrement" onPress={decrement} />
</View>
<Button
title="Go to Details"
onPress={() => navigation.navigate('Details', { itemId: 86, otherParam: 'anything you want here' })}
/>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#f5fcff',
},
row: {
flexDirection: 'row',
justifyContent: 'space-around',
width: '100%',
},
text: {
fontSize: 20,
textAlign: 'center',
margin: 10,
},
});
const mapStateToProps = (state) => ({
count: state.counter.count,
});
const mapDispatchToProps = {
increment,
decrement,
};
export default connect(mapStateToProps, mapDispatchToProps)(HomeScreen);
Integrating APIs
To make your app functional, you often need to integrate with external APIs. React Native allows you to make network requests using libraries like Axios or the built-in fetch
API.
Setting Up Axios

First, install Axios:
npm install axios
Making API Requests
Create a service file to handle API requests. For example, create an apiService.js
file in the services
directory:
import axios from 'axios';
const api = axios.create({
baseURL: 'https://api.example.com',
});
export const fetchData = async () => {
try {
const response = await api.get('/data');
return response.data;
} catch (error) {
console.error('Error fetching data:', error);
throw error;
}
};
Using API Data in Components
Fetch data from the API and display it in a component. Update the HomeScreen
to fetch and display data:
import React, { useEffect } from 'react';
import { View, Text, Button, StyleSheet } from 'react-native';
import { connect } from 'react-redux';
import { increment, decrement } from '../actions/counterActions';
import { fetchData } from '../services/apiService';
const HomeScreen = ({ count, increment, decrement, navigation }) => {
const [data, setData] = React.useState(null);
useEffect(() => {
const getData = async () => {
const result = await fetchData();
setData(result);
};
getData();
}, []);
return (
<View style={styles.container}>
<Text style={styles.text}>Count: {count}</Text>
<View style={styles.row}>
<Button title="Increment" onPress={increment} />
<Button title="Decrement" onPress={decrement} />
</View>
{data && (
<View>
<Text>Data from API:</Text>
<Text>{JSON.stringify(data)}</Text>
</View>
)}
<Button
title="Go to Details"
onPress={() => navigation.navigate('Details', { itemId: 86, otherParam: 'anything you want here' })}
/>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#f5fcff',
},
row: {
flexDirection: 'row',
justifyContent: 'space-around',
width: '100%',
},
text: {
fontSize: 20,
textAlign: 'center',
margin: 10,
},
});
const mapStateToProps = (state) => ({
count: state.counter.count,
});
const mapDispatchToProps = {
increment,
decrement,
};
export default connect(mapStateToProps, mapDispatchToProps)(HomeScreen);
Handling Forms and User Input

Handling forms and user input is a common requirement in mobile apps. React Native provides components like TextInput
for handling user input.
Creating a Form Component
Create a form component to handle user input. For example, create a UserForm
component:
import React, { useState } from 'react';
import { View, TextInput, Button, StyleSheet } from 'react-native';
const UserForm = () => {
const [name, setName] = useState('');
const [email, setEmail] = useState('');
const handleSubmit = () => {
console.log('User data:', { name, email });
// Here you can handle form submission, e.g., send data to an API
};
return (
<View style={styles.container}>
<TextInput
style={styles.input}
placeholder="Name"
value={name}
onChangeText={setName}
/>
<TextInput
style={styles.input}
placeholder="Email"
value={email}
onChangeText={setEmail}
keyboardType="email-address"
/>
<Button title="Submit" onPress={handleSubmit} />
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
padding: 16,
},
input: {
height: 40,
borderColor: 'gray',
borderWidth: 1,
marginBottom: 12,
width: '100%',
paddingHorizontal: 8,
},
});
export default UserForm;
Integrating the Form Component
Integrate the UserForm
component into your HomeScreen
:
import React, { useEffect } from 'react';
import { View, Text, Button, StyleSheet } from 'react-native';
import { connect } from 'react-redux';
import { increment, decrement } from '../actions/counterActions';
import { fetchData } from '../services/apiService';
import UserForm from '../components/UserForm';
const HomeScreen = ({ count, increment, decrement, navigation }) => {
const [data, setData] = React.useState(null);
useEffect(() => {
const getData = async () => {
const result = await fetchData();
setData(result);
};
getData();
}, []);
return (
<View style={styles.container}>
<Text style={styles.text}>Count: {count}</Text>
<View style={styles.row}>
<Button title="Increment" onPress={increment} />
<Button title="Decrement" onPress={decrement} />
</View>
{data && (
<View>
<Text>Data from API:</Text>
<Text>{JSON.stringify(data)}</Text>
</View>
)}
<UserForm />
<Button
title="Go to Details"
onPress={() => navigation.navigate('Details', { itemId: 86, otherParam: 'anything you want here' })}
/>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#f5fcff',
},
row: {
flexDirection: 'row',
justifyContent: 'space-around',
width: '100%',
},
text: {
fontSize: 20,
textAlign: 'center',
margin: 10,
},
});
const mapStateToProps = (state) => ({
count: state.counter.count,
});
const mapDispatchToProps = {
increment,
decrement,
};
export default connect(mapStateToProps, mapDispatchToProps)(HomeScreen);
Handling Navigation State
Managing navigation state is crucial for creating a smooth user experience. React Navigation provides hooks and tools to manage navigation state effectively.
Using Navigation Hooks
React Navigation provides hooks like useNavigation
and useRoute
to access the navigation and route objects in functional components.
Using useNavigation
The useNavigation
hook provides access to the navigation object:
import React from 'react';
import { View, Text, Button, StyleSheet } from 'react-native';
import { useNavigation } from '@react-navigation/native';
const HomeScreen = () => {
const navigation = useNavigation();
return (
<View style={styles.container}>
<Text style={styles.text}>Home Screen</Text>
<Button
title="Go to Details"
onPress={() => navigation.navigate('Details', { itemId: 86, otherParam: 'anything you want here' })}
/>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#f5fcff',
},
text: {
fontSize: 20,
textAlign: 'center',
margin: 10,
},
});
export default HomeScreen;
Using useRoute
The useRoute
hook provides access to the route object:
import React from 'react';
import { View, Text, StyleSheet } from 'react-native';
import { useRoute } from '@react-navigation/native';
const DetailsScreen = () => {
const route = useRoute();
const { itemId, otherParam } = route.params;
return (
<View style={styles.container}>
<Text style={styles.text}>Details Screen</Text>
<Text>Item ID: {itemId}</Text>
<Text>Other Param: {otherParam}</Text>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#f5fcff',
},
text: {
fontSize: 20,
textAlign: 'center',
margin: 10,
},
});
export default DetailsScreen;
Persisting Navigation State
To persist navigation state across app restarts, use the asyncStorage
library to store and retrieve navigation state.
Setting Up asyncStorage
Install the @react-native-async-storage/async-storage
library:
npm install @react-native-async-storage/async-storage
Persisting Navigation State
Update your App.js
to persist navigation state:
import React, { useState, useEffect } from 'react';
import { NavigationContainer } from '@react-navigation/native';
import {
createStackNavigator } from '@react-navigation/stack';
import AsyncStorage from '@react-native-async-storage/async-storage';
import HomeScreen from './screens/HomeScreen';
import DetailsScreen from './screens/DetailsScreen';
const Stack = createStackNavigator();
const App = () => {
const [initialState, setInitialState] = useState();
useEffect(() => {
const restoreState = async () => {
try {
const savedState = await AsyncStorage.getItem('NAVIGATION_STATE');
const state = savedState ? JSON.parse(savedState) : undefined;
setInitialState(state);
} catch (e) {
console.error('Failed to load navigation state', e);
}
};
if (!initialState) {
restoreState();
}
}, [initialState]);
return (
<NavigationContainer
initialState={initialState}
onStateChange={(state) =>
AsyncStorage.setItem('NAVIGATION_STATE', JSON.stringify(state))
}
>
<Stack.Navigator initialRouteName="Home">
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="Details" component={DetailsScreen} />
</Stack.Navigator>
</NavigationContainer>
);
};
export default App;
Testing Your React Native App
Testing is a crucial part of the development process. React Native provides several tools and libraries for testing your app.
Setting Up Jest

Jest is a popular testing framework for JavaScript. It works well with React Native and provides a comprehensive testing solution.
Installing Jest
First, install Jest and related libraries:
npm install --save-dev jest babel-jest @testing-library/react-native
Configuring Jest
Create a jest.config.js
file in your project root:
module.exports = {
preset: 'react-native',
setupFilesAfterEnv: ['@testing-library/react-native/cleanup-after-each'],
transformIgnorePatterns: [
'node_modules/(?!(@react-native|react-native|@react-native-community|react-native-safe-area-context|@react-native-async-storage|react-native-vector-icons)/)',
],
};
Writing Tests
Write tests for your components using Jest and React Testing Library. Create a HomeScreen.test.js
file in the __tests__
directory:
import React from 'react';
import { render, fireEvent } from '@testing-library/react-native';
import HomeScreen from '../screens/HomeScreen';
test('renders correctly', () => {
const { getByText } = render(<HomeScreen />);
expect(getByText('Home Screen')).toBeTruthy();
});
test('navigates to details screen on button press', () => {
const { getByText } = render(<HomeScreen />);
fireEvent.press(getByText('Go to Details'));
expect(getByText('Details Screen')).toBeTruthy();
});
Running Tests
Run your tests using the following command:
npm test
Debugging Your App
React Native provides several tools to help you debug your app.
Using React Native Debugger
React Native Debugger is a standalone app for debugging React Native applications. It includes React DevTools, Redux DevTools, and a network inspector.
Download and install React Native Debugger from the official website.
Launch React Native Debugger and connect it to your app:
# Open the debugger
open "rndebugger://set-debugger-loc?host=localhost&port=8081"
# Start your app
react-native start
Using the Developer Menu
Access the Developer Menu in your app by shaking your device or pressing Cmd + D
(iOS) or Cmd + M
(Android) on the emulator. The Developer Menu provides options to reload the app, enable live reloading, and access debugging tools.
Deploying Your React Native App
Once your app is ready, you need to deploy it to the App Store (iOS) and Google Play (Android).
Preparing for Deployment
Before deploying, you need to prepare your app by configuring app icons, splash screens, and app metadata.
Configuring App Icons and Splash Screens
Create app icons and splash screens for your app. Use a tool like App Icon Generator to generate the necessary assets.
Place the assets in the appropriate directories:
- For iOS, place app icons in
ios/YourApp/Images.xcassets/AppIcon.appiconset/
and splash screens inios/YourApp/Images.xcassets/LaunchImage.launchimage/
. - For Android, place app icons in
android/app/src/main/res/mipmap-*
and splash screens inandroid/app/src/main/res/drawable/
.
Configuring App Metadata
Configure app metadata such as the app name, version, and package identifier in the following files:
- For iOS, update the
Info.plist
file inios/YourApp/
. - For Android, update the
AndroidManifest.xml
file inandroid/app/src/main/
.
Building and Signing the App
Build and sign your app for deployment.
Building and Signing for iOS
Open your project in Xcode and follow these steps:
- Select your target and go to the
Signing & Capabilities
tab. - Select your development team and configure signing settings.
- Go to
Product > Archive
to build and archive your app. - Use the
Organizer
window to upload your app to the App Store.
Building and Signing for Android
Use the following commands to build and sign your app for Android:
cd android
./gradlew assembleRelease
Locate the generated APK file in android/app/build/outputs/apk/release/
and follow the instructions to sign the APK.
Publishing the App
Follow the instructions provided by Apple and Google to publish your app to the App Store and Google Play.
Conclusion
Building mobile apps with React Native is an exciting journey that allows you to create high-quality apps using JavaScript and React. By following this step-by-step guide, you can set up your development environment, build components, handle navigation and state management, integrate APIs, handle user input, test, debug, and finally deploy your app. With practice and continuous learning, you’ll be able to create robust and scalable mobile applications with React Native.
Read Next: