Skip to content

fix: Add getHostComponent.web.ts so web bundles don't pull in React Native internals - #1477

Open
giaBaoJS wants to merge 1 commit into
mrousavy:mainfrom
giaBaoJS:fix/get-host-component-web
Open

fix: Add getHostComponent.web.ts so web bundles don't pull in React Native internals#1477
giaBaoJS wants to merge 1 commit into
mrousavy:mainfrom
giaBaoJS:fix/get-host-component-web

Conversation

@giaBaoJS

Copy link
Copy Markdown
Contributor

Fixes #1216

The bug

packages/react-native-nitro-modules/src/views/getHostComponent.ts:4 deep-imports a React Native internal:

// eslint-disable-next-line @react-native/no-deep-imports
import * as NativeComponentRegistry from 'react-native/Libraries/NativeComponent/NativeComponentRegistry'

src/views/ has no web variant, so on web Metro resolves the native file and pulls native-only React Native modules into the bundle. With Metro's experimental tree-shaking that fails the build outright:

Error: Circular dependency detected while tree-shaking:
  node_modules/react-native/Libraries/StyleSheet/PlatformColorValueTypes.js

The rest of the package already handles this correctly — src/turbomodule/NativeNitroModules.web.ts exists for exactly the same reason. views/ was just missing its counterpart.

You replied on the issue with "Ah yes, will fix that soon" (comment), so this follows that.

The fix

One new file: src/views/getHostComponent.web.ts.

  • getHostComponent throws — Nitro Views are backed by native iOS/Android views, so there is nothing to return on web.
  • callback is kept identical, since src/index.ts does export * from './views/getHostComponent' and callback is part of that public surface. It is 6 lines of platform-independent code, mirroring how NativeNitroModules.web.ts restates isRuntimeAlive.
  • Types are pulled in with import type, so they are fully erased at build time and the web output has no reference back to the native file (verified in the emitted JS below).

React Native's platform-extension resolution does the rest. No other file is touched.

Verification

1. Reproduced the reported failure, on a fresh Expo SDK 57 app (RN 0.86.2, react-native-nitro-modules@0.36.5 from npm), whose App.tsx imports getHostComponent from react-native-nitro-modules:

$ EXPO_UNSTABLE_TREE_SHAKING=1 EXPO_UNSTABLE_METRO_OPTIMIZE_GRAPH=1 EXPO_USE_FAST_RESOLVER=1 \
    npx expo export --platform web --clear --no-minify
Web Bundling failed 1431ms index.ts (250 modules)
Error: Circular dependency detected while tree-shaking:
  .../node_modules/react-native/Libraries/StyleSheet/PlatformColorValueTypes.js

2. Same app, same command, with this branch (npm packed from this checkout and installed into the repro app):

Web Bundled 1682ms index.ts (187 modules)
› web bundles (1):
_expo/static/js/web/index-49631077d01776ee288812b5cd75a37e.js (830KB)
Exported: dist

Grepping that web bundle:

string count
PlatformColorValueTypes 0
NativeComponentRegistry 0 (the one textual hit is the doc comment in the new file)
Nitro Views are not supported on ${"web"} 1

The last line is the decisive one — the web variant is what actually got bundled, with Platform.OS inlined to "web".

3. Native resolution is unchanged. Same app, npx expo export --platform ios --no-bytecode, with this branch installed:

string count
NativeComponentRegistry 72
NativeComponentRegistry is not available (native getHostComponent.ts) present
Nitro Views are not supported (web variant) absent

4. Build output. bun run build in packages/react-native-nitro-modules emits the new file into every target:

lib/commonjs/views/     getHostComponent.js  getHostComponent.web.js  HybridView.js
lib/module/views/       getHostComponent.js  getHostComponent.web.js  HybridView.js
lib/typescript/views/   getHostComponent.d.ts  getHostComponent.web.d.ts  HybridView.d.ts

This matters because package.json only sets "react-native": "src/index" — Metro on web uses module/main, i.e. lib/. The import types are erased, so lib/module/views/getHostComponent.web.js imports only react-native.

5. Checks. bun typecheck (all workspaces) and bun lint-ci in packages/react-native-nitro-modules are clean.

On the test requirement

CONTRIBUTING asks every bug fix to ship with a test, and I could not find an honest place to put one:

  • The Harness workflows run on iOS and Android only — they cannot exercise a web-only Metro resolution.
  • A nitrogen compile-time spec doesn't apply; nothing about codegen changes here.
  • packages/react-native-nitro-modules has a jest config, but its only test is it.todo('write a test') and no workflow runs it, so a jest assertion here would not be a CI safety net — and asserting "the .web.ts file exports the same keys as the native one" would test the file rather than the bundling behaviour that actually broke.

Rather than add a test CI never runs, I verified the user-visible path directly (steps 1–3 above). Happy to add either of these if you'd prefer:

  • a jest test in src/__tests__ pinning that the web and native variants export the same runtime keys, plus wiring bun test into lint-typescript.yml, or
  • a CI step that runs expo export --platform web against a small fixture app.

Just say which and I'll push it.

What I did not verify

  • I did not run this on Android or iOS devices/emulators. The native claim above is bundler-level evidence (the iOS bundle still contains the native implementation and not the web one), not a runtime check.
  • I did not test with the exact versions from the issue (pnpm, RN 0.81.5). I reproduced and fixed it on Expo SDK 57 / RN 0.86.2 with npm.
  • Web behaviour on a Nitro View is now a clear runtime throw rather than a bundler error. If you'd rather have getHostComponent return a no-op component so a web app can render past it, that's a one-line change — I went with throwing to match NativeNitroModules.web.ts.

…onent.web.ts`)

`getHostComponent.ts` deep-imports `react-native/Libraries/NativeComponent/NativeComponentRegistry`.
`src/views/` had no web variant, so web bundles resolved the native file and dragged
native-only React Native modules in. With Metro's experimental tree-shaking that fails the
build with "Circular dependency detected while tree-shaking: .../PlatformColorValueTypes.js".

Add a `.web.ts` variant that throws for `getHostComponent` (Nitro Views are native-only)
and keeps `callback` as-is, mirroring the existing `NativeNitroModules.web.ts` pattern.
iOS/Android resolution is unaffected.

Fixes mrousavy#1216
@vercel

vercel Bot commented Aug 13, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
nitro-docs Skipped Skipped Aug 13, 2026 9:03am

Request Review

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Deep import of react-native/Libraries/NativeComponent/NativeComponentRegistry breaks expo web builds with tree shaking

1 participant