493 lines
13 KiB
Plaintext
493 lines
13 KiB
Plaintext
---
|
|
title: HTML & static sites
|
|
description: Build static sites, landing pages, and web applications with Bun's bundler
|
|
---
|
|
|
|
Bun's bundler has first-class support for HTML. Build static sites, landing pages, and web applications with zero configuration: point Bun at your HTML file and it bundles the scripts, stylesheets, and assets the file references.
|
|
|
|
```html title="index.html" icon="file-code"
|
|
<!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`.
|
|
|
|
```bash terminal icon="terminal"
|
|
bun ./index.html
|
|
```
|
|
|
|
```
|
|
Bun v1.3.3
|
|
ready in 6.62ms
|
|
→ http://localhost:3000/
|
|
Press h + Enter to show shortcuts
|
|
```
|
|
|
|
With no configuration, Bun's development server provides:
|
|
|
|
- **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 by default
|
|
- **Smart Configuration** - Reads `tsconfig.json` for paths, JSX options, and experimental decorators
|
|
- **Plugins** - Plugin support, including TailwindCSS
|
|
- **ESM & CommonJS** - Use ESM and CommonJS in your JavaScript, TypeScript, and JSX files
|
|
- **CSS Bundling & Minification** - Bundles CSS from `<link>` tags and `@import` statements
|
|
- **Asset Management** - Copies and hashes images and assets, and rewrites asset paths in JavaScript, CSS, and HTML
|
|
|
|
## Single Page Apps (SPA)
|
|
|
|
When you pass a single `.html` file to Bun, Bun uses it as a fallback route for all paths. This suits single page apps that use client-side routing:
|
|
|
|
```bash terminal icon="terminal"
|
|
bun index.html
|
|
```
|
|
|
|
```
|
|
Bun v1.3.3
|
|
ready in 6.62ms
|
|
→ http://localhost:3000/
|
|
Press h + Enter to show shortcuts
|
|
```
|
|
|
|
Your React or other SPA works with no configuration. Routes like `/about` and `/users/123` serve the same HTML file, so your client-side router handles the navigation.
|
|
|
|
```html title="index.html" icon="file-code"
|
|
<!doctype html>
|
|
<html>
|
|
<head>
|
|
<title>My SPA</title>
|
|
<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`:
|
|
|
|
```bash terminal icon="terminal"
|
|
bun ./index.html ./about.html
|
|
```
|
|
|
|
```txt
|
|
Bun v1.3.3
|
|
ready in 6.62ms
|
|
→ http://localhost:3000/
|
|
Routes:
|
|
/ ./index.html
|
|
/about ./about.html
|
|
Press h + Enter to show shortcuts
|
|
```
|
|
|
|
This serves:
|
|
|
|
- `index.html` at `/`
|
|
- `about.html` at `/about`
|
|
|
|
### Glob patterns
|
|
|
|
To specify multiple files, use a glob pattern that ends in `.html`:
|
|
|
|
```bash terminal icon="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 shortcuts
|
|
```
|
|
|
|
### Path normalization
|
|
|
|
Bun chooses the base path from the longest common prefix among all the files.
|
|
|
|
```bash terminal icon="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 shortcuts
|
|
```
|
|
|
|
## JavaScript, TypeScript, and JSX
|
|
|
|
Bun's transpiler natively implements JavaScript, TypeScript, and JSX support. See [loaders](/bundler/loaders).
|
|
|
|
<Note>Bun also uses the same transpiler at runtime.</Note>
|
|
|
|
### ES Modules & CommonJS
|
|
|
|
You can use ESM and CommonJS in your JavaScript, TypeScript, and JSX files. Bun transpiles and bundles them automatically.
|
|
|
|
There is no pre-build or separate optimization step. Bun does it all at the same time.
|
|
|
|
See [module resolution](/runtime/module-resolution).
|
|
|
|
## CSS
|
|
|
|
Bun's CSS parser is also natively implemented (about 70,000 lines of Rust).
|
|
|
|
It's also a CSS bundler. You can use `@import` in your CSS files to import other CSS files.
|
|
|
|
For example:
|
|
|
|
<CodeGroup>
|
|
|
|
```css styles.css icon="file-code"
|
|
@import "./abc.css";
|
|
|
|
.container {
|
|
background-color: blue;
|
|
}
|
|
```
|
|
|
|
```css abc.css icon="file-code"
|
|
body {
|
|
background-color: red;
|
|
}
|
|
```
|
|
|
|
</CodeGroup>
|
|
|
|
This outputs:
|
|
|
|
```css styles.css icon="file-code"
|
|
body {
|
|
background-color: red;
|
|
}
|
|
|
|
.container {
|
|
background-color: blue;
|
|
}
|
|
```
|
|
|
|
### Referencing local assets in CSS
|
|
|
|
```css styles.css icon="file-code"
|
|
body {
|
|
background-image: url("./logo.png");
|
|
}
|
|
```
|
|
|
|
Bun copies `./logo.png` to the output directory and rewrites the path in the CSS file to include a content hash.
|
|
|
|
```css styles.css icon="file-code"
|
|
body {
|
|
background-image: url("./logo-[ABC123].png");
|
|
}
|
|
```
|
|
|
|
### Importing CSS in JavaScript
|
|
|
|
To associate a CSS file with a JavaScript file, import it from the JavaScript file.
|
|
|
|
```ts app.ts icon="/icons/typescript.svg"
|
|
import "./styles.css";
|
|
import "./more-styles.css";
|
|
```
|
|
|
|
This generates `./app.css` and `./app.js` in the output directory. Bun bundles all CSS files imported from JavaScript into a single CSS file per entry point. If you import the same CSS file from multiple JavaScript files, Bun includes it only once in the output CSS file.
|
|
|
|
## Plugins
|
|
|
|
The dev server supports plugins.
|
|
|
|
### Tailwind CSS
|
|
|
|
To use TailwindCSS, install the `bun-plugin-tailwind` plugin:
|
|
|
|
```bash terminal icon="terminal"
|
|
# Or any npm client
|
|
bun install --dev bun-plugin-tailwind
|
|
```
|
|
|
|
Then, add the plugin to your `bunfig.toml`:
|
|
|
|
```toml title="bunfig.toml" icon="settings"
|
|
[serve.static]
|
|
plugins = ["bun-plugin-tailwind"]
|
|
```
|
|
|
|
Then, reference TailwindCSS in your HTML with a `<link>` tag, an `@import` in CSS, or an import in JavaScript.
|
|
|
|
<Tabs>
|
|
<Tab title="index.html">
|
|
|
|
```html title="index.html" icon="file-code"
|
|
<!-- Reference TailwindCSS in your HTML -->
|
|
<link rel="stylesheet" href="tailwindcss" />
|
|
```
|
|
|
|
</Tab>
|
|
<Tab title="styles.css">
|
|
|
|
```css title="styles.css" icon="file-code"
|
|
@import "tailwindcss";
|
|
```
|
|
|
|
</Tab>
|
|
<Tab title="app.ts">
|
|
|
|
```ts title="app.ts" icon="/icons/typescript.svg"
|
|
import "tailwindcss";
|
|
```
|
|
|
|
</Tab>
|
|
</Tabs>
|
|
|
|
<Info>Only one of these is necessary, not all three.</Info>
|
|
|
|
## Inline environment variables
|
|
|
|
Bun can replace `process.env.*` references in your JavaScript and TypeScript with their actual values at build time. Use this to inject 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`:
|
|
|
|
```toml title="bunfig.toml" icon="settings"
|
|
[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)
|
|
```
|
|
|
|
<Note>
|
|
Inlining 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.
|
|
|
|
</Note>
|
|
|
|
Then run the dev server:
|
|
|
|
```bash terminal icon="terminal"
|
|
PUBLIC_API_URL=https://api.example.com bun ./index.html
|
|
```
|
|
|
|
### Build for production
|
|
|
|
When building static HTML for production, use the `env` option to inline environment variables:
|
|
|
|
<Tabs>
|
|
<Tab title="CLI">
|
|
```bash terminal icon="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_*
|
|
```
|
|
|
|
</Tab>
|
|
<Tab title="API">
|
|
```ts title="build.ts" icon="/icons/typescript.svg"
|
|
// Inline all environment variables
|
|
await Bun.build({
|
|
entrypoints: ["./index.html"],
|
|
outdir: "./dist",
|
|
env: "inline", // [!code highlight]
|
|
});
|
|
|
|
// Only inline env vars with a specific prefix (recommended)
|
|
await Bun.build({
|
|
entrypoints: ["./index.html"],
|
|
outdir: "./dist",
|
|
env: "PUBLIC_*", // [!code highlight]
|
|
});
|
|
```
|
|
|
|
</Tab>
|
|
</Tabs>
|
|
|
|
### Example
|
|
|
|
Given this source file:
|
|
|
|
```ts title="app.ts" icon="/icons/typescript.svg"
|
|
const apiUrl = process.env.PUBLIC_API_URL;
|
|
console.log(`API URL: ${apiUrl}`);
|
|
```
|
|
|
|
And running with `PUBLIC_API_URL=https://api.example.com`:
|
|
|
|
```bash terminal icon="terminal"
|
|
PUBLIC_API_URL=https://api.example.com bun build ./index.html --outdir=dist --env=PUBLIC_*
|
|
```
|
|
|
|
The bundled output contains:
|
|
|
|
```js title="dist/app.js" icon="/icons/javascript.svg"
|
|
const apiUrl = "https://api.example.com";
|
|
console.log(`API URL: ${apiUrl}`);
|
|
```
|
|
|
|
## Echo console logs from browser to terminal
|
|
|
|
Bun's dev server can stream console logs from the browser to the terminal. To enable this, pass the `--console` CLI flag.
|
|
|
|
```bash terminal icon="terminal"
|
|
bun ./index.html --console
|
|
```
|
|
|
|
```
|
|
Bun v1.3.3
|
|
ready in 6.62ms
|
|
→ http://localhost:3000/
|
|
Press h + Enter to show shortcuts
|
|
```
|
|
|
|
Bun broadcasts each `console.log` or `console.error` call to the terminal that started the server, so browser errors show up in the same place you run your server. This also helps AI agents that watch terminal output.
|
|
|
|
Internally, Bun reuses the existing WebSocket connection from hot module replacement (HMR) to send the logs.
|
|
|
|
## Edit files in the browser
|
|
|
|
Bun's frontend dev server supports Automatic Workspace Folders in Chrome DevTools, so you can save edits to files from 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:
|
|
|
|
<Tabs>
|
|
<Tab title="CLI">
|
|
```bash terminal icon="terminal"
|
|
bun build ./index.html --minify --outdir=dist
|
|
```
|
|
</Tab>
|
|
<Tab title="API">
|
|
```ts title="build.ts" icon="/icons/typescript.svg"
|
|
await Bun.build({
|
|
entrypoints: ["./index.html"],
|
|
outdir: "./dist",
|
|
minify: true,
|
|
});
|
|
```
|
|
</Tab>
|
|
</Tabs>
|
|
|
|
<Warning>
|
|
Plugins are only supported through `Bun.build`'s API or through `bunfig.toml` with the frontend dev server, not
|
|
through `bun build`'s CLI.
|
|
</Warning>
|
|
|
|
### Watch Mode
|
|
|
|
Run `bun build --watch` to watch for changes and rebuild automatically. This works well for library development.
|
|
|
|
<Info>You've never seen a watch mode this fast.</Info>
|
|
|
|
## Plugin API
|
|
|
|
For more control, configure the bundler through the JavaScript API and use Bun's built-in `HTMLRewriter` to preprocess HTML.
|
|
|
|
```ts title="build.ts" icon="/icons/typescript.svg"
|
|
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 <script> tags, <link rel="stylesheet"> tags, and other assets
|
|
// and bundle them automatically
|
|
contents: rewriter.transform(html),
|
|
loader: "html",
|
|
};
|
|
});
|
|
},
|
|
},
|
|
],
|
|
});
|
|
```
|
|
|
|
## What Gets Processed?
|
|
|
|
Bun automatically handles all common web assets:
|
|
|
|
- **Scripts** (`<script src>`) are run through Bun's JavaScript/TypeScript/JSX bundler
|
|
- **Stylesheets** (`<link rel="stylesheet">`) are run through Bun's CSS parser & bundler
|
|
- **Images** (`<img>`, `<picture>`) are copied and hashed
|
|
- **Media** (`<video>`, `<audio>`, `<source>`) are copied and hashed
|
|
- Any `<link>` tag with an `href` attribute pointing to a local file is rewritten to the new path, and hashed
|
|
|
|
Bun resolves all paths relative to your HTML file, so you can organize your project however you want.
|
|
|
|
<Warning>
|
|
**This is a work in progress**
|
|
- Need more plugins
|
|
- Need more configuration options for things like asset handling
|
|
- Need a way to configure CORS, headers, etc.
|
|
|
|
{/* todo: find the correct link to link to as this 404's and there isn't any similar files */}
|
|
{/* If you want to submit a PR, most of the code is [here](https://github.com/oven-sh/bun/blob/main/src/runtime/api/bun/html-rewriter.ts). You could even copy paste that file into your project and use it as a starting point. */}
|
|
|
|
</Warning>
|
|
|
|
## How this works
|
|
|
|
The dev server is a small wrapper around Bun's support for [HTML imports](/bundler/fullstack) in JavaScript.
|
|
|
|
## Standalone HTML
|
|
|
|
You can bundle your entire frontend into a **single self-contained `.html` file** with no external dependencies using `--compile --target=browser`. Bun inlines all JavaScript, CSS, and images directly into the HTML.
|
|
|
|
```bash terminal icon="terminal"
|
|
bun build --compile --target=browser ./index.html --outdir=dist
|
|
```
|
|
|
|
Learn more in the [Standalone HTML docs](/bundler/standalone-html).
|
|
|
|
## Adding a backend to your frontend
|
|
|
|
To add a backend to your frontend, use the `routes` option in `Bun.serve`. See the [full-stack docs](/bundler/fullstack).
|