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 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 request | Where rendering happens | Cache behavior |
|---|---|---|
| CSR | Browser on each visit | Normal HTTP asset caching only |
| SSR | Server for each request | None unless route or adapter enables it |
| SSG | Build process | Generated HTML and route data |
| ISR | Server after expiry | Serve cached output, then replace 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. Use the JSON report and generated route manifest in CI when static output is an application requirement.
Run in Terminal
bashtavo build --report-json
tavo inspect route /articles/getting-started --json