Static Site Generation (SSG)
Set output: "static" in void.config.ts to prerender all pages at build time:
{
"output": "static"
}When output is "static":
- Pages default to
prerender = trueand are written as HTML files todist/client/. - Use
export const prerender = falsein a page's.server.tsto opt out. That page will be server-rendered on request. - Dynamic pages without
getPrerenderPaths()are implicitly not prerendered (the paths aren't known at build time). - The build output is self-contained and works for direct Cloudflare deployment, self-hosting, or
void deploy.
Trailing slashes and directory indexes
For hosts such as GitHub Pages, enable trailing-slash Pages URLs and directory-style HTML output:
import { defineConfig } from 'void/config';
export default defineConfig({
output: 'static',
routing: { trailingSlash: true },
});Void writes /posts/ to posts/index.html and /posts/hello/ to posts/hello/index.html. Use trailing slashes in your links; generated route types follow this setting. In development and when served by Void, URLs without the slash redirect to it. API URLs stay the same.
The default is false, which writes posts.html. The error page stays at 404.html in either mode.
On Cloudflare, use default HTML handling or force-trailing-slash, rather than drop-trailing-slash. Third-party frameworks use their own URL settings.
Per-page overrides
Individual pages can opt out of prerendering:
// pages/dashboard.server.ts
export const prerender = false;For dynamic pages, return the paths to render from getPrerenderPaths(). Annotate the return type with PageParams to check the parameter names:
// pages/blog/[slug].server.ts
import type { PageParams } from 'void/routes';
export async function getPrerenderPaths(): Promise<PageParams<'/blog/[slug]'>[]> {
return [{ slug: 'hello-world' }, { slug: 'getting-started' }];
}Return every required parameter as a string or number. Void URL-encodes the values. For a value containing /, use a catch-all page such as pages/docs/[...slug] with { slug: 'guides/intro' }.
TypeScript needs the annotation above to check the keys. Invalid entries stop the build with their server filename and index.
404 pages
Static builds that prerender at least one page include a default 404.html in dist/client/. To provide your own, add public/404.html or prerender a page at /404.
Fully static Void deployments serve this page for missing URLs with an HTTP 404 status by default. You can override this with routing.notFound. Hybrid deployments handle missing URLs in the Worker.
When hosting dist/client/ yourself, configure your host to serve 404.html with a 404 status for missing pages.
Comparison with edge prerendering
output value | Default prerender | Per-page override | Prerender timing |
|---|---|---|---|
"server" (default) | false | export const prerender = true | Deploy-time (platform ISR) |
"static" | true | export const prerender = false | Build or deploy post-build |
With the default output: "server", use export const prerender = true for deploy-time edge prerendering.
Deployment behavior
void deploy chooses the deployment automatically:
- Fully static: every page is prerendered, with no API routes, middleware, cron jobs, or queues. Void deploys the built files as a static site.
- Hybrid: some pages need server rendering, such as a dynamic page without
getPrerenderPaths()or a page withprerender = false. Void deploys a Worker for those routes and serves prerendered pages as static assets.
On direct Cloudflare deployments, static files can bypass the Worker:
- Fully static apps serve files directly. Edge rules, including
_headersor_redirectsinpublic/, require a small Worker on every request. - Hybrid apps serve prerendered pages directly when they have no
.server.tscompanion. API routes, dynamic pages, and pages with server companions run in the Worker.
Every page uses the Worker if the app has middleware, auth, live updates, edge rules, a custom Vite base, a routing.notFound value other than "none", or a dynamic page at the root of pages/, such as pages/[slug].vue. With these settings, prerendered requests also run through the Worker.
Relationship to inference.appType: "static"
The inference.appType field describes app type (SPA, static, void), while output controls rendering strategy:
inference.appType: "static" | output: "static" | |
|---|---|---|
| What | Deploy a pre-built static site (no Void plugin) | Prerender a Void app at build time |
| Worker | None (static assets only) | Only for server code and edge rules |
| Use case | VitePress, plain HTML, external SSG tools | Void apps with mostly or fully static content |