Static Deployments

Build a pure static Waku app and publish dist/public without a server runtime.


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
  • Waku 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:

// src/pages/index.tsx
export default async function HomePage() {
  return <main>...</main>;
}

export const getConfig = async () => {
  return {
    render: 'static',
  } as const;
};

Dynamic segments need staticPaths so Waku knows which concrete URLs to emit:

// src/pages/blog/[slug].tsx
export default async function BlogPost({ slug }: { slug: string }) {
  return <article>...</article>;
}

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:

// src/pages/docs/[section]/[slug].tsx
export const getConfig = async () => {
  return {
    render: 'static',
    staticPaths: [
      ['guides', 'getting-started'],
      ['reference', 'configuration'],
    ],
  } as const;
};

API routes are dynamic by default. If an API route produces a build-time resource, mark it static too:

// 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:

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:

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:

/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:

// src/waku.server.tsx
import { fsRouter } from 'waku';
import adapter from 'waku/adapters/vercel';

export default adapter(
  fsRouter(import.meta.glob('./**/*.{tsx,ts}', { base: './pages' })),
  { static: true },
);

Use the adapter for your platform:

PlatformAdapterStatic output behavior
Vercelwaku/adapters/vercelcopies dist/public to .vercel/output/static
Netlifywaku/adapters/netlifyuses dist/public as the publish directory
Cloudflarewaku/adapters/cloudflareconfigures 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.

designed bycandycode alternative graphic design web development agency San Diego