This tutorial is out of date and no longer maintained.
Note: This tutorial takes the approach of converting an existing React application to React Native. An alternative equivalent would be to start a new React Native project with expo init ReactNativeExample
or npx react-native init ReactNativeExample
.
Furthermore, the deploy process documented at the end of this tutorial is Android-specific. However, the process may still be educational for those interested in iOS deployment.
Over the years, building web applications that are mobile-friendly has become easier with the advent of media queries and the introduction of service workers. Using media queries, we can support the screen sizes of different mobile devices. Using service workers, we can implement features like push notifications and background sync.
React Native is a multi-platform solution developed by Facebook that allows you to build mobile apps using JavaScript. These mobile apps are considered multi-platform because they’re written once and deployed across multiple platforms, like Android, iOS, and the web.
In this tutorial, you will build an application that displays user information from the Random User API using React Native components like ScrollView
, Text
, and Image
. The app will run both on the web and mobile using the React Native Web library, which lets you use React Native components and APIs in web applications.
To complete this tutorial, you will need:
Note: You will need an Expo account if you are interested in deploying your app later.
This tutorial was originally written at the time of Node v10. At the time, the dependencies included react
v16.6.3, react-native
v0.9.8, and expo
v31.0.5.
This tutorial was verified with Node v14.2.0, npm
v6.14.5, react
16.13.1, react-native
v0.62.2, and expo
v37.0.11.
Before you get started, you’ll need to set up the project and install project dependencies.
You’ll be making use of Create React App to bootstrap your application. You’re using Create React App because it can be configured to alias React Native.
To bootstrap your application using Create React App, run the following command in your terminal:
- npx create-react-app random-people
Navigate to your new project directory:
- cd random-people
You will be installing polyfills for some of the latest JavaScript APIs - like Promise
and Array.from
- as the transpiler doesn’t provide those.
Run the following commands to install the project’s development dependencies:
- npm install --save-dev babel-plugin-module-resolver@4.0.0
- npm install --save-dev babel-plugin-transform-object-rest-spread@6.26.0
- npm install --save-dev babel-plugin-transform-react-jsx-source@6.22.0
- npm install --save-dev babel-preset-expo@8.1.0
The babel-plugin-module-resolver
is a plugin that resolves your project modules when compiling with Babel. You will use this package to alias react-native
to react-native-web
when setting up the project config.
Note: With Babel 7, proposals using the -transform-
naming convention have been renamed to -proposal-
.
babel-plugin-transform-object-rest-spread
was affected and has been renamed to babel-plugin-proposal-object-rest-spread
. Future development may require changing this step.
We’ll use Expo to build and run the new application. Expo is an open source toolchain built around React Native for building Android and iOS applications. It provides access to the system’s functionality like the Camera and Storage.
First, install Expo CLI globally:
- npm install --global expo-cli@3.25.1
Next, install Expo locally:
- npm install expo@37.0.0
Then, install React Native and React Native Web:
- npm install react-native@0.62.2
- npm install react-native-web@0.12.2
- npm install react-art@16.13.1
After downloading the packages needed to run and build the application, the next step is to set up the configuration files. Create a file called .babelrc
in the root of your project:
- nano .babelrc
Add the following code to the file to configure the transpilers your project will use:
{
"presets": ["babel-preset-expo"],
"env": {
"development": {
"plugins": [
"transform-object-rest-spread",
"transform-react-jsx-source"
]
}
},
"plugins": [
[
"module-resolver",
{
"alias": {
"^react-native$": "react-native-web"
}
}
]
]
}
Create a file named app.json
:
- nano app.json
This file is used to configure parts of your application like the name
, description
, sdkVersion
. You can find the options available for the app.json
file in the Expo documentation.
Add the following lines of code:
{
"expo": {
"sdkVersion": "37.0.0",
"name": "random-people",
"slug": "random-people",
"version": "0.1.0",
"description": "An application for displaying random people",
"primaryColor": "#ff8179"
}
}
Note: At the time of testing this tutorial, the latest Expo SDK version is 37. At a later date, you may need to run expo upgrade
(or manually modify your app.json
and package.json
files and run npm install
).
Let’s update the package.json
file to include commands for running your application on Android and iOS emulators. Also, you’ll include the main
field referencing the App.js
file. This file will act as the entry file for the expo-cli
. Open the package.json
file in your editor:
- nano package.json
Modify the file so it looks like this:
{
"name": "random-people",
"version": "0.1.0",
"private": true,
"main": "./App.js",
// ...
"scripts": {
"start-web": "react-scripts start",
"build-web": "react-scripts build",
"test-web": "react-scripts test",
"eject-web": "react-scripts eject",
"start-native" : "expo start",
"android": "expo start --android",
"ios": "expo start --ios",
"build:ios": "expo build:ios",
"build:android": "expo build:android"
},
// ...
}
Run npm run start-web
to run the application and visit localhost:3000
in a web browser to view the application.
Home
ComponentYour application will display users via the Random User API. Using the API, you will receive a display name and avatar of the returned users.
Within the src
directory, create a file named home.js
:
- nano src/home.js
Add this code to the file to define the component:
import React from "react";
import { ScrollView, ActivityIndicator, StyleSheet } from "react-native";
class Home extends React.Component {
state = {
users: [],
loading: true
};
componentDidMount() {
// TODO: get users
}
render() {
return (
<ScrollView
noSpacer={true}
noScroll={true}
style={styles.container}
>
<ActivityIndicator
style={[styles.centering, styles.gray]}
color="#ff8179"
size="large"
/>
</ScrollView>
);
}
}
var styles = StyleSheet.create({
container: {
backgroundColor: "whitesmoke"
},
centering: {
alignItems: "center",
justifyContent: "center",
padding: 8,
height: '100vh'
},
});
export default Home;
The Home
component renders a ScrollView component that holds the component’s elements.
Currently, the component displays an ActivityIndicator; this will be replaced by the user list when the call to the API is complete.
You will create styles for the elements using the StyleSheet component. This allows you to style the component using properties similar to CSS properties.
Let’s create a method that gets random users from the Random User API. This method will be called during the componentDidMount
lifecycle.
Update the home.js
component to include the getUsers
method:
// ...
class Home extends React.Component {
state = {
// ...
};
componentDidMount() {
this.getUsers();
}
async getUsers() {
const res = await fetch("https://randomuser.me/api/?results=20");
const { results } = await res.json();
this.setState({ users: [...results], loading: false });
}
render() {
// ...
}
}
// ...
You can make requests using the native Fetch
API. Results from the request are parsed and added to state. When the request is complete, you’ll hide the ActivityIncidator
by setting loading
to false.
App
ComponentThe AppComponent
holds the logic for the application. You’ll update the default view created by Create React App to suit that of your application by adding logic to display native components.
Replace the content of your src/App.js
file:
import React from 'react';
import { AppRegistry, StyleSheet, View } from 'react-native';
import Home from './home';
class App extends React.Component {
render() {
return (
<View style={styles.appContainer}>
<Home />
</View>
);
}
}
const styles = StyleSheet.create({
appContainer: {
flex: 1,
},
});
AppRegistry.registerComponent('App', () => App);
export default App;
In this code, you register your <App>
component using the AppRegistry
. The AppRegistry is the entry point of React Native applications.
This version of App.js
displays the web version. You will need a second App.js
that displays the React Native version.
Make a copy of src/App.js
as a new App.js
file in the root of your project:
- cp src/App.js App.js
Note: Be sure to modify the App.js
file in the root of your project with these changes as well, so it works with Expo when you test it.
It is important to change the path for Home
. From ./home
to ./src/home
.
Now, you will create the <UserItem>
component.
UserItem
Each user item will be displayed using a View
component. The View component is an important building block that supports layout using flexbox, styling, and accessibility. The View
component of each item will be within a SwipeableFlatList
. Each item will display the user’s avatar, name, and email.
Create a file called user-item.js
within the src
directory:
- nano src/user-item.js
Add the following code to the file:
import React from "react";
import { View, Image, Text, StyleSheet } from "react-native";
const UserItem = ({ item: user }) => {
return (
<View style={styles.row}>
<Image style={styles.rowIcon} source={user.picture.medium} />
<View style={styles.rowData}>
<Text style={styles.rowDataText}>
{`
${user.name.title}
${user.name.first}
${user.name.last}
`}
</Text>
<Text style={styles.rowDataSubText}>{user.email}</Text>
</View>
</View>
);
};
const styles = StyleSheet.create({
row: {
flexDirection: "row",
justifyContent: "center",
alignItems: "center",
padding: 15,
marginBottom: 5,
backgroundColor: "white",
borderBottomWidth: StyleSheet.hairlineWidth,
borderBottomColor: "rgba(0,0,0,0.1)"
},
rowIcon: {
width: 64,
height: 64,
marginRight: 20,
borderRadius: "50%",
boxShadow: "0 1px 2px 0 rgba(0,0,0,0.1)"
},
rowData: {
flex: 1
},
rowDataText: {
fontSize: 15,
textTransform: "capitalize",
color: "#4b4b4b"
},
rowDataSubText: {
fontSize: 13,
opacity: 0.8,
color: "#a8a689",
marginTop: 4
}
});
export default UserItem;
To display the avatar of each user, you make use of the Image component. The component takes a source
prop which acts as the src
which you are used to on the web. The component can be styled further as you have using styles.rowIcon
property.
Next, you’ll create the UserList
to display each UserItem
.
UserList
The FlatList
component is one that is performant in its list rendering methods. It lazy-loads the items within the list, and only loads more items when the user has scrolled to the bottom of the list. The SwipeableFlatList
is a wrapper around the FlatList
provided by React Native Web, which makes each item within the list swipeable — so each item will reveals a set of actions when swiped.
Note: SwipeableFlatList
was previously part of react-native
, but has since been removed. Users have forked or created their own implementations of SwipeableFlatList
.
This tutorial was been modified to use react-native-swipeable-lists
.
- npm install react-native-swipeable-lists@0.0.1
Let’s create the SwipeableFlatList
for the users returned from the API. Import the SwipeableFlatList
component from the react-native-swipeable-lists
package and update the render function to display the list. Create a file called user-list.js
in the src
directory:
- nano src/user-list.js
Add this code to the file:
import React from "react";
import { SwipeableFlatList } from "react-native-swipeable-lists";
import UserItem from "./user-item";
const UserList = ({ users }) => {
return (
<SwipeableFlatList
data={users}
bounceFirstRowOnMount={true}
maxSwipeDistance={160}
renderItem={UserItem}
/>
);
};
export default UserList;
data
: this prop represents the data that will be fed to each item within the list. The data
prop is usually an array.bounceFirstRowOnMount
: if true, it triggers on a bounce animation on the first item in the list, signifying that it has hidden actions within.maxSwipeDistance
: this prop sets a maximum swipeable distance for each item.renderItem
: this prop takes a function that renders an item; this function will be passed an item
prop that contains the data to be displayed.Let’s update the src/home.js
file to include the new UserList
.
Open the src/home.js
file:
- nano src/home.js
And update it with the following:
import React from "react";
import { ScrollView, ActivityIndicator, StyleSheet } from "react-native";
import UserList from "./user-list";
class Home extends React.Component {
state = {
// ...
};
componentDidMount() {
// ...
}
render() {
return (
<ScrollView
noSpacer={true}
noScroll={true}
style={styles.container}
>
{this.state.loading ? (
<ActivityIndicator
style={[styles.centering, styles.gray]}
color="#ff8179"
size="large"
/>
) : (
<UserList users={this.state.users} />
)}
</ScrollView>
);
}
}
// ...
export default Home;
Now if you visit localhost:3000
on your browser, you will see a list of users.
You added a SwipeableFlatList
component which means each user item is swipeable. Next, add actions that users can swipe to reveal.
Each item within the list will be provided a set of actions that will be revealed when swiped to the left. The actions set will use the TouchableHighlight
component encompassed by the View component. The TouchableHighlight component is used when you require viewers to respond to touches, more or less acting as a button.
Create a file named user-actions.js
in the src
directory:
- nano src/user-actions.js
Add the following contents to the file:
import React from "react";
import { View, TouchableHighlight, Text, Alert, StyleSheet } from "react-native";
const UserActions = () => {
return (
<View style={styles.actionsContainer}>
<TouchableHighlight
style={styles.actionButton}
onPress={() => {
Alert.alert("Tips", "You could do something with this edit action!");
}}
>
<Text style={styles.actionButtonText}>Edit</Text>
</TouchableHighlight>
<TouchableHighlight
style={[styles.actionButton, styles.actionButtonDestructive]}
onPress={() => {
Alert.alert(
"Tips",
"You could do something with this remove action!"
);
}}
>
<Text style={styles.actionButtonText}>Remove</Text>
</TouchableHighlight>
</View>
);
};
const styles = StyleSheet.create({
actionsContainer: {
flex: 1,
flexDirection: "row",
justifyContent: "flex-end",
alignItems: "center",
padding: 10
},
actionButton: {
padding: 10,
color: "white",
borderRadius: 6,
width: 80,
backgroundColor: "#808080",
marginRight: 5,
marginLeft: 5
},
actionButtonDestructive: {
backgroundColor: "#ff4b21"
},
actionButtonText: {
textAlign: "center"
}
});
export default UserActions;
The TouchableHighlight
component takes an onPress
callback that is triggered when the component is clicked. Each callback triggers an Alert
display. Styles are also applied to the encompassing View component and other components on the page.
To include the actions on each user item, update the UserList
component to include the renderQuickActions
prop, which also takes a function.
Open src/user-list.js
and modify it:
// ...
import UserActions from "./user-actions";
const UserList = ({ users }) => {
return (
<SwipeableFlatList
// ...
renderQuickActions={UserActions}
/>
);
};
export default UserList;
Now, when you swipe left on any user item it reveals two actions - Edit
and Remove
.
Header
ComponentNow that you’ve successfully fetched users and displayed them using native components, let’s add a header. Using the SafeAreaView
component, you’ll create an area with defined boundaries. This will act as the header for your application.
Create a new file called header.js
in the src
directory:
- nano src/header.js
Add the following code to the file:
import React from 'react';
import { SafeAreaView, View, Text, StyleSheet } from 'react-native';
const Header = ({ onBack, title }) => (
<SafeAreaView style={styles.headerContainer}>
<View style={styles.header}>
<View style={styles.headerCenter}>
<Text accessibilityRole="heading" aria-level="3" style={styles.title}>{title}</Text>
</View>
</View>
</SafeAreaView>
);
const styles = StyleSheet.create({
headerContainer: {
borderBottomWidth: StyleSheet.hairlineWidth,
borderBottomColor: '#ff4e3f',
backgroundColor: '#ff8179',
},
header: {
padding: 10,
paddingVertical: 5,
alignItems: 'center',
flexDirection: 'row',
minHeight: 50
},
headerCenter: {
flex: 1,
order: 2
},
headerLeft: {
order: 1,
width: 80
},
headerRight: {
order: 3,
width: 80
},
title: {
fontSize: 19,
fontWeight: '600',
textAlign: 'center',
color: 'white'
},
});
export default Header;
Now, let’s add the Header
component to the App
component. This will display a header at the top of the application.
Update the App.js
file to include the Header
component:
// ...
import Header from './header';
class App extends React.Component {
render() {
return (
<View style={styles.appContainer}>
<Header title="Random People" />
<Home />
</View>
);
}
}
// ...
export default App;
After the application refreshes the header will be added to the top of the application.
Note: Be sure to modify the App.js
file in the root of your project with these changes as well, so it works with Expo when you test it.
It is important to change the path for Header
. From ./header
to ./src/header
.
Let’s see the methods you can use to test the application on mobile.
The expo-cli
utility provides method to test the application on mobile devices. The first is using a URL generated after running the application. You can visit this URL on your mobile browser to test the application.
To test the application on mobile, the expo-cli
provides methods to test the application mobile. The first is using a URL generated after running the application. This URL can be visited on your mobile browser to test the application.
Run the following command within your project to run the application with Expo:
- npm run start-native
Expo typically starts your application on port 19002
, so visit localhost:19002
to view the Expo dev tools. Within the dev tools, you can send a link as an SMS or email to your mobile phone.
You can select any of the three connection options — an external tunnel, LAN, or Local connection. For the local connection, your mobile phone and development computer have to be connected to the same network, but the tunnel works regardless.
The next option for testing on a mobile device is using an emulator. Using Android studio or Xcode, you can boot emulators for their respective platforms. Download and install the tool for the platform of choice — Xcode for iOS or Android studio for Android.
After installation, run npm run android
or npm run ios
to start the application on any of the emulators.
Note: This is an optional step that is not required for completing the tutorial. This should be considered for educational purposes on the workflow from project creation to app store submission.
Furthermore, if you are interested in deploying to iOS instead of Android, consult the official documentation.
You’ll be deploying your application to the Android Play store. To achieve this, you’ll need to update the app.json
file to include Android specific properties. Open the app.json
file and update the file to include the android
field:
{
"expo": {
"sdkVersion": "37.0.0",
"name": "random-people",
"slug": "random-people",
"version": "0.1.0",
"description": "An application for displaying random people",
"primaryColor": "#ff8179",
"android": {
"package": "com.random.people"
}
}
}
The android.package
field is a unique value that will represent your package in the app store.
After updating the file, run the npm run build:android
command.
This command will present you with a prompt, asking you to provide a keystore or to generate a new one. If you have an existing keystore, you can select this option or let Expo generate one for your application.
After completion, a download link will be generated for your application. Clicking on this link will trigger a download for your APK.
To deploy the downloaded APK to the Android Play Store, visit the Play Console to create an account. After creating an account, you’ll be required to pay a registration fee of $25 before proceeding. After completing the registration process, visit this page and follow the steps to upload your application to the Play Store.
Using the React Native Web and React Native libraries, you created an application that can be deployed on multiple platforms using native components.
You can view the source code for the demo on GitHub.
If you’d like to learn more about React, take a look at our How To Code in React.js series, or check out our React topic page for exercises and programming projects.
Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.
This textbox defaults to using Markdown to format your answer.
You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!
Are there any companies or big products where react-native-web is being used at production
This is a great tutorial for the Android side of things. I’m a bit confused about how the same app gets deployed to the web though. For example how to host it on a digital ocean droplet?