React needs a multi-platform framework where standard Web APIs can be used to describe not just web applications, but native applications as well. React Strict DOM is designed to accelerate this outcome.

Engineering teams are increasingly building multi-platform applications from a single codebase. The ability to share high-quality interfaces and products across multiple platforms at reduced cost is a key advantage.

Flutter is a good example of a framework designed for these customers. React Native has the Expo framework, but its web support is limited by React Native’s elements and styles. React DOM has frameworks like Next.js, but they only produce web apps. The ecosystem is fragmented, limiting React’s value by making multi-platform development unnecessarily difficult and slowing innovation by confining it to individual platforms.

But the accumulated progress across React, React Native, React Devtools, Hermes, Flow, and Expo means that we can now begin to unify React UI development in a way that was impossible just a few years ago. A sketch of how we could consolidate elements and styles was proposed in the RFC “React DOM for Native”. Soon after, I created React Strict DOM to help bring this vision to life.

The cost of fragmentation

React’s declarative component model changed UI development. However, fifteen years after its release, the technology stack remains remarkably fragmented. The first decision a developer makes—“Web or Native?”—locks them into a specific set of APIs (e.g., <div> or <View>) and paradigms. This bifurcation leads to hidden engineering inefficiencies and the need to make tough trade-offs.

On the web, React DOM leaves important capabilities missing. Developers are required to make their own decisions about how to support component animations, interactions, and styles. There are no component-native solutions provided by React. Every project invites a new debate, which has led to significant inconsistencies between codebases and a lack of truly portable UI code.

Conversely, if you choose to go native, the learning curve is steep. React Native relies on a different set of primitives (View, Text, Image) and a strict subset of CSS applied via JavaScript objects (StyleSheet). Web knowledge does not transfer one-to-one. Consequently, “universal” apps are often two separate applications, or rely heavily on platform-specific components. This isn’t just a web-to-native problem. Some React developers start first with React Native, and they can face similar challenges when moving to the web.

This technical fragmentation acts as a tax on developers and a drag on business. Doing similar UI work twice correlates with slower time-to-market and greater product inconsistency. Perhaps most critically, it is hindering our ability to leverage emerging technologies like generative AI. When an LLM must learn several different ways to achieve similar results with React, its output is slower, less reliable, and more expensive.

One React

What if we could have just one React that allows us to write components once, run them everywhere, and deliver a top-tier experience on each platform?

The limitations of React Native

The React ecosystem has previously relied on React Native and React Native for Web to develop multi-platform apps. While this approach has enabled some teams to share code between platforms, it is misaligned with the broader React ecosystem and the web platform.

Compromised web experiences. React Native’s API was designed for native mobile platforms, not the web. When running on the web, these abstractions result in bundle size increases and some runtime performance overhead. And critically, the web experience itself is diminished because existing React Native code often lacks the accessibility and responsive design optimizations required on the web. The result is web apps that feel like compromised ports rather than first-class web experiences.

Swimming against the ecosystem. The vast majority of React code—millions of components across thousands of codebases—was written for React DOM using standard web APIs. It doesn’t make sense for organizations already invested in React on the web to retrain developers and rewrite components libraries to use React Native. It would be a massive switching cost with questionable returns. And diverging from web standards provides no benefit when building for the web alone.

A stagnant alternative. React Native’s UI components have changed little over the past decade and never gained meaningful adoption among web developers. The API design still reflects mobile-first assumptions that don’t account for different interaction models. Meanwhile the web platform continues to evolve with new capabilities for layout (CSS Grid, Container Queries), interaction (scroll-driven animations), and accessibility (ARIA 1.2).

The advantages of standardizing on web APIs

React Strict DOM was created to standardize multi-platform React development on web APIs. It converges React DOM and React Native not by replacing either, but by providing a strict compatibility layer between them.

Native-quality output on every platform. On the web, components can render to standard HTML elements and static CSS without abstraction overhead or loss of capabilities. On native platforms, components render to native views that look and behave like truly native apps. The difference is that the developer interface used to describe these experiences is based on web standards rather than a proprietary abstraction.

Unlocks the existing React ecosystem. Standardizing on web APIs retains and leverages existing web code. The millions of React components already built for the web become potential candidates for multi-platform use. Organizations can benefit from their existing investments in React web development rather than starting from scratch.

Meets developers where they are. Web APIs are the most widely understood programming interface in the world. Almost every React developer knows HTML and CSS. Every AI coding assistant is trained on web technologies. By using web APIs, React Native becomes accessible to web developers without requiring them to learn a new paradigm. Teams already using React DOM can start building native apps using their existing knowledge and codebase.

Enables practical migration paths. Because web APIs are well-structured and widely standardized, automated migrations become viable. Babel codemods can reliably transform both React DOM and React Native codebases to use React Strict DOM. This bidirectional migration capability means teams can adopt a unified approach incrementally rather than requiring a risky rewrite.

Future-proofs the investment. Web platform APIs have proven remarkably stable over decades while continuing to evolve with new capabilities. Betting on web standards means benefiting from continued investment by browser vendors, standards bodies, and the broader web community. Developing a proprietary API is costly and has a greater risk of falling behind or diverging from developer expectations.

React Strict DOM in action

These advantages sound compelling in theory, but how does React Strict DOM actually work in practice? The developer experience centers around two main APIs that bridge the gap between web standards and multi-platform rendering.

The developer interface

React Strict DOM exports two primary modules: html and css. These modules provide a familiar interface for developers while enabling the library to deliver platform-appropriate output.

The html module exports strictly-typed components for standard HTML elements. On the web, <html.div> simply renders a standard <div> element. On native platforms, the same component renders to a native <View>, but React Strict DOM polyfills various HTML element props and behaviors. This means many features work consistently across platforms, even though the underlying rendering targets are fundamentally different.

The css module provides css.create() for defining styles. This API looks similar to React Native’s StyleSheet.create(). On the web, styles are compiled at build time into optimized, static CSS rules that can be merged dynamically at runtime. On native, the styles are processed into platform-appropriate style objects, with polyfills added where necessary to support web features that don’t yet exist natively—CSS custom properties, inheritance, media queries, pseudo-classes, and more.

Here’s what using React Strict DOM looks like:

import { css, html } from 'react-strict-dom';

const styles = css.create({
container: {
display: 'flex',
padding: '1rem',
},
text: {
fontSize: '1.25rem',
color: '#333',
}
});

function Example() {
return (
<html.div style={styles.container}>
<html.h1 style={styles.text}>React Strict DOM</html.h1>
</html.div>
);
}

This code runs identically on web and native. The component author doesn’t need to think about platform differences or write conditional logic. React Strict DOM handles the translation from web semantics to native rendering while preserving the intent and behavior of the original code.

How it works beneath the surface

React Strict DOM doesn’t try to fake one platform on another—it creates a strict compatibility layer between them. By constraining the API surface to a well-defined subset of web standards, the library can make stronger guarantees about cross-platform behavior.

On native platforms, this means implementing features that React Native historically lacked. Support for CSS custom properties means that design tokens and themes can be shared across components. CSS inheritance is implemented to match web behavior, allowing properties like color and fontFamily to propagate down the component tree. Layout calculations are exposed through synchronous APIs like getBoundingClientRect(), enabling measurements that were previously impossible in React Native. Some of these features are being implemented natively, and others are being polyfilled in JavaScript for the time being.

For teams ready to adopt this approach, the official React Strict DOM documentation provides comprehensive guides, API references, and migration strategies.

Production success

Meta has been using React Strict DOM to build cross-platform applications since 2023, proving that this approach delivers on its promise.

Meta VR apps

The Facebook and Instagram VR apps, unveiled at Meta Connect 2024, were the first fully native apps built by reusing substantial amounts of existing web code, demonstrating that the approach works not just for simple interfaces but for complex, production-grade native apps.

Over 60% of the files used by the Facebook VR app were shared directly with facebook.com, including some of the most sophisticated features on the web, like the news feed, commenting systems, content rendering logic, and router. These are complex, battle-tested systems built over years.

Reusing existing code allowed Meta to ship features faster with fewer engineers while maintaining consistency across platforms. Engineers who understood how to work on the web could immediately contribute to the VR app without learning a new codebase or framework.

The VR apps look and feel indistinguishable from apps built natively from scratch. This proved that code sharing with web doesn’t require compromising on native product quality. The benefits come from development efficiency, not user-facing trade-offs.

Zalando

European online retailer Zalando provides another validation of the React Strict DOM approach, but from a different angle. As an early adopter of React Strict DOM, Zalando’s experience demonstrates that this isn’t just a solution for Meta’s unique scale—it works for companies of different sizes with different needs.

Zalando faced a common challenge: their native and web teams were building similar features independently, leading to duplicated effort and inconsistent experiences. They wanted to increase iteration speed and scale their product across platforms without sacrificing quality. They chose to standardize on web technologies and use React Strict DOM.

Zalando’s technical leadership bet that web platform APIs are more likely to remain relevant long-term than any proprietary framework API. By building their cross-platform UI library on React Strict DOM, they invested in a technology foundation that aligns with the broader industry trajectory.

These production deployments validate the core thesis: standardizing React on web APIs enables genuine code sharing between platforms while maintaining the quality users expect from native applications. The approach scales and is a natural fit for emerging AI-coding workflows, suggesting it has the flexibility to work for different teams and new engineering paradigms.

React Native now makes this possible

This vision is possible today because of the change to React Native’s architecture, specifically the move from the asynchronous bridge to the JSI (JavaScript Interface).

In the legacy architecture, React Native communicated with the host platform via an asynchronous JSON bridge. This made emulating the DOM impossible. You could not synchronously measure an element (e.g., by using getBoundingClientRect()) or prevent an event default, because the bridge introduced an unavoidable delay for serialization and transmission.

React Native now allows the JavaScript engine to call C++ native functions synchronously. This unlocks capabilities that are fundamental to the web:

  1. Synchronous layout: React Native now supports synchronous, imperative APIs like offsetWidth and getBoundingClientRect() to return native layout information in the same frame as the query.
  2. Event loop consistency: React Native now implements the same microtask and task execution model as the browser. This ensures that features like Promises, setTimeout, and event bubbling can behave consistently across all platforms.
  3. Web APIs: React Native is developing native implementations of modern Web APIs like MutationObserver and IntersectionObserver. They hook directly into the native view hierarchy via C++ bindings to deliver performant visibility tracking without passing data over a bridge.

This is why React Strict DOM isn’t “faking” the DOM—React Native is directly implementing the necessary subsets of the DOM specification. All of this lets us render React web components on native more accurately than ever.

Migration strategy

The unification of React rendering would be severely limited if we couldn’t get there from where we are today. Meta was able to successfully automate code migration for existing web and native components using Babel codemods.

From web

For existing web codebases, migration can be relatively straightforward. Because React Strict DOM is a strict subset of HTML, most well-structured web code can be transformed without difficulty. AST codemods can convert standard elements like div to html.div, and existing CSS or CSS-in-JS to css.create. This allows for rapid, error-free migration of existing React web components at scale.

// Original
import * as React from 'react';
const styles = createStyles({/*...*/});
export function Component() {
return (
<>
<div className={styles.root} role="presentation" />
<label htmlFor="id" />
</>
);
}

// Transformed
import * as React from 'react';
import {css, html} from 'react-strict-dom';
const styles = css.create({/*...*/});
export function Component() {
return (
<>
<html.div role="none" style={styles.root} />
<html.label for="id" />
</>
);
}

From native

Codemods can also be used to migrate plenty of existing React Native components to React Strict DOM. For example, a codemod can analyze a <View> and determine which HTML element it should be replaced with, as well as map StyleSheet styles to css.create. This approach could form the foundation of a cost-effective way to migrate components in existing React Native codebases to a “web-first” standard.

// Original
import * as React from 'react';
import {StyleSheet, View, Text} from 'react-native';
const styles = StyleSheet.create({/*...*/});
export function Component() {
return (
<>
<View accessibilityLabel={label} style={styles.root} />
<TextInput multiline={true} numberOfLines={5} />
</>
);
}

// Transformed
import * as React from 'react';
import {css, html} from 'react-strict-dom';
import {baseStyles} from '@utils/react';
const styles = css.create({/*...*/});
export function Component() {
return (
<>
<html.div aria-label={label} style={[baseStyles.view, styles.root]} />
<html.textarea style={baseStyles.textInput} rows={5} />
</>
);
}

Escape hatches

Although the goal is to enable as much code sharing as possible, there are still cases where platform-specific APIs or optimizations are needed. Where distinct native behaviors are required, React Strict DOM relies on the platform-specific file extensions introduced by React Native. Developers can create different component implementations in *.native.js and *.web.js files with a shared props interface.

Closing thoughts

The fragmentation between React DOM and React Native has been a persistent challenge. React Strict DOM represents a practical path toward unification—one that preserves the strengths of both platforms while eliminating unnecessary divergence. Meta’s production experience demonstrates that this approach delivers tangible benefits: faster development, better code reuse, and native-quality experiences. It’s a step towards positioning React as the most effective framework for building multi-platform apps.

When we constrain how we build, we can create trade-offs that work in our favor. Trading some flexibility to increase predictability is what lets us compose larger systems with more confidence. This is a quality that matters as our applications grow in complexity, and as AI becomes a fundamental part of how we build software.