mrkeyoor.com_
Sat 08 Aug 20:58 UTC
npmMobileupdated 08 Aug 2026

react-native-web

React Native for Web implements much of React Native's component and JavaScript API on top of React DOM. It lets a shared component import View, Text, Pressable, StyleSheet, Platform, and related APIs from react-native while a web bundler redirects those imports to react-native-web. It is an adaptation layer, not a mobile runtime in a browser: DOM output, browser accessibility, CSS behavior, and unsupported native APIs still require web-specific judgment.

Verdict

The established bridge for teams with real React Native code to share, especially through Expo. Do not install it merely to make a web-only app look cross-platform; the aliasing, type package, API gaps, and native-module boundaries are real ongoing costs.

API stability4/5The core View, Text, StyleSheet, Platform, Pressable, and AppRegistry model has stayed recognizable across the 0.x line, and version 0.21 supports both React 18 and 19. The package is still below 1.0, follows React Native compatibility selectively, labels some exports unstable, and may drop APIs that React Native itself deprecates, so exact parity is not a stable contract.
Docs5/5The documentation site has separate installation, bundler setup, multi-platform, browser support, TypeScript, accessibility, styling, and per-component pages. Its compatibility table is unusually useful because it names unsupported APIs and partial behavior instead of implying complete parity. A few setup pages still present older webpack and Create React App examples, but the limitations are findable.
Maintenance3/5Version 0.21.2 and the repository's last push both landed on October 16, 2025. The repository is open rather than archived, has 159 open issues and pull requests, and 0.21 added React 19-era compatibility, but there has been no newer npm release or repository push for almost ten months as of this guide's update. That is slower than React and React Native release cadence.
Ecosystem5/5The project has 22,144 GitHub stars and recorded 5,205,975 downloads in the measured week. Expo uses it for web, the documentation points to React Native Directory entries with known web support, and its react-native alias convention is understood across bundlers and component libraries. Ecosystem breadth does not guarantee a particular native dependency has a web implementation.

Use it if

  • You have a React Native application and want to share presentation and interaction code with a browser target
  • You are building through Expo, which includes web integration and the package's recommended configuration path
  • Your design system can stay within the documented cross-platform component and style subset
  • You need React Native-style Pressable, accessibility props, and atomic StyleSheet output in an existing React web application
Skip it if

Setup reality

The shortest install is npm install react-dom react-native-web, but a shared-code setup has more edges. Version 0.21.2 requires React and React DOM 18 or 19 as peers. Configure your bundler so the exact react-native import resolves to react-native-web; repeat that mapping in Jest, Babel, and any Node process used for pre-rendering, or the same file can pass in the browser build and fail in tests or SSR. The docs recommend babel-plugin-react-native-web for per-export rewriting and dead-code removal. TypeScript declarations are not bundled, so install @types/react-native-web and add react-native-web to compilerOptions.types when augmenting React Native's props and style types. Full-screen ScrollView shells need explicit html, body, and root height rules, often with body overflow hidden. Browser support targets recent engines, but the guide says to supply Promise, Object.assign, Array.from, and ResizeObserver polyfills when supporting older environments. Native modules do not become browser APIs: use Platform checks or .web.js files for meaningful differences, verify every third-party React Native dependency has web support, and keep server-only evaluation away from document and window. Expo removes much of this wiring and is the project's recommended multi-platform starting point.

Patterns

Register and run a web applicationrender-application

import { AppRegistry, Text, View } from 'react-native';

function App() {
  return <View><Text>Hello, web</Text></View>;
}

AppRegistry.registerComponent('App', () => App);
AppRegistry.runApplication('App', {
  rootTag: document.getElementById('root'),
});

Shared files import from react-native; your web build must alias that exact package name to react-native-web.

Alias React Native in webpackconfigure-webpack-alias

// webpack.config.js
module.exports = {
  resolve: {
    alias: {
      'react-native$': 'react-native-web',
    },
    extensions: ['.web.js', '.js', '.json'],
  },
};

The trailing dollar makes the alias exact. Put .web.js before .js when shared imports should select web-specific files.

Apply the same alias in Jestconfigure-jest-alias

// jest.config.js
module.exports = {
  moduleNameMapper: {
    '^react-native$': 'react-native-web',
  },
};

Bundler aliases do not automatically affect Jest; without this mapping, shared tests may load React Native's native entry.

Enable per-export Babel transformsoptimize-babel-imports

// babel.config.json
{
  "plugins": ["react-native-web"]
}

Install babel-plugin-react-native-web separately. The project recommends it for pruning unused modules at build time.

Create reusable stylescreate-responsive-styles

import { StyleSheet, Text, View } from 'react-native';

const styles = StyleSheet.create({
  card: { padding: 16, borderRadius: 8, backgroundColor: '#fff' },
  title: { fontSize: 18, fontWeight: '600' },
});

export function Card() {
  return <View style={styles.card}><Text style={styles.title}>Title</Text></View>;
}

React Native style objects use camelCase and a supported subset of CSS; descendant selectors and ordinary CSS cascade rules are not part of StyleSheet.create.

Style a Pressable from interaction statehandle-press-state

import { Pressable, Text } from 'react-native';

<Pressable
  accessibilityRole="button"
  onPress={() => save()}
  style={({ hovered, focused, pressed }) => ({
    opacity: pressed ? 0.6 : 1,
    outlineStyle: focused ? 'solid' : 'none',
    backgroundColor: hovered ? '#eee' : '#fff',
  })}
>
  <Text>Save</Text>
</Pressable>

hovered and focused are web-relevant states. Keep keyboard focus visible rather than removing outlines without a replacement.

Use Platform for a small web differencebranch-small-differences

import { Platform, StyleSheet } from 'react-native';

const styles = StyleSheet.create({
  panel: {
    height: Platform.OS === 'web' ? 240 : 180,
    cursor: Platform.OS === 'web' ? 'pointer' : undefined,
  },
});

Use Platform for small branches only. Separate .web.js and .native.js files are easier to maintain when markup or behavior diverges.

Provide a web-specific component implementationsplit-platform-files

// ShareButton.web.js
export function ShareButton({ url }) {
  return <button onClick={() => navigator.clipboard.writeText(url)}>Copy link</button>;
}

// ShareButton.native.js
import { Share } from 'react-native';
export function ShareButton({ url }) {
  return <Button title="Share" onPress={() => Share.share({ message: url })} />;
}

Configure web resolution to prefer .web.js. Native modules and browser APIs should stay in their platform files.

Prepare a full-height ScrollView shellmake-full-height-root

/* Inline or load before the app mounts */
html, body { height: 100%; }
body { overflow: hidden; }
#root { display: flex; height: 100%; }

The setup guide recommends this shell for full-screen apps with a root ScrollView; body overflow hidden is wrong for ordinary document-scrolling pages.

Get an application element for server renderingrender-server-html

import { renderToString } from 'react-dom/server';
import { AppRegistry } from 'react-native-web';
import App from './App.js';

AppRegistry.registerComponent('App', () => App);
const { element, getStyleElement } = AppRegistry.getApplication('App');
const html = renderToString(element);
const css = renderToString(getStyleElement());

Server resolution must point react-native imports at react-native-web too. Keep modules that touch window or document out of the server import path.

Augment React Native types for web propsadd-typescript-support

// tsconfig.json
{
  "compilerOptions": {
    "jsx": "react-jsx",
    "types": ["react-native-web"]
  }
}

// install separately: npm install -D @types/react-native-web

The runtime package has no bundled TypeScript declarations. Its official TypeScript guide relies on DefinitelyTyped.

Respond to viewport changesadapt-window-size

import { Text, useWindowDimensions, View } from 'react-native';

export function Layout() {
  const { width } = useWindowDimensions();
  const columns = width >= 900 ? 3 : width >= 600 ? 2 : 1;
  return <View style={{ flexDirection: 'row' }}><Text>{columns} columns</Text></View>;
}

The hook updates when dimensions change. Avoid reading window directly in shared or server-rendered components.

Alternatives

PackageRegistryPick it when
react-domnpmYou are building a web-first React application and want direct DOM semantics and browser ecosystem access
exponpmYou want a managed universal app stack that configures React Native for Web and adds cross-platform APIs
react-strict-domnpmYou want Meta's experimental web-first component layer with stricter cross-platform styling rules
@tamagui/corenpmYou want a cross-platform styled-component system and compiler rather than broad React Native API emulation