How to: React Native Web app. A Happy Struggle.

A short yet detailed tutorial to building a universal application.

Lucas Mórawski
Bits and Pieces

--

You wake up. Sun is shining, birds are singing. There are no wars, no hunger and code can be easily shared between native and web environment. Wouldn’t that be nice? Unfortunately, only in the latter one, hope is on the horizon, but there are still some caveats on the way.

Why should you care?

PWA (Progressive Web App) is now a big three-letter word in the sea of tech acronyms, but this approach still has it’s drawbacks. There are tons of technological difficulties and use cases where you’re forced to build a native app alongside a web variety. There’s a great article by Ian Naylor about that.

But, building only a native app for your e-commerce business is also a big mistake. So making one piece of software working everywhere seems like a logical step. You cut working hours, production and maintenance costs. That is why I started this little experiment.

A simple e-commerce universal example app for online food order. Upon that I created a boilerplate for future projects and further experimentation.

Papu — food app on Android/iOS/Web

Check your primitives

We’re working here with React, so we should separate the app logic from the UI. Using some state managing system like Redux/MobX/other is the best choice. That move already makes business logic universal and shareable among the platforms.

The visual part is yet a different beast. To construct your app’s interface you need to have a common set of primitive building blocks. They need to work on the web, as well as in the native environment. Unfortunately web speaks a different dialect

<div>Aye there Cap! Standard web container at your service!</div>

than native

<View>Hi! I'm a basic container in React Native</View>

Some smart people figured this out. One of my favorite solutions is the grand React Native Web library made by Nicolas Gallagher. Not only it takes care of the primitives by letting you use the React Native components on the web (not all of them!). It also exposes some of React Native’s APIs like Geolocation, Platform, Animated, AsyncStorage and more! Check out some great examples in the RNW guides.

Tip: Use Bit (GitHub) for your components. It will help you organize, share and reuse them between apps to build faster. It’s also fun to discover your components in a visual collection, try the playground and install in the code.

Reacy spinners with Bit

A boilerplate to begin with

So we figured out how to deal with the primitives. We still need to glue the web and native production environments together and make the magic happen. For my project I used create-react-app for the web part and RN’s init script (no Expo here). First I created a project with create-react-app rnw_web. Then another one with react-native init raw_native. Then I “frankensteined” their respective package.json files into one and run yarn on it, in a new project folder. The final package file looks like so:

package.json for React Native Web boilerplate (no navigation in this version)

You need to copy all the source folders from the web and native folders to your new unified project folder.

Folders that need to be copied to a new project

Next in our newly created src folder we put two files App.js and App.native.js. Thanks to webpack we can use file name extensions to tell the bundler which files to use where. It is vital to use separate App files, since we’re going to use different approaches to app navigation.

App.js for Web. Here with react-router for navigation.
App.js for React Native with react-navigation.

That’s how I built a simple boilerplate and constructed a frame for the app. You can try out my clean boilerplate by cloning my github repo.

Next we’re going to a complicate it just a little bit, by adding routing/navigation system.

Navigation problems and solutions

Unless your app consists of only one screen you need some sort of navigation. Right now (Sep 2018) there is only one ready universal web/native formula — React Router. It’s a goto solution for web but not exactly for RN.

React Router Native lacks screen transitions, back-button support (android), modals, navbars, others. Other navigators provide this functionality, like React Navigation.

That’s the one I used in my project but you could use others. So it’s React Router for web and React Navigation for native. This yet creates a new problem. Navigating, as well as passing parameters, in both of those navigators dramatically differs.

To keep the React Native Web spirit of using the more native-like experience everywhere, I approached this problem by building web routes and wrapping them in a HOC. That exposed a React Navigation like API.

This allows to navigate between the screens on the web, without the need to create separate components for both worlds.
First step is to create a route map object for web routes:

import WebRoutesGenerator from "./NativeWebRouteWrapper"; //custom function that generates React Router routes and wraps them in a HOCconst routeMap = {
Home: {
screen: HomeScreen,
path: '/',
exact: true
},
Menu: {
screen: MenuScreen,
path: '/menu/sectionIndex?'
}
}
//in the render method
<View>
{WebRoutesGenerator({ routeMap })}
</View>

The syntax is a copy of React Navigation navigator creation functions with an addition of React Router specific options. Then, with my helper function, I create react-router routes. Wrap them in a HOC. That clones the screen component and adds navigation property to it’s props. This mimics React Navigation and exposes methods like navigate(), goBack(), getParam().

Modals

React Navigation with it’s createStackNavigator gives an option to make the screen slide from the bottom as a modal. To achieve this on the web I have used React Router Modal library by Dave Foley. To use a screen as a modal, first you have to add a modal option to the route map:

const routeMap = {
Modal: {
screen: ModalScreen,
path: '*/modal',
modal: true //the router will use ModalRoute component to render this route
}
}

You also need to add a <ModalContainer /> component from the react-router-modal library to your app’s layout. This is where it’ll be rendered.

Navigating between the screens

Thanks to our custom HOC (called temporarily NativeWebRouteWrapper — that’s a terrible name btw) we can use almost the same set of functions as in React Navigation to move between the screens on the web:

const { product, navigation } = this.props
<Button
onPress={navigation.navigate('ProductScreen', {id: product.id})}
title={`Go to ${product.name}`}
/>
<Button
onPress={navigation.goBack}
title="Go Back"
/>

Getting back to a previous screen in the stack

In React Navigation you can go back n-number of screens in your navigation stack. There’s no such thing in React Router, since there are no stacks. To solve this problem you need to import the custom pop function and pass few parameters:

import pop from '/NativeWebRouteWrapper/pop'render() { 
const { navigation } = this.props
return (
<Button
onPress={pop({screen: 'FirstScreen', n: 2, navigation})}
title="Go back two screens"
/>
)
}

screen-screen name (used on the web by React Router)
n-number of screens to go back in the stack (used by React Navigation)
navigation-navigation object

End results

If you want play with this idea, I’ve created two boilerplates.

First one is just a clean universal production environment for web and native. You can find it here.

Second one is the first one enhanced with my navigation solutions. Check it out here.

There’s also a demo app based on that idea called papu. It’s full of bugs and blind alleys but you can build it yourself and launch in your browser and mobile to get a taste of how it all works.

Next step

We trully need some universal navigation library to make projects like this easier to build. Making React Navigation to function also in the web environment would be awesome (actually you can do it today, but it’s a very bumpy ride — check it out here)

Thanks for your time! Please recommend and share if you like it. Hit me on twitter should you have any questions or comment below 😃

--

--

I tried to do music once, but people wouldn’t pay for it. So I’m computerizing code of the Internets and it seems like a more lucrative occupation.