DOCUMENTATION Docs Powered by Fuzor Docs

Build profiles

One source tree builds in four profiles. Pages, layouts, components and islands do not change between them.

NavigationStatic deliveryServer delivery
spadist-spa/index.html holds every page as a template, plus lazy island chunksdist-spa-server/: the same document and a Node server
mpadist-mpa/ holds one HTML file per pagedist-mpa-server/: the same documents and a Node server

Pages are always prerendered

Fuzor compiles every page to finished HTML during the build, including the initial markup of islands with known props. This does not change when an app gets a server:

  • A static build writes the documents as files.
  • A server build embeds the same documents in server/entry.mjs and also writes them to client/. For each request the server selects the finished document for the URL and sends it with an ETag. It never renders or assembles HTML per request.

So there is nothing to opt into for "static pages": every page already is one. What a server adds is request-time work that cannot be prerendered: API endpoints and server data that fills slots on a prerendered page. Because client/ contains the same documents, you can put it on a CDN and route only /api and /_fuzor/data to the server; see deployment.

SPA

The SPA document contains every page as a <template>. After it loads, the router swaps templates for navigation without requesting HTML, uses the browser Navigation API where available and the History API elsewhere, restores scroll positions, moves focus and announces the new page to screen readers. Island JavaScript loads only when an island on the current page activates.

The document grows with the number of pages. Measured with pnpm benchmark on an Apple M1 (Chromium, local server, a shared layout; half Markdown pages, half HTML pages with a component and an island):

PagesBuildSPA document (gzip)DOMContentLoadedEntry JavaScript (gzip)
10001.7 s1.6 MB (26 KB)41 msSPA 33 KB, MPA 14 KB
20002.5 s3.3 MB (49 KB)60 msSPA 33 KB, MPA 14 KB

An MPA page document stays under 2 KB regardless of the page count, and neither mode shifted layout (CLS 0). Pages are highly repetitive and compress well, so the SPA stays practical into the low thousands of pages; beyond that, or on slow connections, prefer the MPA. A static host must send index.html for deep links (most hosts call this an SPA fallback); server builds and fuzor preview do this for you.

fuzor build --target spa --inline puts all JavaScript and CSS into one index.html for embedded or offline use. It cannot be combined with server delivery.

MPA

Each page is its own document and links are ordinary document navigations, so pages work with JavaScript disabled and JavaScript is only loaded for islands. Static MPA builds also write 404.html pages and redirect pages; see routing.

When a server is used

Server delivery is selected automatically when the app has:

  • an api/ folder with at least one endpoint, or
  • a server.ts next to any page.

The CLI prints the files that required it. --server (or fuzorPlugin({ server: true })) forces server delivery for an app without either, for example to get document ETags and caching headers from the Node server. Asking for a static build of an app that needs a server (server: false, FUZOR_SERVER=0 or --inline) is a build error that names the files.

Caching

Server-delivered documents are sent with Cache-Control: no-cache and a weak ETag, so browsers revalidate and get 304 Not Modified until a new build changes the page. Not-found pages use no-store. Hashed files in assets/ are immutable for a year.

Set revalidate (seconds) in a page's metadata to let shared caches such as CDNs keep the document:

---
title: Weekly report
revalidate: 3600
---

The document is then sent with Cache-Control: public, max-age=0, s-maxage=3600, stale-while-revalidate=3600. Browsers still revalidate every time; the CDN may serve it for an hour and refresh it in the background. Because the SPA document is shared by all pages, it uses the shortest revalidate of its pages, and none if any page lacks one. For HTML pages or dynamic routes, set revalidate in the paths entry's metadata. Static hosts set their own headers.

Rebuilding

Content changes are published by running fuzor build again, for example from a scheduled job or a CMS webhook:

  • The build is written to a staging folder and replaces the previous output only when it succeeds, so a failed rebuild keeps the last good version online.
  • A lock file ensures one build per project at a time.
  • Hashed assets from the previous build are kept for one more build, so pages already open in a browser can still load their island code. If a page is even older, a failed island import reloads it once.
  • A running Node server keeps the manifest it started with; restart it to serve the new build.
Built with Fuzor Back to top ↑
On this page Back to top ↑