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 HOC const 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 😃