Running React Native everywhere: The Web
Tags: javascript, webdev, react, reactnative
Published: 2021-09-26 17:17:56

---
title: Running React Native everywhere: The Web
published: true
date: 2021-09-26 17:17:00 UTC
tags: #javascript #webdev #react #reactnative
canonical_url: https://mmazzarolo.com/blog/2021-09-22-running-react-native-everywhere-web/
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/oi6g2daaghtnlshqsbch.png
---

## TL;DR

Fourth part of the ["Running React Native everywhere"](https://dev.to/mmazzarolo/running-react-native-everywhere-4gpp) series: a tutorial about structuring your monorepo to run multiple React Native apps targeting different platforms.

This time, we'll focus on running React Native on the web.

## About React Native for Web

**[React Native for Web](https://github.com/necolas/react-native-web)** is an accessible implementation of React Native's components and APIs that is interoperable with React DOM.  
React Native for Web translates all the references of React Native's components (e.g. `View`) and APIs to their HTML & DOM counterpart (e.g., `div`).

[The React Native for Web homepage](https://necolas.github.io/react-native-web/) does a great job at highlighting why you should try it:

- **Accessible HTML.** Support different devices and input modes, render semantic tags.
- **High-quality interactions.** Support gestures and multiple input modes (touch, mouse, keyboard).
- **Reliable styles.** Rely on scoped styles and automatic vendor-prefixing. Support RTL layouts.
- **Responsive containers.** Respond to element resize events.
- **Incremental adoption.** Interoperates with existing React DOM components. Bundle only what you use.

If you've already built a React Native app and you're planning to port it to the web, I recommend giving React Native for Web a try.

One of the most common mistakes people make about React Native for Web is assuming that it **is** React Native.  
Let's be clear: in a React Native for Web project you're not "using" React Native, you're just aliasing every component and API from `react-native` to `react-native-web`.

```js
// Running the following:
import { Button, Image, StyleSheet, Text, View } from "react-native";
// At build time is translated to:
import { Button, Image, StyleSheet, Text, View } from "react-native-web";
```

Instead of thinking about React Native for Web as a library for building mobile apps that run on the web, think of it as a website that uses React Native as a "components and API framework".

Because React Native for Web **is** a React website, you can use front-end tools to build and run it.  
For example, you can build it with [Webpack](https://webpack.js.org/) or [Rollup](https://rollupjs.org/guide/en/) instead of [Metro bundler](https://facebook.github.io/metro/).

Like for React Native for Windows + macOS, you **can** add React Native for Web to an existing mobile project. I've already written about this option in the past in ["Run your React Native app on the web with React Native for Web"](https://mmazzarolo.com/blog/2020-10-24-adding-react-native-web/).  
However, in this tutorial we'll add it as a separate `web` workspace.

## Create React App (CRA)

React Native for Web is compatible with multiple frameworks and tools. You can use it with [Create React App](https://github.com/facebook/create-react-app), [Next.js](https://github.com/zeit/next.js/tree/master/examples/with-react-native-web), [Gatsby](https://github.com/slorber/gatsby-plugin-react-native-web), [Expo (!)](https://docs.expo.io/workflow/web/), or you can create a custom build process.

To keep it simple, in this tutorial we'll use Create React App, which is a basic way to setup a simple, web-only React app with built-in support for aliasing `react-native-web` to `react-native`.

Create React App is very limited in its configuration options, so we'll use [CRACO](https://github.com/gsoft-inc/craco) (Create React App Configuration Override) to customize its Webpack configuration to make it compatible with Yarn workspaces.

## Setup

First of all, add the `react-native-web` library to the `nohoist` list in the root's `package.json`:

```diff
 {
   "name": "my-app",
   "version": "0.0.1",
   "private": true,
   "workspaces": {
     "packages": [
       "packages/*"
     ],
     "nohoist": [
       "**/react",
       "**/react-dom",
       "**/react-native",
       "**/react-native/**",
       "**/react-native-windows",
+      "**/react-native-web"
     ]
   }
 }
```

Then, from the `packages` directory, scaffold a new Create React App project:

```bash
npx create-react-app my-app && mv my-app web
```

Rename the package name:

```diff
 {
-  "name": "my-app",
+  "name": "@my-app/web",
   "version": "0.0.0",
   "private": true,
```

And install `react-native-web`:

```bash
cd web && yarn add react-native-web
```

The cool thing about Create React App is that adding `react-native-web` to our dependencies is enough to make it automatically resolve `react-native-web` instead of `react-native`.

To start using our React Native app within the web project, add it to the JavaScript entry point:

```diff
 import React from "react";
 import ReactDOM from "react-dom";
 import "./index.css";
-import App from "./App";
+import { App } from "@my-app/app";

 ReactDOM.render(
   <React.StrictMode>
     <App />
   </React.StrictMode>,
   document.getElementById("root")
 );
```

If Create React App supported Yarn workspaces out-of-the-box, what we've done so far would have been enough to run the app... unfortunately, it doesn't.  
Luckily, we can use CRACO (or other tools such as [`customize-cra`](https://github.com/arackaf/customize-cra) or [`react-app-rewired`](https://github.com/timarney/react-app-rewired)) to customize the Webpack configuration used by Create React App to resolve packages imported from other workspaces.

Install CRACO and [`react-native-monorepo-tools`](https://github.com/mmazzarolo/react-native-monorepo-tools/):

```bash
yarn add -D @craco/craco react-native-monorepo-tools
```

Create a `craco.config.js` file at the root of your `web` workspace.  
We'll use it to:

- Update the `babel-loader` config used in Webpack to allow importing from directories outside of the `web` workspace.
- Use [Webpack's aliases](https://webpack.js.org/configuration/resolve/) to ensure all the libraries in the `nohoist` list are resolved from `web/node_modules`. This ensures the build process doesn't bundle the same library twice if its present in multiple workspaces.
- Inject the `__DEV__` global variable in the codebase. `__DEV__` is commonly used in React Native apps to determine if we're running in development or production mode (like `process.env.NODE_ENV` on the web).

```javascript
const webpack = require("webpack");
const { getWebpackTools } = require("react-native-monorepo-tools");

const monorepoWebpackTools = getWebpackTools();

module.exports = {
  webpack: {
    configure: (webpackConfig) => {
      // Allow importing from external workspaces.
      monorepoWebpackTools.enableWorkspacesResolution(webpackConfig);
      // Ensure nohoisted libraries are resolved from this workspace.
      monorepoWebpackTools.addNohoistAliases(webpackConfig);
      return webpackConfig;
    },
    plugins: [
      // Inject the React Native "__DEV__" global variable.
      new webpack.DefinePlugin({
        __DEV__: process.env.NODE_ENV !== "production",
      }),
    ],
  },
};
```

To use the updated Webpack configuration, swap `react-scripts` in favour of `craco` in the workspace `start` and `build` scripts:

```diff
 {
   "name": "@my-app/web",
   "version": "0.0.0",
   "private": true,
   "scripts": {
-    "start": "react-scripts start",
+    "start": "craco start",
-    "build": "react-scripts build",
+    "build": "craco build",
     "test": "react-scripts test --watchAll=false --passWithNoTests",
     "eject": "react-scripts eject"
   },
```

And while you're at it, update the root `package.json` so that you can invoke the web scripts from the root of the monorepo:

```json
  "web:start": "yarn workspace @my-app/web start",
  "web:build": "yarn workspace @my-app/web build"
},
```

We're done.  
We can now run `yarn web:start` to run your app in development mode, and `yarn web:build` to create a production build.

![](https://mmazzarolo.com/static/8155faa0a45a7cc5a67561f82b0c071b/b1001/screenshot.png)

## Compatibility and platform-specific code

React Native for Web provides compatibility with the vast majority of React Native’s JavaScript API. Features deprecated in React Native should be considered unsupported in React Native for Web.  
See ["React Native compatibility"](https://necolas.github.io/react-native-web/docs/react-native-compatibility/) for details.

Also, like for Windows and macOS, React Native provides two ways to organize your web-specific code and separate it from the other platforms:

- Using the [`platform` module](https://reactnative.dev/docs/platform-specific-code#platform-module).
- Using [platform-specific file extensions](https://reactnative.dev/docs/platform-specific-code#platform-specific-extensions).

## On Expo & Next.js

In this tutorial we're not using [Expo](https://expo.io/) because it's not compatible (yet) with every platform we're supporting. Still, [Expo for Web](https://docs.expo.io/workflow/web/) supports React Native for Web out-of-the-box, provides dozens of additional cross-platform APIs, includes web build optimizations, and is compatible with the broader React Native ecosystem.

And thanks to [`@expo/next-adapter`](https://github.com/expo/expo-cli/tree/master/packages/next-adapter), you can even use [Next.js](https://nextjs.org/) to control your Expo for Web app.  
For details, check ["Using Next.js with Expo for Web"](https://docs.expo.dev/guides/using-nextjs/).

## Next steps

In the next step, we'll re-use the `web` codebase we just created as a boilerplate to support [Electron](https://www.electronjs.org/) & browser extensions.

Stay tuned!

- [Overview](https://dev.to/mmazzarolo/running-react-native-everywhere-4gpp)
- [Monorepo setup](https://dev.to/mmazzarolo/running-react-native-everywhere-yarn-workspaces-monorepo-3j5g)
- [Android & iOS](https://dev.to/mmazzarolo/running-react-native-everywhere-android-ios-3451)
- [Windows & macOS](https://dev.to/mmazzarolo/running-react-native-everywhere-windows-macos-7lp)
- **The Web (☜ you're here)**
- [Electron & browser extension](https://dev.to/mmazzarolo/running-react-native-everywhere-browser-extensions-electron-46hh)


> _Originally published at [mmazzarolo.com](https://mmazzarolo.com/blog/2021-09-22-running-react-native-everywhere-web/)_