Navigated to /docs/getting-started/caching

Caching

Prebuild stable routes and cache safe SSR responses without crossing user or request boundaries.

Mark routes that can be static

Export prerender when a route can safely reuse its rendered output. For a dynamic path, generateStaticParams lists the concrete URLs the production build should prerender.

Static generation renders the initial createTavo view during the build; the browser can then hydrate it and let its controller own later interactions. In this example, every visitor receives the same safe article HTML while save state remains local to the hydrated component.

Create src/pages/articles/[slug].tsx

TSX
tsximport { createTavo, TavoController } from "@tavojs/core";
import type { PageLoadContext, PageProps } from "@tavojs/core/router";
import { Button, Page, Stack, Text } from "@tavojs/ui";

type Article = { title: string };
type ArticleParams = { slug: string };
type ArticlePageState = { saved: boolean };

const articles: Record<string, Article> = {
  "getting-started": { title: "Getting started" },
  "release-notes": { title: "Release notes" },
};

function getArticle(slug: string): Article {
  const article = articles[slug];
  if (!article) throw new Error(`Unknown article: ${slug}`);
  return article;
}

export const prerender = true;

export function generateStaticParams(): ArticleParams[] {
  return [{ slug: "getting-started" }, { slug: "release-notes" }];
}

export function load({ params }: PageLoadContext): Article {
  return getArticle(params.slug);
}

class ArticlePageController extends TavoController {
  toggleSaved() {
    this.model.patch((state) => ({ saved: !state.saved }));
  }
}

export default createTavo<
  PageProps<Article, ArticleParams>,
  ArticlePageState,
  ArticlePageController
>({
  model: () => ({ saved: false }),
  controller: ArticlePageController,
  view: ({ props, state, controller }) => (
    <Page>
      <Stack gap="md">
        <Text as="h1" variant="h1">
          {props.data?.title}
        </Text>
        <Button
          variant="outline"
          aria-pressed={state.saved ? "true" : "false"}
          onClick={() => controller?.toggleSaved()}
        >
          {state.saved ? "Saved for later" : "Save article"}
        </Button>
      </Stack>
    </Page>
  ),
});

Cache only shareable output

The route cache stores rendered HTML and resolved route data. Requests carrying Cookie or Authorization bypass static caching because their output may be user-specific.

  • Do not mark personalized pages static.

  • Use vary only for a small, deliberate set of request headers that genuinely change safe output.

  • Keep request users, sessions, tenants, and permissions out of global stores and module variables.

Separate rendering from caching

Mode or requestWhere rendering happensCache behavior
CSRBrowser on each visitNormal HTTP asset caching only
SSRServer for each requestNone unless route or adapter enables it
SSGBuild processGenerated HTML and route data
ISRServer after expiryServe cached output, then replace it
Cookie or Authorization requestServerShared static cache is bypassed

Choose a production cache adapter

The Node runtime uses a bounded process-local memory cache by default. It is useful for one process, but it is not shared across replicas and disappears when the process restarts. Supply a cache adapter when the application needs shared persistence or coordinated invalidation.

Inspect what the build decided

tavo build prints each route render mode and static policy. Use the JSON report and generated route manifest in CI when static output is an application requirement.

Run in Terminal

BASH
bashtavo build --report-json
tavo inspect route /articles/getting-started --json

Checkpoint

Next steps