HTML & static sites - BunDocumentation Index Search...⌘KInstall Bun Search...Navigation Asset Processing HTML & static sitesRuntimePackage ManagerBundlerTest RunnerGuidesReferenceBlogFeedback:first-child]:!hidden peer-[.is-custom]:[&>:first-child]:sm:!hidden peer-[.is-custom]:[&>:first-child]:md:!hidden peer-[.is-custom]:[&>:first-child]:lg:!hidden peer-[.is-custom]:[&>:first-child]:xl:!hidden">CoreBundlerDevelopment ServerFullstack dev serverHot reloadingAsset ProcessingHTML & static sitesStandalone HTMLCSSLoadersSingle File ExecutableSingle-file executableExtensionsPluginsMacrosOptimizationBytecode CachingMinifierMigrationesbuild On this pageSingle Page Apps (SPA)Multi-page apps (MPA)Glob patternsPath normalizationJavaScript, TypeScript, and JSXES Modules & CommonJSCSSReferencing local assets in CSSImporting CSS in JavaScriptPluginsTailwind CSSInline environment variablesDev server (runtime)Build for productionExampleEcho console logs from browser to terminalEdit files in the browserKeyboard ShortcutsBuild for ProductionWatch ModePlugin APIWhat Gets Processed?How this worksStandalone HTMLAdding a backend to your frontendAsset ProcessingHTML & static sites Copy pagespan]:line-clamp-1 overflow-hidden group flex items-center py-0.5 gap-1 text-sm text-gray-950/50 dark:text-white/50 group-hover:text-gray-950/70 dark:group-hover:text-white/70 rounded-none rounded-r-xl border px-3 border-gray-200 aspect-square dark:border-white/[0.07] bg-background-light dark:bg-background-dark hover:bg-gray-600/5 dark:hover:bg-gray-200/5" aria-label="More actions" type="button" id="radix-_R_n4ctdbsnlht5lebsnpfdb_" aria-haspopup="menu" aria-expanded="false" data-state="closed"> *]:[overflow-wrap:anywhere]"> Copy pagespan]:line-clamp-1 overflow-hidden group flex items-center py-0.5 gap-1 text-sm text-gray-950/50 dark:text-white/50 group-hover:text-gray-950/70 dark:group-hover:text-white/70 rounded-none rounded-r-xl border px-3 border-gray-200 aspect-square dark:border-white/[0.07] bg-background-light dark:bg-background-dark hover:bg-gray-600/5 dark:hover:bg-gray-200/5" aria-label="More actions" type="button" id="radix-_R_1cctdbsnlht5lebsnpfdb_" aria-haspopup="menu" aria-expanded="false" data-state="closed"> Bun’s bundler has first-class support for HTML. Build static sites, landing pages, and web applications with zero configuration. Just point Bun at your HTML file and it handles everything else.
index.html Bun’s transpiler is also used at runtime.
ES Modules & CommonJS
You can use ESM and CJS in your JavaScript, TypeScript, and JSX files. Bun will handle the transpilation and bundling automatically.
There is no pre-build or separate optimization step. It’s all done at the same time.
Learn more about module resolution in Bun.
CSS
Bun’s CSS parser is also natively implemented (clocking in around 58,000 lines of Zig).
It’s also a CSS bundler. You can use @import in your CSS files to import other CSS files.
For example:
styles.cssabc.css Only one of those are necessary, not all three.
Inline environment variables
Bun can replace process.env.* references in your JavaScript and TypeScript with their actual values at build time. This is useful for injecting configuration like API URLs or feature flags into your frontend code.
Dev server (runtime)
To inline environment variables when using bun ./index.html, configure the env option in your bunfig.toml:
bunfig.toml This only works with literal process.env.FOO references, not import.meta.env or indirect access like const env = process.env; env.FOO.If an environment variable is not set, you may see runtime errors like ReferenceError: process is not defined in the browser.
Then run the dev server:
terminal Currently, plugins are only supported through Bun.build’s API or through bunfig.toml with the frontend dev server
not yet supported in bun build’s CLI.
Watch Mode
You can run bun build --watch to watch for changes and rebuild automatically. This works nicely for library development.
You’ve never seen a watch mode this fast.
Plugin API
Need more control? Configure the bundler through the JavaScript API and use Bun’s builtin HTMLRewriter to preprocess HTML.
build.ts
Fetch the complete documentation index at: /docs/llms.txt
Use this file to discover all available pages before exploring further.
Skip to main contentBun home pageBuild static sites, landing pages, and web applications with Bun’s bundler
doctype html> html> head> link rel="stylesheet" href="./styles.css" /> script src="./app.ts" type="module">script> head> body> img src="./logo.png" /> body> html>To get started, pass HTML files to bun. terminal
bun ./index.html
Bun v1.3.3 ready in 6.62ms → http://localhost:3000/ Press h + Enter to show shortcutsBun’s development server provides powerful features with zero configuration: Automatic Bundling - Bundles and serves your HTML, JavaScript, and CSS Multi-Entry Support - Handles multiple HTML entry points and glob entry points Modern JavaScript - TypeScript & JSX support out of the box Smart Configuration - Reads tsconfig.json for paths, JSX options, experimental decorators, and more Plugins - Plugins for TailwindCSS and more ESM & CommonJS - Use ESM and CommonJS in your JavaScript, TypeScript, and JSX files CSS Bundling & Minification - Bundles CSS from tags and @import statements Asset Management - Automatic copying & hashing of images and assets; Rewrites asset paths in JavaScript, CSS, and HTML Single Page Apps (SPA) When you pass a single .html file to Bun, Bun will use it as a fallback route for all paths. This makes it perfect for single page apps that use client-side routing: terminal
bun index.html
Bun v1.3.3 ready in 6.62ms → http://localhost:3000/ Press h + Enter to show shortcutsYour React or other SPA will work out of the box — no configuration needed. All routes like /about, /users/123, etc. will serve the same HTML file, letting your client-side router handle the navigation. index.html
doctype html> html> head> title>My SPAtitle> script src="./app.tsx" type="module">script> head> body> div id="root">div> body> html>Multi-page apps (MPA) Some projects have several separate routes or HTML files as entry points. To support multiple entry points, pass them all to bun: terminal
bun ./index.html ./about.html
Bun v1.3.3 ready in 6.62ms → http://localhost:3000/ Routes: / ./index.html /about ./about.html Press h + Enter to show shortcutsThis will serve: index.html at / about.html at /about Glob patterns To specify multiple files, you can use glob patterns that end in .html: terminal
bun ./**/*.html
Bun v1.3.3 ready in 6.62ms → http://localhost:3000/ Routes: / ./index.html /about ./about.html Press h + Enter to show shortcutsPath normalization The base path is chosen from the longest common prefix among all the files. terminal
bun ./index.html ./about/index.html ./about/foo/index.html
Bun v1.3.3 ready in 6.62ms → http://localhost:3000/ Routes: / ./index.html /about ./about/index.html /about/foo ./about/foo/index.html Press h + Enter to show shortcutsJavaScript, TypeScript, and JSX Bun’s transpiler natively implements JavaScript, TypeScript, and JSX support. Learn more about loaders in Bun.
@import "./abc.css";
.container {
background-color: blue;
}
This outputs:
styles.cssbody {
background-color: red;
}
.container {
background-color: blue;
}
Referencing local assets in CSS
You can reference local assets in your CSS files.
styles.cssbody {
background-image: url("./logo.png");
}
This will copy ./logo.png to the output directory and rewrite the path in the CSS file to include a content hash.
styles.cssbody {
background-image: url("./logo-[ABC123].png");
}
Importing CSS in JavaScript
To associate a CSS file with a JavaScript file, you can import it in your JavaScript file.
app.tsimport "./styles.css"; import "./more-styles.css";This generates ./app.css and ./app.js in the output directory. All CSS files imported from JavaScript will be bundled into a single CSS file per entry point. If you import the same CSS file from multiple JavaScript files, it will only be included once in the output CSS file. Plugins The dev server supports plugins. Tailwind CSS To use TailwindCSS, install the bun-plugin-tailwind plugin: terminal
# Or any npm client bun install --dev bun-plugin-tailwindThen, add the plugin to your bunfig.toml: bunfig.toml
[serve.static] plugins = ["bun-plugin-tailwind"]Then, reference TailwindCSS in your HTML via tag, @import in CSS, or import in JavaScript. index.html styles.css app.ts:first-child:not(p)]:mt-0 [&>:first-child:not(p)_img]:mt-0 [&>:first-child[data-table-wrapper]]:!pt-0" data-component-part="tab-content">index.html
link rel="stylesheet" href="tailwindcss" />:first-child:not(p)]:mt-0 [&>:first-child:not(p)_img]:mt-0 [&>:first-child[data-table-wrapper]]:!pt-0 hidden" data-component-part="tab-content">styles.css
@import "tailwindcss";:first-child:not(p)]:mt-0 [&>:first-child:not(p)_img]:mt-0 [&>:first-child[data-table-wrapper]]:!pt-0 hidden" data-component-part="tab-content">app.ts
import "tailwindcss";
[serve.static] env = "PUBLIC_*" # only inline env vars starting with PUBLIC_ (recommended) # env = "inline" # inline all environment variables # env = "disable" # disable env var replacement (default)
PUBLIC_API_URL=https://api.example.com bun ./index.htmlBuild for production When building static HTML for production, use the env option to inline environment variables: CLI API:first-child:not(p)]:mt-0 [&>:first-child:not(p)_img]:mt-0 [&>:first-child[data-table-wrapper]]:!pt-0" data-component-part="tab-content">terminal
# Inline all environment variables bun build ./index.html --outdir=dist --env=inline # Only inline env vars with a specific prefix (recommended) bun build ./index.html --outdir=dist --env=PUBLIC_*:first-child:not(p)]:mt-0 [&>:first-child:not(p)_img]:mt-0 [&>:first-child[data-table-wrapper]]:!pt-0 hidden" data-component-part="tab-content">build.ts
// Inline all environment variables
await Bun.build({
entrypoints: ["./index.html"],
outdir: "./dist",
env: "inline",
});
// Only inline env vars with a specific prefix (recommended)
await Bun.build({
entrypoints: ["./index.html"],
outdir: "./dist",
env: "PUBLIC_*",
});
Example
Given this source file:
app.tsconst apiUrl = process.env.PUBLIC_API_URL;
console.log(`API URL: ${apiUrl}`);
And running with PUBLIC_API_URL=https://api.example.com:
terminalPUBLIC_API_URL=https://api.example.com bun build ./index.html --outdir=dist --env=PUBLIC_*The bundled output will contain: dist/app.js
const apiUrl = "https://api.example.com";
console.log(`API URL: ${apiUrl}`);
Echo console logs from browser to terminal
Bun’s dev server supports streaming console logs from the browser to the terminal.
To enable, pass the --console CLI flag.
terminalbun ./index.html --console
Bun v1.3.3 ready in 6.62ms → http://localhost:3000/ Press h + Enter to show shortcutsEach call to console.log or console.error will be broadcast to the terminal that started the server. This is useful to see errors from the browser in the same place you run your server. This is also useful for AI agents that watch terminal output. Internally, this reuses the existing WebSocket connection from hot module reloading to send the logs. Edit files in the browser Bun’s frontend dev server has support for Automatic Workspace Folders in Chrome DevTools, which lets you save edits to files in the browser. Keyboard Shortcuts While the server is running: o + Enter - Open in browser c + Enter - Clear console q + Enter (or Ctrl+C) - Quit server Build for Production When you’re ready to deploy, use bun build to create optimized production bundles: CLI API:first-child:not(p)]:mt-0 [&>:first-child:not(p)_img]:mt-0 [&>:first-child[data-table-wrapper]]:!pt-0" data-component-part="tab-content">terminal
bun build ./index.html --minify --outdir=dist:first-child:not(p)]:mt-0 [&>:first-child:not(p)_img]:mt-0 [&>:first-child[data-table-wrapper]]:!pt-0 hidden" data-component-part="tab-content">build.ts
await Bun.build({
entrypoints: ["./index.html"],
outdir: "./dist",
minify: true,
});
await Bun.build({
entrypoints: ["./index.html"],
outdir: "./dist",
minify: true,
plugins: [
{
// A plugin that makes every HTML tag lowercase
name: "lowercase-html-plugin",
setup({ onLoad }) {
const rewriter = new HTMLRewriter().on("*", {
element(element) {
element.tagName = element.tagName.toLowerCase();
},
text(element) {
element.replace(element.text.toLowerCase());
},
});
onLoad({ filter: /\.html$/ }, async args => {
const html = await Bun.file(args.path).text();
return {
// Bun's bundler will scan the HTML for HTML & static sites - Bun,AI智能索引,全网链接索引,智能导航,网页索引
- Build static sites, landing pages, and web applications with Bun