Skip to main content

React Native

@webview-ts/react-native is the host side for apps embedding react-native-webview.

useBridgeHost

import { WebView } from 'react-native-webview';
import { useBridgeHost } from '@webview-ts/react-native';
import { camera } from './plugins/camera';

function WebViewScreen() {
const { webViewProps, sendEvent, bridgeHost, sourceId } = useBridgeHost({
plugins: [
camera.host({
takePhoto: async ({ quality }) => {
const photo = await NativeCamera.take({ quality });
return { uri: photo.uri, width: photo.width, height: photo.height };
},
}),
],
});

return <WebView {...webViewProps} source={{ uri: 'https://your-app.com' }} />;
}

webViewProps wires onMessage and ref — spread it onto the WebView and the transport is connected.

Direct handlers

Without plugins, pass an ActionMap type for full inference:

type MyActions = {
'storage.get': { payload: { key: string }; response: { value: string | null } };
};

const { webViewProps } = useBridgeHost<MyActions>({
handlers: {
'storage.get': async ({ key }) => ({ value: await AsyncStorage.getItem(key) }),
},
});

Every declared action must be implemented, payloads and responses are checked, and duplicate action names (across handlers and plugins) throw at setup.

Mixing direct handlers and plugins

TypeScript type arguments are all-or-nothing: useBridgeHost<MyActions>({ plugins }) disables inference of the plugins tuple, silently untyping sendEvent. Wrap direct handlers with defineHandlers instead — both sides stay inferred:

import { defineHandlers, useBridgeHost } from '@webview-ts/react-native';

const { sendEvent } = useBridgeHost({
handlers: defineHandlers<MyActions>({
'storage.get': async ({ key }) => ({ value: await AsyncStorage.getItem(key) }),
}),
plugins: [location.host(locationHandlers)], // sendEvent stays typed
});

Sending events

sendEvent is typed against the plugin event map (open set):

sendEvent('location.updated', { lat, lng });

For multiple WebViews and targeted/broadcast delivery, see Multi-WebView routing.

Platform quirks, handled

Two things the adapter absorbs so you don't have to:

  • iOS vs Android delivery — react-native-webview delivers host→web messages on window (iOS) or document (Android, non-bubbling). The client adapter listens on both.
  • Mount races — messages sent before the WebView ref is attached are queued and flushed on attach, instead of being silently dropped.

Outside React

createBridgeHost is the pure-function version of the hook — same options, usable in any JS host context without React.