Navigated to /docs/core/static-output-and-cache

Static output and route cache

Choose build-time output or revalidated SSR, enumerate dynamic paths, and control shared response caching with vary keys and cache tags.

Choose caching for the deployment you run

TSXsrc/pages/catalog.tsx
tsxexport const revalidate = 300;
export const cacheTags = ["catalog"];

export default function CatalogPage() {
  return <main>Catalog</main>;
}
IntentFunctional moduledefineRoutePageBehavior
Cached SSR with regenerationexport const revalidate = 300revalidate: 300Numeric revalidate automatically enables static caching.
Build-time static HTML without revalidationexport const prerender = truestatic: trueOn Node, also enables a long-lived immutable response; review its freshness implications below.
Disable inherited static policyexport const revalidate = falserevalidate: falseResets inherited static and revalidation policy.

Declare revalidated dynamic output

For a dynamic cached route, generateStaticParams() returns PageStaticParams for the paths the build should materialize. The loader and cache-tag resolver receive PageLoadContext.

TSXsrc/pages/catalog/[id].tsx
tsximport { notFound, type PageLoadContext, type PageProps } from "@tavojs/core/router";
import { Page, Text } from "@tavojs/ui";

type Product = { id: string; name: string };
type ProductParams = { id: string };

const catalog: Record<string, Product> = {
  starter: { id: "starter", name: "Starter plan" },
  team: { id: "team", name: "Team plan" }
};

export const revalidate = 300;
export const vary = "accept-language";

export function cacheTags({
  params
}: PageLoadContext): string[] {
  return ["catalog", `product:${params.id}`];
}

export function generateStaticParams(): ProductParams[] {
  return [{ id: "starter" }, { id: "team" }];
}

export function load({ params }: PageLoadContext): Product {
  return catalog[params.id] ?? notFound();
}

export default function ProductPage({
  data
}: PageProps<Product, ProductParams>) {
  return (
    <Page>
      <Text as="h1" variant="h1">
        {data?.name}
      </Text>
    </Page>
  );
}
  • generateStaticParams returns parameter records for dynamic paths the build must enumerate. Every record must provide values expected by that route pattern.

  • This catalog is local so the example can prerender without another server. A production build needs access to every loader data source; an application-relative fetch does not start your own /api endpoint during the build.

  • A numeric revalidate is measured in seconds, rounded down, and clamped to zero.

  • Static policy composes through the route chain. The shortest finite revalidate value wins.

  • vary names are lowercased and deduplicated. Localization also adds Accept-Language variation.

  • CSR routes ignore static policy and static params with a manifest diagnostic.

Understand Node cache behavior

Inspection exposes the composed PageCachePolicy. Each static or request-aware tag declaration follows PageCacheTags.

  • A successful route with numeric revalidate emits Cache-Control: public, max-age=0, s-maxage=N.

  • Tavo.js uses the static cache only for responses eligible under the composed route policy.

  • Non-200 responses, resolved route errors, and requests with personal headers do not receive shared static cache headers.

  • Vary values must cover every request header that can change shared output. Missing variation can serve one user's representation to another.

  • Cache tags support targeted invalidation where the selected runtime exposes it. Attach tags to the page or documented directory layouts.

Understand static output without a revalidation interval

Both prerender = true and static: true in defineRoutePage enable static output without a refresh interval. In Core 1.0.4, eligible Node responses also use an unexpired in-process cache entry and send Cache-Control: public, max-age=31536000, immutable. Treat this as content that can remain cached for a year downstream.

  • Use numeric revalidate when the Node runtime must refresh changing content. A static host cannot execute revalidation; rebuild and redeploy its generated HTML.

  • Use prerender or helper static only when build-time output is the intended source and the deployed asset/cache layer has been verified.

  • Server cache invalidation does not clear a browser or CDN cache that has already stored an immutable response. Plan a host purge or a versioned URL when publishing changed content.

  • Restarting a Node process clears its default memory cache. A custom persistent cache needs its own deployment and invalidation policy.

Cache only shareable successful output

  • Do not statically cache a route whose output depends on a session, authorization decision, private cookie, or user-specific request header.

  • A cache tag identifies related entries; it does not make private output safe to share.

  • Return the correct non-200 status for failures and not-found output so they are not mistaken for a successful reusable document.

  • Keep cache keys and tags bounded. Never place secret values in either one because operational tooling may expose them.

  • When variation or privacy is uncertain, prefer uncached SSR and add caching only after request-level tests prove isolation.

Inspect generation and response headers

BASH
bashnpx tavo inspect route /catalog/starter --json
npx tavo build
PORT=4174 node .tavo/build/server/start.mjs
curl -i http://127.0.0.1:4174/catalog/starter
  • Confirm the inspected route has the expected static, revalidate, vary, and tag policy.

  • Confirm every generated dynamic parameter produces output at the intended path.

  • Assert Cache-Control and Vary for a successful anonymous request.

  • Repeat with the deployment's personal headers and failure cases and confirm shared cache headers are absent.

  • Exercise tag invalidation only through a runtime that explicitly exposes and documents it.

Look up exact public types

Follow linked API names to their canonical TypeScript declarations and package boundaries.