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
tsximport { createTavo, TavoController } from "@tavojs/core";
import { notFound } from "@tavojs/core/router";
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) notFound();
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 request | Where rendering happens | Cache behavior |
|---|---|---|
| CSR | Browser on each visit | Browser route cache and normal HTTP caching; no server HTML cache |
| SSR | Server for each request | None unless route or adapter enables it |
| SSG | Build process | Generated HTML and route data |
| ISR | Server on the first request after expiry | That request waits for fresh output; later requests reuse it |
| Cookie or Authorization request | Server | Shared 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. tavo inspect route expects the route pattern printed by tavo routes, such as /articles/:slug, rather than one concrete article URL. Use the JSON report and generated route manifest in CI when static output is an application requirement.
Run in Terminal
bashnpx tavo build --report-json
npx tavo inspect route /articles/:slug --json