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.

The path we followed to arrive at this point is also really interesting:

First, React appeared and changed the way we think about creating web apps

Next, React Native was released, taking all the good from React and making it available to iOS and Android apps. React Native was initially created to work on mobile platforms, and it is highly influenced by the way native apps are developed. Be that as it may, it preserves some key elements that make it feel closer web development: JavaScript, markup with JSX, and Flexbox

Finally, react-native-web was created to take those React Native applications and make them run in browsers again

React Native as a universal UI language

The need for the final step above is a bit unclear at first. We already have React, which was initially created to build web apps. Why would we use something else that wasn’t made to fit that explicit purpose?

The first reason is that React Native uses a kind of subset of React to generate the UI. If we want a code that runs in both mobile and web, we should stick with the more restrictive one; in this case, it’s React Native. As long as we don’t use modules that require some native functionality, a React Native app should work in browsers through react-native-web out of the box.

The second reason — and what really makes React Native superior to React for creating universal apps — is that React Native is a pure UI language. It defines some base components that define UI primitives, and those are thought to be independent of the platform that runs them. All the components we can create in React Native are based on primitives like <View> , <Text> , or <Image> , which are basic elements that make sense to any visual interface, no matter where it is run.

On the other hand, React’s primitives are just DOM nodes — HTML tags like <div> , <p> , or <a> , which are not pure UI. They weren’t created to define a visual language; rather, they’re meant to structure and make sense of the hypertext. React’s primitives have meaning beyond the interface, and that meaning doesn’t make much sense outside of browsers.

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.

At this point, we have made sense of why it’s a good idea to use React Native as the common language for web and native. I am convinced it’s the way to go, but react-native-web has been with us for almost two years, and it still struggles to spread, especially among web developers.

Two ecosystems for one platform

Using React Native for creating web apps isn’t simple, and not just because of the restrictions we need to fit it — it’s like native and web are still two separate worlds. Even if it’s already possible to use one codebase and run it everywhere, that code is full of conditions that can be run in one environment but not in another.

The same could be said of the libraries we can use to develop a react-native-web app.

React Native libraries

On the one hand, we have React Native libraries. We should be able to take these libraries and plug them into our react-native-web project with no issue (unless they run native code or have native dependencies).

Unfortunately for our universal app, performance used to be the main focus among React Native developers — and we achieve the best UI performance in mobile when rendering is taken to the native side. In this way, we can make computations in the JavaScript thread without affecting the responsiveness of the interface. Those libraries with native code can’t be used in a react-native-web project.

It would be great if, whenever possible, React Native library developers shipped their projects with a JavaScript alternative to their native functionality. If the native code doesn’t make much sense in a browser, React Native libraries should offer a mock of the native part that won’t break when it runs in a web environment.

Even when we find a React Native library that’s compatible with web, the process of making it work in our web app is not straightforward. In order to build our web apps, we use webpack as the bundler (more on this topic later), and it usually doesn’t transpile the files inside our node_modules folder. React Native libraries don’t need to be bundled to work for mobile, so we need to add exceptions into our webpack’s module configuration in order to take them into the bundling.

If you are a React Native library developer, please 🙏, show some love for react-native-web and ship a transpiled version in your libraries. Nowadays there are tools that make it really easy, like microbundle or bili.

React libraries

On the other hand, we have React libraries, which are generally thought to work in browsers. They make use of HTML tags to structure the UI; thus, if we use them in our universal app, they will break the mobile version.

If you are a React library developer, and you think your library would make sense for mobile environments, you should know it’s possible to make it work in React Native as well by using the same primitives as React Native through react-primitives.

In the beginning, it’s a bit tedious to get in the flow, but when you get used to the primitives and Flexbox, it can even help improve the organization of your code. Moreover, you will be learning React Native, which is very handy to have in your tool belt.

Building a universal app with React Native

Another pain we must suffer when creating a universal application is managing multiple bundlers. The standard method of developing and building apps in React Native is by using the Metro bundler. Made by Facebook, Metro allows us to build and test apps locally with almost no need for configuration. It’s shipped when you create a React Native application using Expo.

When we want to create the web version of our app, react-native-web recommends using webpack as the bundler. Don’t get me wrong — webpack is great, but the need for two different build systems with two different types of configuration is a pain for my one head.

I hope in the future we can use just one bundler. It’s possible to use Metro to build our web app; I’ve been playing with using Metro to bundle my web app, and it looks totally possible. I could create a JavaScript bundle, but my web app didn’t really work because of some transpilation problems I just didn’t want to investigate. Any of you want to give it a try?

Merging the worlds of mobile and web

I’ve been talking about how React Native and ReactDOM developers can adapt their libraries to make them friendly with each other, and that should be just the first step.

Ideally, we would move toward unifying both worlds into a universal-react developers community. This is more than just adapting libraries not to break in any environment; rather, it’s creating tools that cover the needs of both worlds and add value to them.

As mentioned before, react-native-web apps are really influenced by best practices for mobile. At the moment, when we create a react-native-web app, we more or less adapt mobile apps to work in browsers. But do we really want a phone app, with its full-width layout elements and collapsible drawers, running in a big-screen desktop browser? Probably not.

That’s why the responsive layout revolution started in the mid-2000s: to make our designs adapt to different screen sizes. This is now a basic feature for any web project. In mobile apps, responsive UIs aren’t very common. But wouldn’t it be great to build apps that adapt to phones, tablets, and desktop without the need to code twice?

Responsive layouts are just one example of concepts from the web that can add value to mobile development. Another good one would be URLs. We don’t need to use URLs in our mobile apps, but the concept of assigning a unique identifier to our screens and direct access to some point is really powerful. This, too, is uncommon in mobile apps, which is why it’s rather difficult to handle URLs in react-native-web apps when it should be pretty basic.

React-native-web makes a great effort to take mobile goodies to web development, but there is not much in the other direction yet. In general, we should start thinking purely in terms of UI, no matter what platform we are addressing — and we are not at that point yet.

It’s hard to define hover interactions in React Native because when it was created, we thought they wouldn’t be necessary. For that same reason, gesture interactions in browsers are no piece of cake. All these cases should be covered with ease by a language that aims to be the universal approach to defining UIs.

Welcome to the new world

Creating universal applications is a dream that came true, but like most things that are just starting out, there is still much to do to make it easy for everybody.

This is, as yet, a kind of unexplored world. As a library developer, I think it’s a great opportunity for us to build the next set of tools and libraries that will be used by the community of tomorrow. Wouldn’t you like to be the creator of the “next Redux”?

I want to make a special call to the Expo team to add support for react-native-web to the Expo environment since I think they could play a major role in the transition. It would be a huge push for universal applications that the most famous development platform for mobile shipped web bundles out of the box.

Making Metro work for web would open the web development door for Expo (a huge community there), but educating React Native developers on mocking their native dependencies to work on the web will guarantee that your projects eject when you want them to. A win-win situation for everybody!

Is it worth creating a universal app using React Native in 2019?

Definitely yes! Trying to run the same project across all platforms is certainly still challenging, and there are many gaps left to fill. But we can already use React Native as the one language to create the UI for different versions of our app, which reuse most of the same code even if they target different environments.

If you feel like helping solve some of the issues above, you will be pushing the dream of universal app development forward, and the whole community will be grateful to you.

If you just want to maximize your code reusability, set up a Lerna project, with mobile and web versions of your app as separate packages, and start creating your components using React Native. You will be reusing the whole UI, but you will be able to tackle platform-specific problems individually — no need to worry about compatibility.

If you are interested in this approach, I strongly recommend you read the article Building Cross-Platform Applications with a Universal Component Library from Lucas McGartland.

Full visibility into production React apps Debugging React applications can be difficult, especially when users experience issues that are difficult to reproduce. If you’re interested in monitoring and tracking Redux state, automatically surfacing JavaScript errors, and tracking slow network requests and component load time, Debugging React applications can be difficult, especially when users experience issues that are difficult to reproduce. If you’re interested in monitoring and tracking Redux state, automatically surfacing JavaScript errors, and tracking slow network requests and component load time, try LogRocket LogRocket is like a DVR for web apps, recording literally everything that happens on your React app. Instead of guessing why problems happen, you can aggregate and report on what state your application was in when an issue occurred. LogRocket also monitors your app's performance, reporting with metrics like client CPU load, client memory usage, and more. The LogRocket Redux middleware package adds an extra layer of visibility into your user sessions. LogRocket logs all actions and state from your Redux stores. Modernize how you debug your React apps — start monitoring for free.