DEV Community

Sungyu Kang
Sungyu Kang

Posted on

Type-Safe Communicating Between React Native WebView and the Web

SharedState

GitHub logo gronxb / webview-bridge

Fully Type-Safe Integration for React Native WebView and Web

webview-bridge

NPM NPM NPM NPM

Fully Type-Safe Integration for React Native WebView and Web

webview-bridge is a powerful interface that acts as a bridge between React Native and web applications using react-native-webview. It providing seamless interaction and ensuring type safety.

Inspired by the functionality of tRPC, webview-bridge simplifies the communication process between react-native-webview and web applications.

Key Features:

  • Built upon react-native-webview.
  • Designed with zero external dependencies (except for react-native-webview).
  • Type-Safety
  • Backward Compatibility
  • No App Review Needed
  • Shared State

webview-bridge

Documentation

visit Docs

Example

visit Example

Exporting Type Declarations

To enhance your experience with webview-bridge, it's recommended to export the type declaration of the native bridge object to the web application. Here are a few ways to achieve this:

  1. Monorepo Setup (Recommended): Use a monorepo setup to export the type of the native bridge.
  2. Custom Declaration File: Build a bridge declaration file using tsc and move the file as needed.
  3. …

Image description

Summary

When developing with WebView, it's essential to implement communication between the web and native environments. This could be for functionalities such as in-app browsers, alerts, navigation, etc.

In this post, I'll share how to type-safe and simple communication using the webview-bridge. Additionally, this approach supports shared state, enabling the integration of native states reactively with other web frameworks.

React Native Part

Installation

> pnpm add @webview-bridge/react-native react-native-webview
Enter fullscreen mode Exit fullscreen mode

Setup Bridge WebView

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

type AppBridgeState = {
  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;
Enter fullscreen mode Exit fullscreen mode
export const { WebView } = createWebView({
  bridge: appBridge,
  debug: true, // Enable console.log visibility in the native WebView
});

// Use the WebView component in your app
function App(): JSX.Element {
  return (
    <SafeAreaView style={{ height: "100%" }}>
      <WebView
        source={{
          uri: "http://localhost:5173",
        }}
        style={{ height: "100%", flex: 1, width: "100%" }}
      />
    </SafeAreaView>
  );
}

export default App;
Enter fullscreen mode Exit fullscreen mode

Web (React) Part

Installation

> pnpm add @webview-bridge/web @webview-bridge/react
Enter fullscreen mode Exit fullscreen mode

Native Method

import { linkBridge } from "@webview-bridge/web";
import type { AppBridge } from ""; // Import the type 'appBridge' declared in native

const bridge = linkBridge<AppBridge>({
  onReady: async (method) => {
    console.log("bridge is ready");
    const version = await method.getBridgeVersion();
    console.log("currentBridgerVersion", version);
  },
});

bridge.getMessage().then((message) => console.log(message)); // Expecting "Hello, I'm native"
bridge.sum(1, 2).then((num) => console.log(num)); // Expecting 3
bridge.openInAppBrowser("https://google.com"); // Open google in the native inAppBrowser
Enter fullscreen mode Exit fullscreen mode

Shared State

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>;
}
Enter fullscreen mode Exit fullscreen mode

Top comments (0)