Setup with Next.js (App Router)
This guide provides instructions on how to use webview-bridge
in Next.js (App Router) applications.
Page Router
For page routing, apply the same approach: set up the Provider in _app.tsx
and use it similarly.
What does it solve?
By design, webview-bridge
relies on client-side rendering, which can lead to hydration mismatches due to fetching data from the connected app. Resolving hydration mismatches is challenging in such scenarios. This guide helps to use Providers client-side in an SSR environment, avoiding execution on the server, and enables the use of default hooks seamlessly.
Installation
$ npm add @webview-bridge/react @webview-bridge/web
$ pnpm add @webview-bridge/react @webview-bridge/web
$ yarn add @webview-bridge/react @webview-bridge/web
createLinkBridgeProvider
providers/BridgeProvider.ts
"use client";
import { createLinkBridgeProvider } from "@webview-bridge/react";
import type { AppBridge } from ""; // Import the type 'appBridge' declared in native
export const { BridgeProvider, useBridgeStore, useBridgeStatus, useBridgeLoose, useBridgeEventListener } =
createLinkBridgeProvider<AppBridge>({
throwOnError: true,
// For proper hydration, it is advisable to set up an `initialBridge`. This step can be omitted if necessary.
initialBridge: {
token: '',
openInAppBrowser: async (url) => {
alert('mocking: ' + url)
}
},
onReady: () => {
console.log("bridge is ready");
},
});
Create a BridgeProvider
and related hooks by utilizing options similar to linkBridge
as shown in the code above.
Layout Side
app/layout.tsx
import "./globals.css";
import type { Metadata } from "next";
import { Inter } from "next/font/google";
import { BridgeProvider } from "./providers/bridgeProvider";
const inter = Inter({ subsets: ["latin"] });
export const metadata: Metadata = {
title: "Create Next App",
description: "Generated by create next app",
};
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="en">
<body className={inter.className}>
{/* Here */}
<BridgeProvider>{children}</BridgeProvider>
</body>
</html>
);
}
Wrap the BridgeProvider
around the children in the base code.
Client Component
app/components/BridgeHome.tsx
"use client";
import { useBridgeStatus, useBridgeStore } from "@/providers/BridgeProvider";
function Count() {
const count = useBridgeStore((state) => state.count);
return <p>Native Count: {count}</p>;
}
function DataText() {
const { text, setDataText } = useBridgeStore((state) => ({
text: state.data.text,
setDataText: state.setDataText,
}));
return (
<div>
<p>Native Data Text: {text}</p>
<input
type="text"
value={text}
onChange={(e) => setDataText(e.target.value)}
/>
</div>
);
}
export default function BridgeHome() {
const { increase, openInAppBrowser } = useBridgeStore((state) => ({
increase: state.increase,
openInAppBrowser: state.openInAppBrowser,
}));
const { isNativeMethodAvailable, isWebViewBridgeAvailable } =
useBridgeStatus();
return (
<div>
<div>
{`isWebViewBridgeAvailable: ${String(isWebViewBridgeAvailable)}`}
</div>
<h2>This is WebView</h2>
<button
onClick={() => {
if (isNativeMethodAvailable("openInAppBrowser")) {
openInAppBrowser("https://github.com/gronxb/webview-bridge");
}
}}
>
open InAppBrowser
</button>
<Count />
<button onClick={() => increase()}>Increase from web</button>
<DataText />
</div>
);
}
Utilize hooks from createLinkBridgeProvider
for client-side functionality as shown above.
Page Side
app/page.tsx
import dynamic from "next/dynamic";
import { Suspense } from "react";
import BridgeHome from "./BridgeHome";
const Page = () => {
return (
<Suspense fallback={<div>Loading...</div>}>
<BridgeHome />
</Suspense>
);
};
export default Page;