In React Native Web, what happens when shared code calls an API with no web implementation, such as Alert.alert, findNodeHandle or a native module?
answer
- three failure shapes, not one
- some throw, some quietly do nothing
- Alert.alert: an empty method
- findNodeHandle: use the ref instead
- NativeModules holds only UIManager
basics
~20 sIt depends on the API. React Native Web throws for some (findNodeHandle), silently mocks others (Alert.alert does nothing, StatusBar renders null, NativeModules holds only UIManager) and lacks platform-only exports such as PermissionsAndroid, which arrive as undefined.
solid answer
~40 sReact Native Web implements most of React Native's JavaScript API, but gaps fail in three different ways. Some **throw**: `findNodeHandle` throws "findNodeHandle is not supported on web. Use the ref property on the component instead.", and in an Expo project `requireNativeModule` throws `Cannot find native module '…'` in the browser. Some are **mocks that do nothing**: `Alert.alert` is an empty method, `StatusBar` renders `null`, `BackHandler.addEventListener` logs an error and returns a no-op subscription, `KeyboardAvoidingView` is a mock, and `NativeModules` contains only `UIManager`. And some are **not exported at all** (`PermissionsAndroid`, `ToastAndroid`, `ActionSheetIOS`, `TurboModuleRegistry`), so the import is `undefined` and the first call is a `TypeError`. The silent ones are the dangerous ones: a confirmation dialog that never appears looks like a working button.
code
typescript · 5 lines// confirm.web.ts: web version of a shared confirm() helper
export function confirm(title: string, message: string): Promise<boolean> {
// Alert.alert is an empty method in react-native-web, so use the browser dialog
return Promise.resolve(window.confirm(`${title}\n\n${message}`));
}go deeper
Recall that some React Native APIs do nothing or throw on the web, with Alert.alert and findNodeHandle as the standard examples.
Explain the three failure shapes (throw, silent mock, missing export) with examples of each, and the ref-based replacement for findNodeHandle.
Show how you would audit a codebase before a web launch, why silent mocks are the riskiest, and how adapters and platform files contain each gap.
Frame the web gap inventory as a scoping input: the count of native-only features decides whether shared UI is realistic or the web needs its own product surface.
## Why the gaps exist **React Native Web** (0.21 here) reimplements React Native's JavaScript API with browser primitives. Anything that exists only because a phone has it (an Android back button, a status bar, a native module compiled into the app binary) has either no browser equivalent or a weak one. The library's compatibility page says it plainly: most of the API is covered, some parts are mocked, a few are not started, and features deprecated in React Native should be considered unsupported on the web. The practical skill is knowing **how** each gap fails, because the failure mode decides how you find it. ## Shape 1: it throws - **`findNodeHandle`** throws: "findNodeHandle is not supported on web. Use the ref property on the component instead." On the web a host ref *is* the DOM element, and React Native Web adds `measure`, `measureInWindow` and `measureLayout` to it, so the ref replaces the node handle. - **Expo-style modules without a web implementation**: `requireNativeModule('X')` from `expo-modules-core` throws `Cannot find native module 'X'` in a browser. Expo's own packages avoid it by shipping a `.web.ts` file that never calls it; a package without one gets the throw. During server rendering, where `window` is undefined, `requireNativeModule` returns an empty object instead so static rendering does not crash. - **Expo API methods missing on the platform** throw an `UnavailabilityError` with code `ERR_UNAVAILABLE` and a message like "The method or property expo-local-authentication.authenticateAsync is not available on web". These are the easy ones: the error names the problem. ## Shape 2: it silently does nothing | API | Behaviour in React Native Web | |---|---| | `Alert.alert` | an empty static method: no dialog, no callback | | `StatusBar` | renders `null`; its static setters are no-ops | | `BackHandler.addEventListener` | logs "BackHandler is not supported on web and should not be used." and returns a no-op subscription | | `KeyboardAvoidingView` | a mock; the web has no keyboard frame to avoid | | `RefreshControl` | renders a plain `View`; there is no pull-to-refresh | | `NativeModules` | an object holding only `UIManager` | | `AccessibilityInfo.isScreenReaderEnabled()` | always resolves `true`, since browsers do not expose it | | `LogBox` | `ignoreLogs` and friends are empty functions | | `Vibration.vibrate` | calls the browser's vibrate API only if the browser has one | These are the dangerous ones. A delete button that calls `Alert.alert` for confirmation does nothing at all on the web: no dialog, no error, and the `onPress` of the destructive option never runs. ## Shape 3: it is not exported React Native Web's entry point simply does not export Android-only or iOS-only APIs such as `PermissionsAndroid`, `ToastAndroid`, `ActionSheetIOS` and `Settings`, nor the native-module plumbing `TurboModuleRegistry` and `requireNativeComponent`. `import { ToastAndroid } from 'react-native'` therefore gives `undefined` in the web bundle (a bundler may warn about the missing export), and the first call fails with a `TypeError`. A third-party library that calls `TurboModuleRegistry.getEnforcing` at module scope fails the moment it is imported, before any of your code runs. ## How to handle each shape 1. **Inventory before you port.** Search the codebase for `Alert`, `BackHandler`, `StatusBar`, `NativeModules`, `findNodeHandle` and every native-only dependency; check each library for a web implementation. 2. **Replace mocks with real web behaviour.** An `Alert.web.ts` adapter that uses the browser's confirm dialog, or an in-app modal, keeps one call site across platforms. 3. **Replace `findNodeHandle` with refs** and the `measure` methods React Native Web adds to them. 4. **Keep native-only imports out of the web bundle** with a platform file (`Scanner.web.tsx` next to `Scanner.tsx`) rather than an inline branch, because the import itself can crash. 5. **Catch `UnavailabilityError`** (or check an `isAvailableAsync`-style function where the Expo module provides one) and degrade the feature on the web. ## A quick way to explain it in an interview "React Native Web fails in three ways: it throws, it no-ops, or the export is missing. I look for the silent no-ops first, because they are the ones that pass every test and still break the product."
- In React Native Web, what replaces findNodeHandle when you need a component's position?The ref itself. On the web a host component's ref is the DOM element, and React Native Web attaches `measure`, `measureInWindow` and `measureLayout` to it, so `ref.current.measureInWindow(cb)` works without a node handle. `findNodeHandle` throws on the web by design.
- In an Expo project, what does requireNativeModule do on the web during static rendering compared with in the browser?In the browser it throws `Cannot find native module 'X'`; Expo's own packages avoid that by shipping a `.web.ts` implementation that never calls it. During server or static rendering, where `window` is undefined, it returns an empty object so the page can still render. So a package with no web file can pass static rendering and only fail once the page loads.
- Why is AccessibilityInfo.isScreenReaderEnabled() unreliable in React Native Web?Browsers do not tell pages whether a screen reader is running, so React Native Web resolves it to `true` unconditionally. Code that hides content or changes behaviour based on it will always take the screen-reader branch on the web; build the accessible version by default instead of switching on this value.
A silent mock is like a light switch wired to nothing in a rented flat: it clicks, it looks right, and you only notice when the room stays dark. A switch that sparks (an API that throws) is alarming but gets fixed on day one.
saying these in an interview costs you the question
- React Native Web throws an error for every unsupported API.
- Alert.alert falls back to window.alert automatically.
- NativeModules on the web can load a module compiled into the app.
- PermissionsAndroid is exported as a no-op on the web.
- findNodeHandle works on the web and returns the DOM node.