Sharing UI Between React and React Native

Photo of Marek Lisik

Marek Lisik

Updated Jul 24, 2024 • 12 min read
nick-fewings-589145-unsplash

In 2017, we have seen an increased interest in the question of code sharing across React and React Native, mostly after Nicolas Gallagher took to the stage at React Rally to talk about react-native-web, a solution he has been using to build a web version of Twitter.

The first minor version of the package (0.1.0) was released October that year, and earlier, in March, Microsoft published reactxp to NPM. Since then, both libraries have been actively maintained, albeit by a limited number of contributors. They are currently the only production-proven options for experimenting with “truly” cross-platform React, targeting Native and Web with a single code base.

Philosophy behind react-native-web

In his React Rally talk, Nicolas Gallagher explains why it made most sense for him to use React Native as a starting point for building a native-web cross platform library (note that he built react-native-web to create a PWA and not the iOS / Android mobile app). He argues that React Native is inspired by the web and is, in essence, a web framework without the reliance on the DOM. It offers abstractions that are renderer-agnostic enough to be applied to other platforms beside iOS and Android, including the web (worth mentioning are other projects following the same philosophy, such as react-sketchapp where React Native’s JSX UI definitions are translated to Sketch document hierarchy).

In Gallagher’s view, DOM is an underpowered UI technology: it relies on low-level primitives and its styling solution is problematic at scale (CSS is by default non-deterministic due to the fact that styles cascade). These are inferior in comparison to well defined Views, Texts and StyleSheets of React Native, which are focused and designed around common concerns specific to (mobile) UI development. The fact that react-native-web relies almost solely on the abstractions defined by React Native and matches it’s API as close as possible has a great impact on development experience, as we will see later.

Philosophy behind ReactXP

Built by the Microsoft Skype team, ReactXP has a different goal in mind. It does not embrace React Native in order to bring it back to the web. Instead, it draws upon APIs and components defined by RN to offer its own layer of abstraction over it. This has certain benefits - it’s a closed and predictable API that is limited to a tailored subset of features shared between the platforms.

A good, small example of that is defining shadow styles for a view. Where in vanilla RN (and react-native-web), these properties need to be composed differently for each platform, ReactXP offers a single set that is then internally translated to what is correct on iOS, Android or the web.

As we will see in the following examples, there are certain drawbacks to this approach, however, especially when compared with react-native-web.

Overview

Setup

Both libraries offer an easy way to start a new projects as CLI tools exist that mirror a typical react-native init experience. At the time of writing, however, react-native-web will setup targeting React Native 0.55.4, which means you might need to use older versions of the packages you depend on. ReactXP comes ahead in this comparison, but still lags behind stable RN, with support for 0.57.8.

This, however, is expected - bear in mind that both libraries have small contributor community, mostly driven by a single maintainer focusing on support for their company’s product.

Project Structure

Both projects solve the web integration similarly, defining an entry point <div/> that React will render to. You will likely not need to edit these template files during development.

Your main app.js file will also look very similar to vanilla React Native - just look out for a different way of registering your component in ReactXP.

Aside from that, neither library has any impact or requirements on your project structure.

Platform-specific code

React Native

Platform API already allows targeting web through its select static method. ReactXP has its own version of Platform with similar implementation.

On top of that, the provided bundler configuration allows defining platform-specific modules by file extension, with ‘.web.js’ and ‘.native’.js.

Defining a component

In both cases, the syntax for defining a simple component is virtually identical to vanilla React Native. There are a few minor differences in the case of ReactXP, which you can see here:

import React from "react";
import { Text, Styles } from "reactxp";

const styles = { greeting: Styles.createTextStyle({ ... }) };

const Greeter = ({ name }) => (
<Text style={styles.greeting}>{Hello ${name}}</Text>
);

Aside from moving basic component imports to ‘reactxp’, the way styles are defined is the main difference. Coupled with Typescript (a default configuration for ReactXP), this way of scoping style definitions per component type has the advantage of autocomplete and type safety - the compiler will warn you against supplying incompatible styles to your component.

API differences

react-native-web covers RN’s components pretty well (a total of 5 unimplemented and 3 with no equivalent web APIs), and similarly for other classes - 10 modules are missing mostly due to the lack of web equivalents and 2 have limited functionality. Notable absentees include Alert and Modal.

ReactXP has limited coverage in comparison. Out of the box, it includes only 11 components. On the upside, some of those have been updated with the web in mind. Button, for example, includes methods for reacting to hover, making it easier to provide visual feedback on mouseover (something that’s a little more involved in react-native-web). There’s also GestureView, designed for straightforward handling of focus, key press and gesture events.

Missing, however, are lists. A VirtualListView is provided as a separate package and can be used as a building block for more complex lists, but that might be seen as a step backwards compared to vanilla React Native and react-native-web, at least in terms of convenience.

Similarly, on the APIs side there are a few absentees with ReactXP, but the basics (Alert, Location, I18n, Storage, UserPresence, Linking, Network etc.) are all there - even if structured or named differently.

Third party components

When it comes to integrating components that rely on native and thus cannot be used on the web, the situation is pretty similar for both libraries. A different component needs to be found for the web that fulfills the same purpose, and then platform specific file extensions can be used to define a common wrapper for it in your project. This wrapper will also function as a common interface to pass props and access instance methods of your third party component.

Components that are pure js and rely only on React Native are a different story. It is important to note that react-native-web setup will alias react-native to react-native-web in your projects, so that it is safe to import from react-native without worrying that the library itself is not available on the web. In the case of ReactXP, these imports have to be limited to components defined in platform-specific files (.native.js).

Because of that, with ReactXP you are limited to use these components only in files targeting native platforms. When using react-native-web, however, many simple components (eg. react-native-slider) will work the same on the web and on native - with no additional configuration or platform-specific code needed. This is the most important advantage of mimicking React Native APIs, but also has its limits. For some components that rely on React Native’s internals to a greater extent (such as react-native-snap-carousel), differences in the implementation between react-native-web and vanilla react-native show and break the component on the web.

Common hurdles in a real-life scenario

When thinking about using either of the platforms in a real app, there are a number of concerns to be solved, navigation likely being the first. You might select react-navigation and react-router-dom depending on the platform. These two differ in how they require the app to be structured and configured, but these differences can be mitigated by abstracting configuration and navigation actions away behind a common interface.

Another concern is responsiveness. While a typical React Native app is often designed with a single modality in mind (a phone in portrait), an app supporting the web should adapt correctly at least up to a tablet-size screen.

For the web version, server side rendering might be also be required. There is a brief recipe for react-native-web, while no example usage with ReactXP (see this issue)

Summary

At the time of writing, react-native-web appears to be a superior choice when it comes to cross platform web/native UI development using React/React Native. Thanks to aiming for an API that is equivalent to vanilla React Native as much as possible, it is much easier to pick up, makes porting an existing app to web feasible, and makes it possible to use existing, js-only third party React Native libraries on all platforms.

It is important to bear in mind that in its current state, neither solution is powerful and complete enough to consolidate development for both platforms in a complex product - as it grows your components and services will likely become more and more split and platform-specific, and the project will become harder to maintain. Remember that both libraries have been built for their specific purposes - to aid in the creation of Twitter’s PWA or Skype (and/or as a proof of concept). This limits the scope of problems each library was tested against in production.

Alternatives

Finally, it is worth mentioning other options for using React Native on the web.

React-native-dom by Vincent Riemer is an interesting project for a number of reasons: it runs React in a web worker, uses Yoga for layout, Metro instead of Webpack; and overall brings the web version closer to iOS and Android in terms of development experience and behavior predictability. It is well worth following Vincent’s introduction of the library in his talk from React Europe 2018, but also important to note that it is experimental and not production ready - as per the author’s warning.

Also, Evan Bacon recently announced that web support is coming to the Expo SDK. It is still experimental, yet stable enough to tinker with (although you might not be able to target iOS and Android yet with the same project setup). See the example docs over at github to review compatibility and progress. Hopefully, support from a large platform will increase viability of React Native on the web and again explode this idea within the community.

References:

Articles:

Videos:

Examples:

Photo by Nick Fewings on Unsplash

Photo of Marek Lisik

More posts by this author

Marek Lisik

A graduate of English Studies at the University of Wrocław, Marek started his career in tech early,...
Lost with AI?  Get the most important news weekly, straight to your inbox, curated by our CEO  Subscribe to AI'm Informed

We're Netguru

At Netguru we specialize in designing, building, shipping and scaling beautiful, usable products with blazing-fast efficiency.

Let's talk business