Skip to content

Shared State in React ​

This guide integrates the state declared in Shared State in React Native with React within a WebView.

Installation ​

sh
$ npm add @webview-bridge/react @webview-bridge/web
sh
$ pnpm add @webview-bridge/react @webview-bridge/web
sh
$ yarn add @webview-bridge/react @webview-bridge/web

linkBridge ​

Use the linkBridge function to connect with the WebView as shown below.

Assume AppBridge, used as a generic, is declared in React Native with the following structure (refer to Shared State in React Native):

React Native Part ​

tsx
// React Native sample
import { bridge, type Bridge } from "@webview-bridge/react-native";

interface AppBridgeState extends Bridge{
  count: number;
  increase(): Promise<void>;
};

export const appBridge = bridge<AppBridgeState>(({ get, set }) => ({
  count: 0,
  async increase() {
    set({
      count: get().count + 1,
    });
  },
}));

// It is exported via the package.json type field.
export type AppBridge = typeof appBridge;

Web Part ​

tsx
// Web
import { linkBridge } from "@webview-bridge/web";

const bridge = linkBridge<AppBridge>({
  throwOnError: true,
  onReady: () => {
    console.log("bridge is ready");
  },
});

useBridge ​

The useBridge hook allows you to access shared state.

With Selector ​

Insert the previously bridge.store as the first argument and a selector as the second. Using a selector optimizes rendering.

For example, the code below only re-renders when the count value changes.

tsx
import { useBridge } from "@webview-bridge/react";

function Count() {
  // render when only count changed
  const count = useBridge(bridge.store, (state) => state.count);

  return <p>Native Count: {count}</p>;
}

Without Selector ​

Retrieve without a selector. However, it re-renders if any value changes.

tsx
import { useBridge } from "@webview-bridge/react";

function CountAndIncrease() {
  // render when any value changes
  const { count, increase } = useBridge(bridge.store);

  return (
    <div>
        <p>Native Count: {count}</p>
        <button onClick={() => increase()}>Increase</button>
    </div>
    );
}

With a Provider ​

createLinkBridgeProvider creates the bridge together with a BridgeProvider and hooks bound to it. It takes the same options as linkBridge, and it is the recommended setup for SSR frameworks.

tsx
import { createLinkBridgeProvider } from "@webview-bridge/react";
import type { AppBridge, AppPostMessageSchema } from ""; // types exported from React Native

export const {
  BridgeProvider,
  useBridgeStore, // shared state and native methods, with a selector
  useBridgeStatus, // { isWebViewBridgeAvailable, isNativeMethodAvailable }
  useBridgeLoose, // { loose }
  useBridgeEventListener, // listen to postMessage events
} = createLinkBridgeProvider<AppBridge, AppPostMessageSchema>({
  throwOnError: true,
});

function Count() {
  const count = useBridgeStore((state) => state.count);
  const { isWebViewBridgeAvailable } = useBridgeStatus();

  useBridgeEventListener("eventName1", (message) => {
    console.log(message);
  });

  return <p>Native Count: {isWebViewBridgeAvailable ? count : "-"}</p>;
}

Render the components inside <BridgeProvider>. If native announces its methods after the first render (which can happen on Android), useBridgeStore and useBridgeStatus re-render with them.

See Next.js (App Router) and Remix for complete setups.

NOTE

You can explore a practical example and further understand the implementation by visiting bellow.