Static HTML
In this guide, you’ll learn how to embed a web app directly into a React Native application without deploying it separately. you’ll bundle the web app into a single HTML file, convert it into a string, and display it in a WebView.
Example: static-html
Installation
React Native Project
$ npm add @webview-bridge/react-native @webview-bridge/web react-native-webview
$ pnpm add @webview-bridge/react-native @webview-bridge/web react-native-webview
$ yarn add @webview-bridge/react-native @webview-bridge/web react-native-webview
Creating the Web App - New Project
In your React Native project, run the following commands to set up a Vite project:
> pnpm create vite web
> cd web
> rm .gitignore eslint.config.js package.json tsconfig.json tsconfig.app.json # remove unused file
> mv vite.config.ts ../vite.config.ts
> pnpm add vite -D
> pnpm add react react-dom # (Optional) Other frameworks can be used as well.
Move vite.config.ts
from the web folder to the parent React Native project. Since you deleted package.json
, you may need to install core libraries like react
or react-dom
.
vite.config.ts
You use vite-plugin-singlefile
to bundle everything into a single HTML file and vite-plugin-html-stringify
to convert the HTML into exportable code.
$ npm add vite-plugin-singlefile vite-plugin-html-stringify -D
$ pnpm add vite-plugin-singlefile vite-plugin-html-stringify -D
$ yarn add vite-plugin-singlefile vite-plugin-html-stringify -D
WARN
Q. Why do you export static HTML as a string again?
A. You cannot properly load static HTML with require in react-native-webview.
Please refer to the following issue: react-native-webview#746
import react from "@vitejs/plugin-react";
import { defineConfig } from "vite";
import { viteSingleFile } from "vite-plugin-singlefile";
import { viteHtmlStringify } from "vite-plugin-html-stringify";
export default defineConfig({
root: "./web",
plugins: [
react(),
viteSingleFile({
deleteInlinedFiles: true,
removeViteModuleLoader: true,
}),
viteHtmlStringify({
output: "./web/html.ts",
}),
],
});
package.json
{
"scripts": {
// ...
"build:web": "vite build",
"dev:web": "vite"
}
}
During development, use dev:web
, and before deploying to production, generate static HTML code with build:web
.
Setting Up the Bridge
React Native Part
// src/bridge.ts
import { createWebView, bridge } from "@webview-bridge/react-native";
import InAppBrowser from "react-native-inappbrowser-reborn";
// Register functions in the bridge object in your React Native code
export const appBridge = bridge({
async getMessage() {
return "Hello, I'm native";
},
async sum(a: number, b: number) {
return a + b;
},
async openInAppBrowser(url: string) {
if (await InAppBrowser.isAvailable()) {
await InAppBrowser.open(url);
}
},
// ... Add more functions as needed
});
export type AppBridge = typeof appBridge;
Create a WebView Component by combining the previously defined bridge
with createWebView
.
// src/App.tsx
import html from "../web/html"; // Generated HTML
import { appBridge } from "./bridge";
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={
__DEV
? {
uri: "http://localhost:5173",
}
: {
html, // Use the built HTML in production
}
}
style={{ height: "100%", flex: 1, width: "100%" }}
/>
</SafeAreaView>
);
}
export default App;
NOTE
When running in development mode, you can take advantage of HMR (Hot Module Replacement) for development. Before deployment, you need to generate html.ts
using the build:web command in your CI configuration.
// src/App.tsx
import html from "../web/html"; // generate html;
import { appBridge } from "./bridge";
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={
__DEV
? {
uri: "http://localhost:5173",
}
: {
html, // production일 때 빌드 된 html로 작동
}
}
style={{ height: "100%", flex: 1, width: "100%" }}
/>
</SafeAreaView>
);
}
export default App;
Web Part
Now, let's setting up the web project that will be displayed in the WebView. Utilize the previously exported AppBridge
as a generic in linkBridge
.
That's all there is to it!
You can directly use bridge
as shown below and receive the results.
// web/bridge.ts
import { linkBridge } from "@webview-bridge/web";
import type { AppBridge } from "../src/bridge.ts";
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