skip to content

To serve an Expo app's sign-up flow in a browser, what must be installed, and how does Expo alias react-native to react-native-web on Metro?

level: juniorimportance: should knowfreq 38%

answer

  1. three packages via npx expo install
  2. Metro is the web bundler now
  3. the alias lives in Expo CLI's resolver
  4. .native files ignored on web
  5. browser field preferred on web

basics

~20 s

Install react-dom, react-native-web and @expo/metro-runtime with npx expo install, then run npx expo start --web. Expo CLI's Metro resolver aliases react-native to react-native-web for the web platform, so no webpack or Babel alias is needed.

solid answer

~40 s

In an Expo SDK 57 project, web support is three packages away: `npx expo install react-dom react-native-web @expo/metro-runtime`, which also picks versions matching the SDK (SDK 57 pins `react-native-web` to `~0.21.0`). `npx expo start --web`, or pressing `w` in the CLI, serves the app in a browser; `npx expo export -p web` writes a static build to `dist/`. Metro is the web bundler (`expo.web.bundler: "metro"`); the old `@expo/webpack-adapter` path is deprecated. The alias is not in your config: Expo CLI's Metro resolver maps `react-native` and `react-native/index` to `react-native-web` when the platform is `web`, disables `.native.*` files for web, and, for the browser bundle, prefers a package's `browser`, then `module`, then `main` field. So the sign-up screens import from `react-native` as usual, and web-only pieces go in `.web.tsx` files.

code

bash · 3 lines
bash
npx expo install react-dom react-native-web @expo/metro-runtime
npx expo start --web
npx expo export -p web

go deeper

for a junior

Recall the install command, how to start the web target, and that the same react-native imports work in the browser.

for a middle

Explain that Expo CLI's Metro resolver performs the alias per platform, disables .native files on web and prefers browser fields.

for a senior

Plan the web sign-up flow: which pieces go into .web.tsx files, how to export and host it, and how to diagnose libraries that crash only on web.

for a principal

Decide whether marketing-facing flows such as sign-up belong in the shared app or in a web-first surface, weighing reuse against web conventions.

## The scenario The mobile app's sign-up flow (email, password, terms, verification code) should also be reachable at a web address, so the product can link to it from ads and emails. The team does not want a second codebase. In an Expo project this is mostly configuration that Expo already owns. ## What to install ```bash npx expo install react-dom react-native-web @expo/metro-runtime ``` - **`react-dom`** is the renderer React Native Web draws with. - **`react-native-web`** is the web implementation of `react-native`. Expo SDK 57's bundled-module list pins it to `~0.21.0`, which is why `npx expo install` (rather than a plain package-manager install) is used: it picks versions that match the SDK. - **`@expo/metro-runtime`** supplies the runtime pieces Metro needs in a browser. Before it starts a web session, Expo CLI checks for these packages: without `react-dom` it stops and tells you to run the install command; with `react-dom` present but `react-native-web` missing it only warns that some React Native components may not work on the web. ## Running and exporting 1. `npx expo start --web` (or press `w` in the running CLI) bundles for the web platform and opens the browser. 2. `npx expo export -p web` produces a static web build in `dist/`, copying the contents of `public/` alongside it. 3. A `public/index.html` file overrides the default HTML shell, for example to add meta tags for the sign-up landing page. The app config selects Metro as the web bundler: ```json { "expo": { "web": { "bundler": "metro" } } } ``` Expo's docs call Metro the recommended web bundler and describe the older webpack adapter as deprecated, with a migration guide. ## Where the alias actually happens There is no `resolve.alias` and no Babel alias in an Expo project. Expo CLI extends Metro's resolver, and for the `web` platform it: - maps **`react-native`** and **`react-native/index`** to **`react-native-web`**; - redirects React Native's asset-source helper to Expo's asset implementation; - turns off **`.native.*`** file resolution, so `Form.native.tsx` is never picked for the web while `Form.web.tsx` is; - resolves packages for the browser bundle using their **`browser`, then `module`, then `main`** fields, because many React Native packages' `react-native` field has no web support (server-rendering environments use their own field order). Because this is resolver logic rather than a string rewrite, it applies to dependencies in `node_modules` as well as to app code. ## Shaping the sign-up flow for the web - Shared screens import `View`, `Text`, `TextInput` and `Pressable` from `react-native` and work unchanged. - Pieces with no web equivalent go into platform files: a native SMS-code autofill helper in `CodeInput.native.tsx`, a plain field in `CodeInput.web.tsx`. - Web-only markup (for example a real `form` element so browser password managers recognise the page) can live in a `.web.tsx` file, since React DOM elements render there. - Static rendering for search engines is available through Expo Router's web output settings. ## Hosting the exported flow - `npx expo export -p web` writes plain static files to `dist/`, so any static host can serve the sign-up flow. - How routes map to HTML files (one shell for the whole app or one file per route) is decided by the router's web output setting, which is a separate topic from the alias. - `EXPO_PUBLIC_` environment variables, such as the API base URL the sign-up form posts to, are inlined into the bundle when `process.env.EXPO_PUBLIC_…` is read with dot notation, so each environment gets its own export. - The same screens keep shipping in the native app from the same commit, which is the point of serving the flow from the mobile codebase. ## Common problems | Symptom | Likely cause | |---|---| | CLI stops or warns about web dependencies | `react-dom` (blocking) or `react-native-web` (warning) missing | | A library crashes only on web | it has no web build and was imported from shared code | | A `.native.tsx` file seems ignored on web | expected: native files are disabled for the web platform | | Version warnings after an SDK upgrade | packages installed without `npx expo install` | ## Why this matters in an interview It shows you know the alias is still there, just owned by the framework: the same `react-native` import resolves to a native implementation on phones and to React Native Web in the browser, decided per platform inside Metro's resolver.

  • In an Expo project, why might Form.native.tsx and Form.tsx behave differently from a plain Metro setup on the web?
    Expo CLI turns off `.native.*` resolution for the `web` platform, so the web bundle uses `Form.web.tsx` if it exists and otherwise `Form.tsx`, never `Form.native.tsx`. That is what lets `.native.tsx` hold code shared by iOS and Android that must not reach the browser.
  • Why does Expo prefer a package's browser field over its react-native field when bundling for the web?
    Many React Native packages point their `react-native` field at native-only source. For the web platform Expo resolves `browser`, then `module`, then `main`, which is where packages publish their browser-compatible builds.
  • Why use npx expo install rather than a plain package-manager install for react-native-web?
    Because `npx expo install` picks the version range the SDK was tested with; SDK 57's bundled-module list pins `react-native-web` to `~0.21.0`. A plain install can pull a newer major that Expo's tooling and other SDK packages were not built against.

saying these in an interview costs you the question

  • An Expo project needs a webpack config with a react-native alias for web.
  • Expo's web alias is a Babel rewrite that skips node_modules.
  • .native.tsx files are used on web when no .web.tsx exists.
  • npx expo export -p web requires EAS to build the site.
  • react-native-web must be installed at its latest major regardless of the SDK.