React Native For Web: Merging the worlds of mobile and web

Shikher Mishra
Technology at Nineleaps
5 min readNov 10, 2020

One of the hardest decisions to make when starting a new app is which platforms to target. A mobile app gives you more control and better performance but isn’t as universal as the web.

What about trying to build a mobile app and a responsive web app? Ultimately, the best experience for your customers is for your app to work everywhere, but the development and maintenance costs of that can be prohibitive.

We have already seen how React Native can help you make iOS and Android apps with a shared codebase, without sacrifices in quality. But what about the web? React-native-web is one of the most amazing ideas I’ve stumbled upon in a while. For UI developers, it makes a longtime dream a reality: the ability to create an application that runs on both phones and browsers with just one codebase.

React Native as a universal UI language

You might be thinking, “Wait! doesn’t React already work on the web?” You wouldn’t be wrong. Unfortunately, traditional React and React Native build on a different set of primitives. React uses <div>, <p> and <input>, whereas React Native uses <View>, <Text> and <TextInput>. There are good historical reasons for this, since the building blocks of a web page and of a mobile app are quite different. Nonetheless, it would be great if we could use a single set of shared components.

One of the reason what really makes React Native superior to React for creating universal apps — is that React Native is a pure UI language. React Native for Web’s solution is to provide browser-compatible implementations of React Native’s components — meaning, for example, that the <View> of React Native has a DOM-based version that knows how to render to a <div>. While not every React Native component is supported, enough of them are that you could (hopefully) share the majority of your codebase.

Nonetheless, it’s possible to translate React Native primitives to the DOM language by using HTML tags — that (and more) is what react-native-web does for us.

React Native for Web

Repository — https://github.com/necolas/react-native-web

Compatibility: React Native >= 0.63.

“React Native for Web” makes it possible to run React Native components and APIs on the web using React DOM.

  • High-quality web interfaces: makes it easy to create fast, adaptive web UIs in JavaScript. It provides native-quality interactions, support for multiple input modes (touch, mouse, keyboard), optimized vendor-prefixed styles, built-in support for RTL layout, built-in accessibility, and integrates with React Dev Tools.
  • Write once, render anywhere: interoperates with existing React DOM components and is compatible with the majority of the React Native API. You can develop new components for native and web without rewriting existing code. React Native for Web can also render to HTML and critical CSS on the server using Node.js.

Big Giants using React Native for Web in production?

Twitter, Expo, Major League Soccer, Flipkart, Uber, The Times, DataCamp.

Getting started

This guide will help you render components and applications with React Native for Web.

If you’re not familiar with setting up a new React web project, please refer to the React documentation.

Install

npm install react react-dom react-native-web

Recommended starter kits

There are two ways to get started with a new React-native-web project. You can use either npm or yarn.

1. Expo

Expo is a framework and a platform for universal React applications. Expo for Web uses React Native for Web and provides dozens of additional cross-platform APIs.

npm install expo-cli --global
expo init my-app
cd my-app
expo start

2. Create React App

Create React App is a basic way to setup a simple, web-only React app with built-in support for aliasing react-native-web to react-native. However, it's generally recommended that you use Expo.

npx create-react-app my-app
cd my-app
npm install react-native-web
npm start

Standalone configurations

Configuring a module bundler

If you have a custom setup, you may choose to configure your module bundler to alias the package to react-native.

For example, modify your webpack configuration as follows:

// webpack.config.js
module.exports = {
// ...the rest of your config
resolve: {
alias: {
'react-native$': 'react-native-web'
}
}
}

Configuring Babel

Babel supports module aliasing using babel-plugin-module-resolver

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

Configuring Jest

Jest can be configured using the provided preset. This will map react-native to react-native-web and provide appropriate mocks:

{
"preset": "react-native-web"
}

Please refer to the Jest documentation for more information.

Configuring Flow

Flow can be configured to understand the aliased module:

[options]
module.name_mapper='^react-native$' -> 'react-native-web'

You may also need to include a custom libdef (example) in your config.

Configuring Node.js

Node.js can alias react-native to react-native-web using module-alias. This is useful if you want to pre-render the app (e.g., server-side rendering or build-time rendering).

// Install the `module-alias` package as a dependency first
const moduleAlias = require("module-alias");
moduleAlias.addAliases({
"react-native": require.resolve("react-native-web"),
});
moduleAlias();

Configure Storybooks

Why would I want a storybook with react-native-web?

If you are using Storybook, having an explorer to see how to render your native mobile components matters to you. Why would it be different within the web?

Start by adding storybook/react to your project:

npm install @storybook/react --save-dev

Now create a folder storybook-web at the root of your project. This is where we are gonna put all our configuration for the storybook web.

Create a file main.js in the folder storybook-web which contain the following code:

module.exports = {
stories: ['../src/components/**/*.stories.js'],
webpackFinal: (config) => {
config.resolve.alias = {
...(config.resolve.alias || {}), // Transform all direct `react-native` imports to `react-native-web`
'react-native$': 'react-native-web',
'@storybook/react-native': '@storybook/react',
}; config.resolve.extensions = ['.web.js', '.js', '.json']; // mutate babel-loader
config.module.rules[0].use[0].options.plugins.push(['react-native-web', { commonjs: true }]); return config;
},
};

When building on the web, this will replace every react-native import into react-native-web import and doing the same with @storybook/react-native and @storybook/react.

If you use some native libs which are not supported by react-native-web, you can easily create your own alias.

Let’s now create the file addons.js which will add the addons you normally use on storybook mobile:

import '@storybook/addon-actions/register';
import '@storybook/addon-knobs/register';
import '@storybook/addon-links/register';

The minimal configuration for our project is set! We just need to add a command in the package.json to launch a storybook on the web!

In the scripts object, add the following command:

"storybook:web": "ln -s ../@storybook/react/bin/index.js storybookweb && mv storybookweb node_modules/.bin && storybookweb -p 6006 --config-dir storybook-web"

A quick explanation: @storybook/react-native and @storybook/react to have the same link name to launch their binary: start-storybook.

To be able to differentiate them, we have to create a new link for @storybook/react. This is what we do here:

ln -s ../@storybook/react/bin/index.js storybookweb

I named it storybook web but you can change it as you want.
After created, we move it in node_modules/.bin and we use our new command to launch @storybook/react, specifying the configuration directory:

mv storybookweb node_modules/.bin && storybookweb -p 6006 --config-dir storybook-web"

Now you can launch yarn storybook:web and enjoy your components on web!

References

Smashing Magzine , Logrocket

” Now You can Finally showoff” 😍

— Shikher Mishra

--

--