Getting started
Fuzor needs Node.js 22.15.0+ (22.x), or 24+. Create an app with the official scaffold:
pnpm create fuzor@latest my-app
cd my-app
pnpm dev
npm create fuzor@latest my-app
cd my-app
npm run dev
yarn create fuzor my-app
cd my-app
yarn dev
bun create fuzor my-app
cd my-app
bun dev
What you get
my-app/
├── index.html # document shell with the Fuzor outlet
├── pages/
│ ├── layout.html # wraps every page
│ ├── index.html # /
│ ├── about/index.html # /about/
│ └── docs/index.md # /docs/
├── components/
│ └── app-badge.html # <app-badge></app-badge>
├── islands/
│ └── counter.ts # <fuzor-island src="counter">
├── server.mjs # Node server for server builds
└── vite.config.ts
The shell's outlet comments mark where compiled pages go:
<body><!--fuzor:outlet-start--><!--fuzor:outlet-end--></body>
And the Vite config only adds the plugin:
import { defineConfig } from "vite";
import { fuzorPlugin } from "fuzor/vite";
export default defineConfig({ plugins: [fuzorPlugin()] });
Add a page
Create pages/pricing/index.html:
<h1>Pricing</h1>
<p>Free while Fuzor is in development.</p>
<fuzor-link href="/">Back home</fuzor-link>
It is available at /pricing/ immediately. <fuzor-link> compiles to a plain <a>, so it works with or without JavaScript; in SPA builds the router handles it without a document load. Markdown works the same way: pages/changelog/index.md becomes /changelog/.
Add interactivity
An island is a module in islands/ with a mount function:
// islands/like-button.ts
export function mount(host: HTMLElement) {
let likes = 0;
const button = host.ownerDocument.createElement("button");
button.textContent = "Like";
button.addEventListener("click", () => { button.textContent = `Liked ${++likes}×`; });
host.replaceChildren(button);
}
<fuzor-island src="like-button" client:visible>Loading…</fuzor-island>
Only this module's JavaScript is loaded, and only when the island scrolls into view. React, Solid and Vue components work as islands too; see islands.
Build and preview
pnpm build:spa # dist-spa/
pnpm build:mpa # dist-mpa/
pnpm preview:mpa # serve dist-mpa/ locally
Both builds come from the same files. When you add an api/ folder or server data, builds include a Node server automatically. Build profiles explains every output, and deployment how to ship them.