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.

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)