Guide 36 of 36
Verniz Sites production guide
Build, inspect, test and deploy complete static, hybrid and server products with TypeScript, packages, content and auditable graphs.
On this page
Verniz Sites turns one typed Zolo application into a portable static site, a hybrid product, or a standalone server. HTML is the default product. JavaScript is added only for islands, client entries, mounts, progressive navigation, offline support, or another capability the project explicitly enables.
The normal build does not require Node. TypeScript checking and emit use the
embedded typescript-rs engine; bundling, splitting and minification use
Rolldown/Oxc; npm and JSR packages are resolved by Aube behind Zolo's sealed
package adapter.
Start a complete project¶
[project]
entry = "src/main.zolo"
[web]
output = "static" # static | hybrid | server
out_dir = "dist"
public_dir = "public"
base = "/"
trailing_slash = "always"
minify = true
source_maps = "external"
[client]
target = "es2022"
typecheck = "strict"
split = trueRun the development server, build the production artifact, inspect why it was built, and preview exactly what will be deployed:
zolo dev
zolo build --web=static
zolo explain web /
zolo previewExpected result: zolo dev keeps the last good program and updates the open
page after a valid save; the build reports routes, HTML/CSS/JS/assets, budgets,
rendered versus reused routes and total duration; dist/_zolo/web-manifest.json
seals every published byte; preview validates that manifest before listening.
A byte-identical second build reports an unchanged product and does not replace
dist/.
Client JavaScript and TypeScript¶
External entries are relative to the .zolo file when they start with ./ or
../, and relative to the project root otherwise:
{client.entry("./client/about.ts")}
{client.entry("src/client/admin.ts")}Small progressive enhancements can stay beside their markup:
<button id="count">Add</button>
<script client lang="ts">
const button = document.querySelector<HTMLButtonElement>("#count")!
button.onclick = () => button.dataset.clicked = "true"
</script>Production always uses canonical TypeScript emit before the Rust bundler.
Development keeps a fast incremental Program and bundle per entry, watches type
imports too, and retains the previous client product after an error. Source
maps and editor diagnostics point to authored .ts or the exact inline body in
the .zolo file, never to a temporary projection.
The editor supports completion and auto-import, hover, definition, references,
rename, diagnostics and formatting for external files and inline virtual
documents. Oxlint and Oxfmt are separate opt-ins through zolo lint --client
and zolo fmt --client; they are not hidden build steps.
npm and JSR without an authored node_modules¶
zolo add npm:[email protected]
zolo add jsr:@luca/[email protected]
zolo install
zolo audit packages --deny warnings
zolo explain packagesExpected result: zolo.toml records exact direct declarations and zolo.lock
records exports, conditions, transitives, peers, integrity, license and
provenance. A subsequent build reconstructs the sealed VFS offline and frozen;
it neither installs nor runs lifecycle scripts. Lifecycle remains denied unless
an exact package coordinate is approved. Audit and explain read the sealed graph
without network access.
The TypeScript service consumes the same editor descriptor as the build, so
package imports receive real types, public-subpath completion, hover and
definition without creating node_modules in the project. Missing declaration,
stale lock, private subpath and unavailable VFS produce different diagnostics;
quick fixes prepare a command but never install implicitly.
Typed client/server boundary¶
verniz:routes, verniz:actions, verniz:env, verniz:assets and
verniz:client are generated typed modules. client.mount receives Wire-safe
props, an abort signal and deterministic cleanup. Actions expose typed codecs,
cancellation, revalidation and separate domain, HTTP, offline and protocol
errors. Any, closures, handles, filesystem resources and secret data cannot
cross this boundary silently.
Content, assets and cost¶
Typed Markdown/JSON/TOML/YAML collections can generate route entries, safe HTML, feeds, sitemap and SEO metadata. Route-local provenance makes an edit to one document rebuild its page and only the shared indexes that depend on it. The scale gate covers 2,048 content pages.
public/ preserves stable authored URLs. assets.file emits content-addressed
assets; images can receive verified dimensions, responsive AVIF/WebP candidates
and zero-JavaScript placeholders; assets.font can produce an audited WOFF2
subset or fall back safely. CSS chunks, preload, SRI and route budgets are all
derived from real graph edges.
Optional product layers¶
[web.navigation]adds link-graph-derived prefetch, a byte-limited cache and optional View Transitions. Every failure falls back to native navigation.[web.pwa]adds install metadata without JavaScript.[web.pwa.offline]is a separate network-first cache with explicit routes and a mandatory byte budget.[web.edge]emits a provider-neutral, import-free Wasm adapter for the sealed static response graph. Dynamic edge execution is not claimed.[tasks.<name>]and[workspace.dependencies]define a deterministic task DAG. Web builds delegate incremental work to WebGraph; generic cached tasks require exact inputs and outputs. Vite, Vitest and tsdown remain explicit external escape hatches.
Removing any opt-in removes its runtime and artifacts on the next atomic build.
The release candidate is measured as a complete command, not a compiler microbenchmark. The current same-host comparison, its variance and the historic P10b optimization checkpoint are recorded in the Verniz release benchmark.
Runnable verification matrix¶
Run commands from the repository root unless a row says otherwise.
| Capability | Example and command | Expected proof |
|---|---|---|
| Island SSR contract | zolo run examples/features/36-web/04-island-counter.zolo --no-cache, then build it with --emit native and --emit llvm |
Two independent island states, compiled handlers and reactive regions; VM, Cranelift and LLVM output is byte-equivalent. |
| Partial form target | zolo check examples/features/36-web/05-island-form.zolo |
Current bind:value and @action surfaces typecheck together; the file explicitly labels reactive-list reconciliation and refinement types as future language work. |
| Zero-JS static | zolo build --web=static examples/features/36-web/19-static-site-build.zolo twice |
Two pages + 404 + manifest; second build unchanged/reused; JavaScript is 0 B. |
| Dynamic entries | zolo build --web=static examples/features/36-web/20-static-dynamic-routes.zolo |
One concrete page per enumerated slug; invalid or duplicate params fail before publish. |
| Asset graph | zolo dev examples/features/36-web/21-authored-assets.zolo, then static build |
Asset update reaches the browser in dev; production URL is hashed, dimensioned and manifest-owned. |
| Mounts/actions/Wire | zolo build --web=static examples/features/36-web/22-client-mount-actions.zolo |
Typed mount chunk and action bridge are retained only by the owning route. |
| Content/SEO | from examples/features/36-web/23-content-site, run zolo build --web=static twice |
Pages, feed and sitemap are deterministic; second build reuses every route. |
| Hybrid runtime | from examples/features/36-web/24-hybrid-product, run zolo build --web=hybrid then zolo preview |
Static page plus sealed standalone runtime for server route/action/form. |
| Navigation | from examples/features/36-web/25-navigation-site, run static build and preview |
Normal links remain valid; opt-in runtime and targets are visible in explain. |
| Inline TypeScript | from examples/features/36-web/26-inline-typescript, run static build twice and zolo dev |
Canonical TS chunk/maps, incremental reuse and HMR from .zolo or imported .ts. |
| PWA/offline/edge | from examples/features/36-web/27-pwa-edge, run static build, explain and preview |
Manifest, budgeted cache and import-free edge adapter validate; repeated build is byte-identical. |
| Workspace tasks | from examples/features/36-web/28-task-workspace, run zolo task build --all --graph, then twice normally |
Four nodes/three edges; web routes are reused; repeated generator reports a cache hit. |
Final product QA¶
A production build is not complete merely because rendering succeeds. For the
canonical documentation dogfood, run the authored product auditor from
apps/zolo-lang-verniz:
npm run test:qa
node tools/check-seo-product.mjs
pwsh -File tools/check-search-product.ps1The first command walks every generated HTML document and the fallback. It checks internal links, heading and skip-target structure, accessible dialog names, inherited locale declarations, localized Portuguese surfaces and likely UTF-8 mojibake. The SEO and search commands independently cross-check the Route Graph, sitemap, alternates, redirects and localized index.
Complete the gate in a real browser at desktop and 390 px: keyboard-open and close the command palette, exercise the mobile menu, run both Playground and a Tutorial lesson, visit a long document and the 404 fallback, and confirm there is no horizontal overflow or console error. Record Lighthouse and a performance trace as evidence. The 2026-08-24 baseline is 100/100/100/100 for accessibility, best practices, SEO and agentic browsing on desktop and mobile, with 87 ms LCP, 1 ms TTFB and 0.00 CLS on the local static preview.
Honest limits¶
- Static export rejects request-dependent routes unless they are explicitly placed in a hybrid/server product. There is no silent static-to-server fallback.
- Author-authored package ranges/tags are intentionally unavailable; direct
dependencies use exact versions. Microsoft
tsgostill lacks the upstream static package-mapping API, so the native TypeScript path istypescript-rs. - Offline HTML is returned only after a network failure, never for an HTTP error or a query-bearing navigation. Dynamic code does not yet execute in the edge Wasm adapter.
- Reactive list reconciliation and refinement-type forms remain separate language work. The Wire boundary, leak checks, mounts and actions themselves are implemented.
The normative architecture and phase-by-phase evidence are in
specs/verniz-sites.html.
DOCS / FEEDBACK
Did this page leave a question?
Tell us where the explanation lost you. Documentation is part of the language experience.