--- slug: static-deployments title: Static Deployments description: Build a pure static Waku app and publish dist/public without a server runtime. category: Deployment order: 5 --- ## When Static Deployment Fits A static deployment serves only the files generated during `waku build`. There is no Waku server, worker, Lambda function, or other request-time runtime after deploy. Use a static deployment for apps where every public route can be known and rendered at build time: - marketing sites - documentation sites - blogs with generated static paths - static resources such as RSS feeds, sitemaps, and JSON files Do not use a static-only deployment if the app needs request-time behavior: - dynamic routes without `staticPaths` - dynamic API routes - server actions - per-request cookies, headers, authentication, or personalization - middleware that must run for deployed requests - redirects or rewrites that must be decided at request time If you need those features, deploy Waku with a server-capable adapter instead. Static pages can still be part of a server-capable deployment. ## Make Routes Static Pages, layouts, and slices are static by default in the file-system router. For routes that matter to a static deployment, it is still useful to be explicit: ```tsx // src/pages/index.tsx export default async function HomePage() { return
...
; } export const getConfig = async () => { return { render: 'static', } as const; }; ``` Dynamic segments need `staticPaths` so Waku knows which concrete URLs to emit: ```tsx // src/pages/blog/[slug].tsx export default async function BlogPost({ slug }: { slug: string }) { return
...
; } export const getConfig = async () => { return { render: 'static', staticPaths: ['introducing-waku', 'deploying-waku'], } as const; }; ``` For nested segments, use arrays in the same order as the route parameters: ```tsx // src/pages/docs/[section]/[slug].tsx export const getConfig = async () => { return { render: 'static', staticPaths: [ ['guides', 'quick-start'], ['reference', 'configuration'], ], } as const; }; ``` API routes are dynamic by default. If an API route produces a build-time resource, mark it static too: ```ts // src/pages/_api/rss.xml.ts export const GET = async () => { return new Response(generateRss(), { headers: { 'content-type': 'application/rss+xml' }, }); }; export const getConfig = async () => { return { render: 'static', } as const; }; ``` ## Build Output Run the normal production build: ```sh pnpm exec waku build ``` Static files are written to `dist/public`. Publish that entire directory: - generated HTML files - generated RSC payloads under Waku's RSC path - Vite client assets - files copied from `public` - static API route output Do not publish only the HTML and asset files. The generated RSC payloads are part of the static app and are used during client-side navigation. ## Static Hosts For a plain static host, configure the publish directory as: ```txt dist/public ``` The host should serve generated files as they exist on disk. Avoid using a single-page-app fallback to serve `index.html` for every path. If a route is not emitted during the build, either add it to `staticPaths` or use a server-capable deployment. If the host lets you configure response headers, keep Waku's generated RSC files deployable and cache immutable client assets aggressively. For public RSC payloads, a common choice is to prevent search indexing: ```txt /RSC/* X-Robots-Tag: noindex ``` ## Adapter Static Mode Some platform adapters can generate platform-specific static deployment output. Use their `static` option when you want that platform integration without a serverless function: ```ts // src/waku.server.tsx import { fsRouter } from 'waku'; import adapter from 'waku/adapters/vercel'; export default adapter(fsRouter(import.meta.glob('./pages/**/*.{tsx,ts}')), { static: true, }); ``` Use the adapter for your platform: | Platform | Adapter | Static output behavior | | ---------- | -------------------------- | ------------------------------------------------------------- | | Vercel | `waku/adapters/vercel` | copies `dist/public` to `.vercel/output/static` | | Netlify | `waku/adapters/netlify` | uses `dist/public` as the publish directory | | Cloudflare | `waku/adapters/cloudflare` | configures Wrangler static assets to serve from `dist/public` | The static option only changes the deployment output. It does not make dynamic Waku features work without a runtime. ## Static Rendering Is Not No SSR Static rendering prerenders route output at build time and writes that output to `dist/public`. The generated HTML can contain the page's rendered content. No SSR serves a fallback shell and lets the browser render the route after JavaScript starts. It is useful for different tradeoffs and does not make an app deployable without a server runtime. For that pattern, see [No SSR and Static Fallbacks](/guides/no-ssr).