React Native provided a very powerful abstraction for Android and iOS APIs, some libraries extended the same abstraction to Windows, Web, Virtual Reality and others. Of these libraries, react-native-web is one awesome library which makes it possible to run the same app on the browser.

Using this library we can share almost all Main-Module(react-native) code, but there may be some logic which is non-transferable like Router, NativeModules (or dependencies uses NativeModules like Firebase). So to support it you might have to write a wrapper around web-equivalent of such APIs and use them in the Main Module.

In this blog, I’m going to share my journey on how I implemented it.

1. Repository Structure

To make sure that we don’t mix the dependency of DOM with React Native, I created another project which is going to use the MyReactNative code as a git submodule.

So the project structure looks something like this.

MyReactNativeWeb (Web-Module)

|--src/

|----index.web.js

|----reactRouterStuff.js

|--package.json // MyReactNative dependencies + (react-native-web, | // react-dom, react-router, history, webpack & other | . // web only dependencies)

|--MyReactNative (Main-Module)

|----src/

|------index.android.js

|------index.ios.js

|------nativeRouterStuff.js

|----package.json // MyReactNative dependencies

2. Configurations

React Native comes with zero config setup where, there is no need of us creating any configurations, just metro-bundler’s default config suffices. But for our use case, we have to customize the build configurations, there are many bundlers(webpack, gulp, rollup, etc) you can choose any of them. I chose Webpack since its very popular choice and also powerful.

You can use my webpack and babel config for reference

MyReactNativeWeb/webpack.config.js

MyReactNativeWeb/babel.config.js

MyReactNativeWeb/package.json

"scripts": {

"bundle": "rm -rf dist; NODE_ENV=production webpack -p --progress --config webpack.config.js"

"start": "NODE_ENV=development webpack-dev-server -d --host 0.0.0.0 --port 3000 --config webpack.config.js --inline --hot --colors --devtool source-map"

}

MyReactNativeWeb/index.web.js

import React from 'react';

import {AppRegistry, View} from 'react-native';

import WebRouter from './WebRouter'; const App = () => (

<View style={{flex: 1}}>

<WebRouter />

</View>

); AppRegistry.registerComponent('MyReactNativeWeb', () => App);

AppRegistry.runApplication('MyReactNativeWeb', {

rootTag: window.document.getElementById('react-root')

});

There you have it `npm start` will start serving the app on http://localhost:3000/

3. Web Considerations and Best Practices

Router

If you’re using a Router like React-Native-Router-Flux, it might not work properly on the Browser, so either you have to write separate router definitions in Web-Module and use it or use React Navigation library.

Missing APIs

Not all APIs available in React Native work on browser simply because most browsers don’t support them. APIs like Camera, ImageEditor, BackHandler cannot be implemented due to limited browser support. So, if your app depends on these APIs a lot, you should reconsider certain things, whether to add abstraction and disable those features for web or look for some other solutions like HTML5 Camera APIs + WebGL, this, however, is a lot of work

State Management

In Android and iOS, when under memory pressure the app gets killed and restarted, but before being killed and restarted every app gets a chance to save and restore its state. In React Native there is no simple way for saving the state of React Native app and restoring it if you are using single ReactActivity(or React Controller in iOS), so we end up losing the state. This still was to a certain extent was tolerable, because phones and OS are getting bigger RAMs and better memory management.

But on web one should not rely on in-memory state(redux, mobx or react’s state) because users can reload the page at any time or they can share/bookmark the page and open them later, in such cases the only option is to store the key data like ID of a product or search parameters in the URL. So you have to refactor your react native app such that every page can be opened from deep-link.

Animation Performance

All the animations that are used in React Native Web use JS compared to the web’s de facto CSS. If animation experience is one of the top priority of your app, then you should consider duplicating the effort but in CSS.

Initial Load Time

This library is pretty big(~65kb), along with other libraries like (react-dom, react-router), your web page can easily shoot up to 200kb. While the library author is working on reducing the size and react-dom is also working on slimming the core library currently startup time might be concerning especially if you’re targeting emerging markets like India. If you are proficient in isomorphic solutions then you can try Server Side Rendering(SSR), however, this takes some engineering effort.

Demo

iOS | Web