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>
├── client/
│ └── counter.ts # HTML module script
├── 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
Use a normal module script in the HTML. Add lang="ts" for TypeScript:
<section>
<button type="button">Like</button>
<script type="module" lang="ts">
let likes: number = 0
const button = host.querySelector("button")!
button.addEventListener("click", () => {
button.textContent = `Liked ${++likes}×`
}, { signal })
</script>
</section>
For larger code, use <script type="module" src="/client/like-button.ts"></script>. ESM imports work normally. The compiler extracts client logic while preserving the static HTML. See HTML module scripts for lifecycle and resumability boundaries. React, Solid and Vue use third-party islands.
Build and preview
pnpm build:spa # .fuzor/web/dist-spa/
pnpm build:mpa # .fuzor/web/dist-mpa/
pnpm preview:mpa # serve .fuzor/web/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.