"react-strict-dom", why it's so great?

February 26, 2024 (7 months ago)

Introduction

Last week Meta open-sourced a new library called react-strict-dom, its goal is to improve and standardize the way of writing React components for Web and Native. In this article, I'd like to go through the historical background of building a universal codebase that shares components between Web and Native, and how react-strict-dom changes the way we can do it.

Historical background

As you may know React Native has different primitives for rendering UI than the Web, when you're a Web developer and you see basic RN components:

import { View, Text } from "react-native";
 
const App = () => {
  return (
    <View>
      <Text>Hello, world!</Text>
    </View>
  );
};

it can seem a bit odd and hard to use at first. Also, it removes the ability to use UI libraries that are using Web primitives in React Native. Also migrating components from the Web takes a lot of time and is painful.

react-native-web

A few years ago React Native for Web was created by Nicolas Gallagher during the development of Twitter's Progressive Web App. RNW adds a compatibility shim which translates React Native's primitives into react-dom ones, allowing us to render React Native components on the Web:

react-native-web diagram

But this primitive comes with quite a few drawbacks:

  1. The shim implements a large surface area of fragmented APIs, and it needs to match React Native's non-standard implementations (e.g. events).
  2. The shim itself is quite big, which is not the best for modern web apps.

What is react-strict-dom approach?

react-strict-dom takes the opposite approach to React Native for Web and it utilises Web APIs to render components. For this, it adds two small polyfills which are responsible for translating its APIs to react-native and react-dom primitives. Here's a nice graph which shows how it work in detail:

react-strict-dom diagram

For now, not all APIs are polyfilled or built-in inside Native platforms but there's an ongoing effort to make so (here's a current compatability list with progress on supporting primitives).

Creating components with react-strict-dom

react-strict-dom is powered by stylex which is a new styling solution created by Meta that is already powering facebook.com in production. It comes with the package under css module. All the building blocks with which we can build our app are available under html. This is how building UI with RSD looks like:

import React from "react";
import { css, html } from "react-strict-dom";
 
// Part of apps/examples/src/App.js
export default function App() {
  return (
    <html.div style={styles.div}>
      <html.div data-testid="testid">div</html.div>
      <html.span>span</html.span>
      <html.p>paragraph</html.p>
 
      <html.div />
 
      <html.span>
        <html.a href="https://google.com">anchor</html.a>,
        <html.code>code</html.code>,<html.em>em</html.em>,
        <html.strong>strong</html.strong>,
        <html.span>
          H<html.sub>2</html.sub>0
        </html.span>
        ,<html.span>
          E=mc<html.sup>2</html.sup>
        </html.span>
      </html.span>
    </html.div>
  );
}
 
// This is calling `stylex` under the hood
const styles = css.create({
  div: {
    paddingBottom: 50,
    paddingTop: 50,
    backgroundColor: "white",
  },
});

react-strict-dom is leveraging APIs that we know from the Web to build universal apps.

Is <html.div> a native component?

Yes, it is! The role of react-strict-dom is to translate one universal API to platforms' primitives.

Also as we can read in React Native Principles blog post:

Our top priority for React Native is to match the expectations people have for each platform. This is why React Native renders to platform primitives. We value native look-and-feel over cross-platform consistency.

React Native goal is to create fully native apps, so with the new approach at the end of the day, we still have a fully native app, not a WebView, or anything else. We can easily check it by running example app inside the repository and by inspecting components with Xcode's View Hierarchy tool:

Xcode inspector

💡 For technical deep dive I really encourage you to read RFC by Nicolas: RFC: React DOM for Native (reduce API fragmentation)

Future

There's a lot of work to do, you can follow the current status in COMPATABILITY.md document inside repository, but it looks like having one primitive that most developers know from Web is the best choice to build universal apps.

Also what is worth noticing is that react-strict-dom is powering teams at Meta in shipping features faster, to more platforms with fewer engineers 🚀 and it's the first cross-platform React solution which both targets Web and Native with the same components that Meta is directly investing into!

Summary

It's a really great move in the React ecosystem, it looks like react-strict-dom is a successor of React Native Web and it will allow us to build universal apps with one codebase delivered to multiple targets using the platform's APIs without any compromises or performance issues. Thanks for reading! 🙌