Kumar Harsh Technical writer and software developer based in India.

The complete guide to React Native for Web

6 min read 1712

React Native Logo

Editor’s note: This article was updated on 16 March 2022 to include information about the most recent release of React Native for Web and account for breaking changes.

Introduction

One of the most important decisions to make when starting a new project is choosing your development stack. You need to select a platform on which most of your users are active, but at the same time, the platform shouldn’t be too restrictive.

The future growth of your application might require you to reach users on an entirely different platform. As such, your initial stack should be versatile enough to accommodate such growth requirements.

Speaking of versatile technologies, React Native for Web enables developers to use React Native Components and APIs on the web. This is a game-changer in the battle between mobile-first frameworks, as React Native is one of the most popular choices to build cross-platform mobile applications.

In this article, we will talk about the buzz surrounding React Native for Web and set up a demo project with it, too!

Contents

Can React Native be used for web and mobile?

Yes! With React Native for Web, developers can write a single React Native application that can run natively on Android and iOS, as well as on a web browser using standard web technologies.

But considering that React, the conceptual parent of React Native, is built specifically for the web, it’s worth questioning why such technology is necessary in the first place.

The reality is that the foundation of a mobile app is entirely different from the foundation of a web app. For instance, React uses basic HTML5 elements like div and span to construct its layout, while React Native uses custom UI APIs like View and Text. Due to this difference, very few technologies support building apps for both web and mobile.

React Native for Web is an attempt to bridge this gap. It is built to help create truly cross-platform applications using a single React Native codebase. But how does it do so? Let’s find out.

Using React Native on the browser: How does React Native for Web work?

As mentioned previously, web apps and mobile apps have very different foundations. There are distinct APIs in both platforms to do the same job, like rendering the UI.

We made a custom demo for .
No really. Click here to check it out.

The idea behind any framework that aims to bridge this gap is simple: use one platform’s APIs to build the equivalent of the other platform’s APIs. React Native for Web does just that.

React Native for Web provides browser-compatible equivalents of React Native components. For example, if a <View> is used to render views in React Native mobile, a browser-compatible version of this <View> is available in React Native for Web, which has a method to compile this view down to a web <div>.

This raises an important point: not all native components can be made browser-friendly. Some mobile-oriented components make use of mobile hardware APIs, which are not directly accessible by a web browser.

This means we can’t use React Native for Web to completely port an app into its web version. But we can still port a major part of it, and more often than not, that is sufficient.

Styling is another area of difference between React and React Native. In React, we can use a long list of styling solutions, including CSS, Sass, CSS-in-JS, etc. In React Native, on the other hand, all styles are written in CSS-in-JS.

Perhaps unsurprisingly, React Native for Web adopts the CSS-in-JS approach of React Native, ensuring that the same set of styles works well in both mobile and web.

Integrations

You can visit the React Native Directory to find React Native packages that provide web support.

The following popular web frameworks provide APIs for integration as well:

Getting Started with React Native for Web

Now that we have a theoretical understanding of the concepts behind this addition to the React Native family of APIs, let’s go through the installation process to understand how to set up our web-friendly React Native application.

There are two ways we can set up a React Native for web applications: Expo and Create React App.

Expo

Expo is a framework for universal React application development. It supports developing Android, iOS, and web applications via Expo for web.

Expo for web comes with a collection of cross-platform APIs out of the box, including web build optimizations, and provides a universal SDK for running native mobile Expo apps on the web using React Native for Web.

To bootstrap a React Native for Web application using Expo for web, follow the steps below:

// initialize project with npm
npx expo-cli init your-app-name
cd my-app

// install react-dom and react-native-web
npm install react-dom react-native-web
npx expo-cli start                                                       

Create React App

Create React App is the standard way to bootstrap web-only React applications. Although it comes with built-in support for aliasing react-native-web to react-native, it is still recommended to use Expo.

However, we can bootstrap a React Native for Web application with CRA by following the steps below:

// initialize create-react-app project
npx create-react-app your-app-name
cd my-app

// install react-dom and react-native-web
npm install react-native-web
npm start

Prepare your React Native application for the web

Let’s start by bootstrapping your Expo project with the following:

// initilaize project
npx create-react-app <-- app-name -->

Note that by using npx, we use the latest version of the CRA package. This removes the hassle of managing its version locally when we install it globally:

// change directory
cd <-- app-name -->

// install dependencies
npm i react-native-web

Now that we have the main packages installed, we will register our App root component using React native’s [AppRegistry.registerComponent](https://reactnative.dev/docs/appregistry) — note that if you are using expo-cli this step is not required.

AppRegistry is the JavaScript entry point to running all React Native apps, and you can register the App root component by replacing the code in the index.js file with the following:

import { AppRegistry } from "react-native";
import App from "./App";

AppRegistry.registerComponent("App", () => App);
AppRegistry.runApplication("App", {
  rootTag: document.getElementById("root")
});

Next, let’s add some boilerplate code that uses the React Native CLI. Replace the code in the App component with the code below:

import React from 'react';
import { Button, StyleSheet, Text, View, Dimensions } from 'react-native';

function Link(props) {
  return <Text {...props} accessibilityRole="link" style={StyleSheet.compose(styles.link, props.style)} />;
}

function App() {
  return (
    <View style={styles.container}>
      <View style={styles.header}>
        <Text style={styles.title}>Welcome to React Native for Web</Text>
      </View>
      <Text style={styles.text}>
        This starter project enables you to build web application using React Native module. .
      </Text>
      <Text style={styles.text}>
        Built with <Link href="https://github.com/facebook/create-react-app">Create React App</Link> and{' '}
        <Link href="https://github.com/necolas/react-native-web">React Native for Web</Link>
      </Text>
      <Button onPress={() => {}} title="Example button" />
    </View>
  );
}
let ScreenHeight = Dimensions.get('window').height;
const styles = StyleSheet.create({
  container: {
    flex: 1,
    height: ScreenHeight,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#F5FCFF'
  },
  header: {
    padding: 20
  },
  title: {
    fontWeight: 'bold',
    fontSize: '1.5rem',
    marginVertical: '1em',
    textAlign: 'center'
  },
  text: {
    lineHeight: '1.5em',
    fontSize: '1.125rem',
    marginVertical: '1em',
    textAlign: 'center'
  },
  link: {
    color: '#1B95E0'
  },
  code: {
    fontFamily: 'monospace, monospace'
  }
});
export default App;

Now, we can test the app by running:

npm start

And we should get the following.

Blank React Native for Web app

Customization and enhancements with webpack and Babel

CRA does a lot for us under the hood, such as package aliasing and optimized webpack configuration.

Out of the box, CRA provides built-in support for aliasing React Native for Web to React Native using webpack’s resolve.alias. Consequently, when we import React Native into our application, it resolves to React Native for Web.

CRA also provides an internal optimized webpack configuration, but for savvy developers who have custom needs (such as using Babel for transpilation or build enhancement) you should bootstrap your application using Expo. Also, be sure to add your custom webpack configurations to your webpack.config.js file as seen in the example below:

 const webpack = require('webpack');

module.exports = {
  entry: {
    main: './index.web.js',
  },
  module: {
    loaders: [
      {
        test: /\.js?$/,
        exclude: /node_modules/,
        loader: 'babel',
        query: {
          presets: ['es2015', 'react'],
        },
      },
    ],
  },
  resolve: {
    alias: {
      'react-native$': 'react-native-web',
    },
  },
};

In the code above, webpack uses the babel-loader to convert ES2015 and JSX code into browser-friendly JavaScript.We have manually set up package aliasing using webpack’s resolve.alias. The $ simply specifies a direct match.

Note that in order to use the above configuration, we need to manually install the required dependencies as seen below:

npm i webpack babel-loader babel-preset-react babel-preset-es2015
npm i webpack-dev-server --save-dev

Alternatively, Babel supports module aliasing by using the babel-plugin-module-resolver:

{
  "plugins": [
    ["module-resolver", {
      "alias": {
        "^react-native$": "react-native-web"
      }
    }]
  ]
}

Further recommendations

Although CRA provides some out-of-the-box functionality, it is generally recommended that you bootstrap your application using Expo, especially if you are building a multi-platform app.

For build-time optimizations, it is recommended you use the babel-plugin-react-native-web plugin. You can install it as a dev dependency like so:

npm install --save-dev babel-plugin-react-native-web

Plus, platform-specific code can be added using the platform module:

import { Platform } from 'react-native';

const styles = StyleSheet.create({
  height: (Platform.OS === 'web') ? 200 : 100,
});

Conclusion

Looking back at this tutorial, we discussed what React Native for Web is, the problem it solves, and how it works. We then learned about how to set up a React Native for Web project before setting out to create our own React Native for Web application from scratch.

We also learned about using custom webpack configurations and adding enhancements using Babel.

Interestingly, React Native for Web is used in production web apps by Twitter, Uber, and Major League Soccer. I hope you learned enough to try it out in your next multi-platform project.

LogRocket: Instantly recreate issues in your React Native apps.

LogRocket is a React Native monitoring solution that helps you reproduce issues instantly, prioritize bugs, and understand performance in your React Native apps.

LogRocket also helps you increase conversion rates and product usage by showing you exactly how users are interacting with your app. LogRocket's product analytics features surface the reasons why users don't complete a particular flow or don't adopt a new feature.

Start proactively monitoring your React Native apps — .

Kumar Harsh Technical writer and software developer based in India.

One Reply to “The complete guide to React Native for Web”

  1. Hello Kumar Harsh,

    thank you for this tutorial is very well explained and is easy to follow, I got to the last step but Im having a problem when I try to launch the local server with the command:

    ./node_modules/.bin/webpack-dev-server -inline

    this is the error Im receiving:
    Error: Cannot find module ‘webpack-cli/bin/config-yargs’
    at Function.Module._resolveFilename (internal/modules/cjs/loader.js:636:15)
    at Function.Module._load (internal/modules/cjs/loader.js:562:25)

    and this are the versions Im using in my package.json:
    “webpack”: “^5.38.1”,
    “webpack-cli”: “^4.7.2”,
    “webpack-dev-server”: “^3.11.2”

    thanks in advance for the help.

Leave a Reply