Rendering follows the data
Declare ownership and freshness; let the compiler derive caching, streaming and islands.
On this page
Try a render plan
Change the declarations. This illustrates one query in a static shell; nested dependencies can make a region more dynamic.
Static HTML
Public, static data can be rendered ahead of time. A view without machine bindings ships no client application JavaScript.
Incremental static regeneration
Public data uses the declared revalidation interval. The cached region can be regenerated by a server; this is not a GitHub Pages-only deployment.
Stale while revalidate
Public cached data can be served while the server refreshes it. Cache policy follows the declaration rather than a route-level override.
Request-time region
Live freshness requires request-time data and the framework’s live transport. It cannot be exported as a static-only page.
Private, request-time region
User scope wins over every freshness choice. This data must never reach a shared cacheable region; it needs a server.
Hydration is a separate decision: only machine-bound nodes become islands. These controls are native HTML and use no JavaScript.
Begin with a data requirement
A product page and an account panel can share a screen while needing different treatment. The product description may be public and rarely change. The account panel belongs to the signed-in visitor. Choosing a single rendering label for the whole page hides that distinction. Hozu instead asks each query to declare who can see its result and how fresh that result must be.
The compiler follows those declarations through the recorded view tree. It derives a render plan for individual regions and a separate plan for hydration. The public authoring surface therefore describes data requirements, rather than asking you to maintain another collection of route-level caching switches. ADR 0006 establishes this boundary.
Public data can take several forms
This declaration describes a public catalogue that can be computed ahead of time. The output schema defines what the view may read, while the policy fields express facts about the data.
export const catalogue = query({
input: z.object({}),
output: z.array(Product),
scope: 'public',
freshness: 'static',
})
Public data with static freshness produces a static region. A freshness: { revalidate: 60 } policy instead declares a revalidation interval; freshness: { swr: 60 } declares stale-while-revalidate behaviour. Those intervals are examples of application policy, not recommendations or benchmark measurements. A server is needed to carry out regeneration or background refresh after deployment.
Live freshness makes a region request-time and uses the framework’s live-query transport. It cannot become an entirely static GitHub Pages deployment simply because the surrounding HTML is static. Use the explorer above to compare these cases, then inspect the actual project with hozu plan; the illustration deliberately omits nested dependencies.
User scope is a hard boundary
A user-scoped query belongs to the request’s session identity. Its data must never enter a shared cacheable region. Setting its freshness to static does not make the data public or permit a shared cache. Scope is an ownership constraint, not a hint for the compiler to weigh against performance.
export const myNotes = query({
input: z.object({}),
output: z.array(Note),
scope: 'user',
freshness: 'static',
})
The project declares the session schema, and the resolver receives the appropriate identity. Public resolvers do not receive that session. A public cacheable query whose input depends on user-scoped request data is also unsafe; the validator checks data flow rather than only reading the outermost query’s label.
A static shell can contain a request-specific region that is streamed separately. The shell remains shareable while the private region is produced for that request. Nested regions inherit the more dynamic requirements of their dependencies, so a collection of individually reasonable declarations can still produce a request-time plan.
HTML and hydration answer different questions
Rendering determines when HTML and data are produced. Hydration determines which browser-side nodes need interactive behaviour. A static query does not make a view interactive, and request-time content does not automatically mean that an entire page needs a client application.
Hozu derives islands from machine-bound nodes: events, state-dependent visibility and context-bound values require the client runtime. Unbound content remains HTML. Server-fetched data is serialized into the payload instead of being fetched again when an island starts. This separation lets a mostly static article contain a small interaction without declaring the whole document a client component. A page fetches the client runtime only if an island actually renders on it: an island inside a list, a branch or a query result is preloaded where it first appears, so a page whose list is empty ships no JavaScript (ADR 0036).
A page assertion such as assert: 'static' asks the validator to confirm the derived result. It cannot override an incompatible query or force private data into a cached page. Read the plan when the result surprises you; changing an assertion is not a substitute for understanding the dependency that caused it.
Two kinds of navigation
Soft navigation solves a state-preservation problem. If compatible pages share a route-independent view with an island, the compiler can derive that the view should survive navigation. Its DOM and machine can remain in place while other content changes. A view that reads route parameters or search data cannot be kept as though those inputs were unchanged. ADR 0015 details the conditions.
Cross-document view transitions solve a visual problem. Hozu 0.4.0 emits @view-transition { navigation: auto }, allowing supported browsers to transition between ordinary documents without adding a client router. Reduced-motion preferences remove the animation. This does not preserve an application machine merely because two headers look alike.
This website gives the header and documentation sidebar stable transition names so the reading frame can stay visually still. Its interactive explanations use native controls and CSS, so their existence does not require a Hozu island. The 0.4.0 changelog and ADR 0032 describe the framework change. For the deployment consequences, continue with the deployment guide.
Edit this page on GitHub