Skip to content

Local Webview Assets

Electrobun maps files under the packaged Resources/app/views directory to the views:// protocol. Use these URLs anywhere a webview accepts a normal URL.

build.views bundles JavaScript or TypeScript entrypoints. build.copy copies HTML, CSS, images, and other static files without transforming them.

import type { ElectrobunConfig } from "electrobun";
export default {
app: {
name: "Assets Example",
identifier: "dev.example.assets",
version: "1.0.0",
},
build: {
views: {
mainview: {
entrypoint: "src/mainview/index.ts",
},
},
copy: {
"src/mainview/index.html": "views/mainview/index.html",
"src/mainview/style.css": "views/mainview/style.css",
"src/mainview/logo.png": "views/mainview/logo.png",
},
},
} satisfies ElectrobunConfig;

The destination views/mainview/index.html becomes views://mainview/index.html at runtime. View names are application-defined; you can add as many entries as needed.

import { BrowserWindow } from "electrobun/main";
const mainWindow = new BrowserWindow({
title: "Bundled content",
url: "views://mainview/index.html",
frame: { width: 800, height: 600 },
});
void mainWindow;

BrowserWindow takes the URL in its constructor. Loading a new URL later is a BrowserView operation: mainWindow.webview.loadURL(url).

<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Bundled content</title>
<link rel="stylesheet" href="views://mainview/style.css" />
<script type="module" src="views://mainview/index.js"></script>
</head>
<body>
<img src="views://mainview/logo.png" alt="Application logo" />
</body>
</html>
body {
background-image: url("views://mainview/logo.png");
}

Each configured view is emitted as views/<view-name>/index.js, independent of the entrypoint’s filename. In this example, the mainview entry becomes views/mainview/index.js.

appdata:// maps to the app’s fixed, writable userData directory. Its exact OS-specific location is available as Utils.paths.userData in TypeScript and the userData / user_data / UserData field of the native SDKs’ resolved paths. See Paths for the directory layout.

Access is granted per webview and is disabled by default. Enable it when creating the BrowserWindow or BrowserView that needs to read those files:

import { BrowserWindow } from "electrobun/main";
const win = new BrowserWindow({
url: "views://mainview/index.html",
allowedProtocols: {
appData: true,
},
});

Files written beneath userData are then available to that webview as normal web resources:

<img src="appdata://images/avatar.png" alt="User avatar" />
const response = await fetch("appdata://documents/report.txt");
if (!response.ok) throw new Error("Could not load report");
const report = await response.text();

The protocol works from both views:// pages and remote pages, and its responses are CORS-enabled. Permission follows the webview, not the current page’s origin: if an enabled webview navigates to a remote URL, that remote page can also request appdata:// files.

Both views:// and appdata:// are confined to their respective roots. Absolute paths, .. traversal (including encoded traversal), and symlink escapes outside the root are rejected.

The complete per-webview option is:

type AllowedProtocols = {
views?: boolean; // defaults to true
appData?: boolean; // defaults to false
};

Set views: false for a webview that should not be able to read packaged UI assets. Unspecified properties retain their defaults.