Custom Protocols
Custom protocols handle URL schemes such as app:// without starting a local HTTP server. Register each scheme before creating a webview.
Custom protocols handle URL schemes such as app:// without starting a local HTTP server. Register each scheme before creating a webview.
import { readFile } from 'node:fs/promises';
import { extname, join } from 'node:path';
import { Application } from '@webviewjs/webview';
const MIME = {
'.html': 'text/html; charset=utf-8',
'.js': 'application/javascript; charset=utf-8',
'.css': 'text/css',
};
const app = new Application();
const win = app.createBrowserWindow();
win.registerProtocol('app', async (request) => {
const url = new URL(request.url);
const path = join(process.cwd(), 'dist', url.pathname);
try {
return new Response(await readFile(path), {
headers: { 'Content-Type': MIME[extname(path)] ?? 'application/octet-stream' },
});
} catch {
return new Response(`Not found: ${url.pathname}`, {
status: 404,
headers: { 'Content-Type': 'text/plain; charset=utf-8' },
});
}
});
win.createWebview({ url: 'app://localhost/index.html' });
app.run();The handler receives the standard global Fetch API Request. It may return a
Response, a Promise of a Response, or the legacy plain object shown below.
This makes Fetch-compatible routers such as Hono usable directly. Rejected
handlers and thrown errors become a 500 text/plain response.
Hono
Forward the request directly to a Hono application. Hono returns a standard
Fetch API Response, so no adapter or HTTP server is required:
import { Hono } from 'hono';
const router = new Hono();
router.get('/*', (context) => {
return context.html(`<h1>Current page: ${context.req.path}</h1>`);
});
win.registerProtocol('app', (request) => router.fetch(request));
win.createWebview({ url: 'app://localhost/' });The runnable Hono custom protocol example includes dynamic pages, navigation links, pathname rendering, and application shutdown handling.
Request and response types
interface CustomProtocolResponse {
body: Buffer;
statusCode?: number; // default: 200
mimeType?: string; // default: application/octet-stream
headers?: HeaderData[];
}Request bodies on Linux
Custom protocol Request objects include request bodies on Linux when the
system WebKitGTK version is 2.40 or newer. The Linux native build enables Wry's
linux-body support. Older WebKitGTK versions may deliver an empty body.
The request body example
checks text, binary, and empty POST requests.
Security
Never resolve a request path without checking it remains inside the intended asset directory. Normalize and validate the path before passing it to the file system. The runnable custom protocol example includes this check.
Multiple protocols
Register multiple schemes before createWebview():
win.registerProtocol('app', appHandler);
win.registerProtocol('api', async (request) => {
const response = await fetch(`https://example.test${new URL(request.url).pathname}`);
return response;
});Protocol registrations are fixed when the webview is created. Registering a scheme after createWebview() does not affect an existing webview.
CORS and cache headers
Use response headers when the page needs them:
return new Response(JSON.stringify(data), {
headers: {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*',
'Cache-Control': 'no-store',
},
});