Guide 28 of 36
Web Development with Zolo
Build full-stack applications with std::http, std::web and Verniz, from a rendered View to a production binary.
On this page
Zolo's web stack is built from ordinary language features instead of a separate template language. Verniz is the rendering and interactivity layer: components are functions, markup lowers to normal Zolo calls, and the server sends correct HTML before JavaScript starts.
The stack¶
| Layer | Use it for | Current support |
|---|---|---|
std::html / Verniz |
View, markup, escaping, islands, actions and scoped CSS |
VM, LLVM and Cranelift |
std::http |
Routes, requests, responses, middleware and the native HTTP server | VM, LLVM and Cranelift |
std::web |
Request and Cookies effects with secure cookie envelopes | VM only for now |
zolo dev |
In-place server swaps, browser revalidation and the error overlay | Development VM |
zolo build |
Standalone production binaries | LLVM or Cranelift |
zolo build --web=static |
Portable static-site directory | VM prerender |
The VM-only boundary applies to the std::web effect/cookie layer, not to Verniz Views, islands, actions or scoped CSS.
A complete first page¶
use std::http
use std::html::{document, head, meta, title, body, main, h1, p, View}
fn page(_req) -> View {
<document lang="en">
<head>
<meta charset="utf-8"/>
<title>Hello from Zolo</title>
</head>
<body>
<main>
<h1>Hello from Verniz</h1>
<p>This HTML was rendered by Zolo.</p>
</main>
</body>
</document>
}
let app = http.router() |> http.get("/", page)
http.serve(3000, app)Run it with live web development:
zolo dev src/main.zoloWhen http.serve is reached, zolo dev enters web mode automatically. Open the URL printed by the CLI. Development HTML is instrumented with the reload client even when the page is a pure zero-JavaScript View, so saving an entry file or imported module updates the open tab without adding client_script(). Production products make a narrower decision: only a rendered page that contains an interactive island receives the Verniz runtime automatically; a pure View retains its authored HTML and zero-JS result. Use zolo run for a one-shot VM run, zolo build for a production binary, or the static product below.
Export a static site¶
The same fixed GET routes can become a portable directory without opening a socket:
zolo build --web=static src/main.zoloThe default output maps / to dist/index.html, /about/ to dist/about/index.html, emits a deployable dist/404.html, and writes the versioned Web/Asset Graph to dist/_zolo/web-manifest.json. The output is staged and validated before dist/ is replaced, so a failed build leaves the previous site intact.
Client assets use paths relative to each generated HTML file. That means the same directory works when dist/ is the host root, when a generic Live Server exposes it at /dist/, or when index.html is opened directly. Nested pages automatically receive the required ../ segments; there is still only one shared _zolo/client.js.
Static builds also extract Verniz' generated <style data-zolo-css> from each document. Byte-identical stylesheets are emitted once as _zolo/css/app.<hash>.css, and each page receives a route-relative <link rel="stylesheet" ... data-zolo-css>. The chunk is BLAKE3-addressed, participates in the Asset Graph and remains zero-JavaScript. Development and normal servers keep the stylesheet inline; extraction is a production optimization and does not alter the edit loop.
Production HTML derives its loading hints from the same dependency edges. A critical stylesheet receives preload as="style"; the current classic Verniz runtime receives preload as="script"; a future type="module" entry automatically becomes modulepreload. A content-addressed WOFF2/WOFF/TTF/OTF referenced by reachable generated CSS receives preload as="font" with its MIME, SRI and the required crossorigin="anonymous". Hints are emitted once, CSS before JavaScript and fonts, and retain paths relative to the owning HTML file. Verniz does not eagerly preload images or unrelated transitive imports, avoiding bandwidth competition that the graph cannot justify.
Every manifest output receives SHA-384 Subresource Integrity metadata. For CSS and JavaScript, the same digest is attached to the consuming <link> or <script> and its preload hint. An authored matching hint is secured and reused instead of duplicated; conflicting authored integrity aborts before the atomic swap. Same-product resources do not receive a forced crossorigin, preserving their relative host, Live Server and direct-file URL contract.
For a web-first project, put the product choice in zolo.toml and run plain zolo build:
[project]
entry = "src/main.zolo"
[web]
output = "static"
out_dir = "dist"
public_dir = "public"
base = "/"
trailing_slash = "always" # or "never"
minify = true
source_maps = true
[web.tailwind]
input = "src/tailwind.css"
scan = ["src", "content"]
[client]
target = "es2022"
typecheck = "strict" # or "relaxed"
split = true
# tsconfig = "tsconfig.client.json"
[client.lint]
engine = "oxlint" # opt-in; default is "off"
deny_warnings = false
[client.lint.rules]
no-console = "warn"
no-debugger = "error"
[client.format]
engine = "oxfmt" # opt-in; default is "off"
indent_style = "space"
indent_width = 2
line_width = 100
quotes = "double"
semicolons = "always"
trailing_commas = "all"
[web.images]
optimize = true
# defaults: formats = ["avif", "webp"]
# defaults: widths = [320, 640, 960, 1280, 1600, 1920]
# avif_quality = 80
# avif_speed = 8
# placeholder = "none" # none | color | blur
# placeholder_size = 16 # longest edge for blur, 1..=64
[web.budgets]
html = "64 KiB"
route = "256 KiB"
css = "48 KiB"
javascript = 0
asset = "1 MiB"
total = "8 MiB"When [web].output is configured, zolo dev enforces the same canonical URL
shape as the static product. Fixed and dynamic GET routes use the configured
trailing_slash policy; the one alternate spelling redirects with HTTP 308 and
keeps its query string. Root, dotted resource paths and catch-all routes are not
rewritten, and non-GET action/API endpoints keep their authored path. Projects
without [web].output preserve their historical live-routing behavior.
Tailwind v4 is an optional native Verniz build stage, not a Vite integration.
zolo install seals the official standalone compiler in zolo.lock; static
and development builds then run it frozen/offline under Rust orchestration.
The generated stylesheet is inserted after head metadata and <base>, but
before the first authored stylesheet or <style> block, so product CSS keeps
cascade authority. A migrated product that already owns its reset can omit
Preflight explicitly while retaining the theme and utilities layers:
@layer theme, base, components, utilities;
@import "tailwindcss/theme.css" layer(theme);
@import "tailwindcss/utilities.css" layer(utilities) source(none);
@source "./**/*.zolo";
@source "../content/**/*.{md,json}";Static output uses a content-addressed CSS filename, SRI and preload metadata.
Development serves the stable /_zolo/css/tailwind.css route, recompiles on
changes covered by scan, and keeps the last valid stylesheet after an error.
Use tw"..." when a string is a static list of Tailwind candidates. It is a
native str, so it can be returned from helpers, passed through cn, stored in
constants, or used directly in class. The triple form keeps long variant
tables readable:
const BUTTON_BASE = tw"inline-flex items-center rounded-md"
fn button_variant(outline: bool) -> str {
if outline {
return tw"border bg-background hover:bg-muted"
}
tw"bg-primary text-primary-foreground hover:bg-primary/80"
}
let panel = tw"""
grid gap-4
md:grid-cols-2
dark:bg-slate-950
"""Tailwind literals are static by design: interpolation is rejected with TE148
because a generated fragment cannot be discovered reliably by Tailwind. Choose
between complete literals with normal Zolo control flow instead. Verniz passes
every reachable .zolo source in the compiled module graph to the official
Tailwind scanner, including package sources outside the application's src
directory. This makes helper modules and component packages work without a
manual @source entry while avoiding unrelated files on disk.
Scoped component styles can use Tailwind transforms directly. Verniz scopes
the selector first, then compiles @apply and @variant against the configured
Tailwind entry through an implicit @reference; no lang attribute or manual
reference is required, and Preflight is not duplicated:
<div id="box">
content
<style>
#box {
@apply bg-red-500 rounded-xl;
width: 100px;
}
</style>
</div>The resulting rule retains the component's zc-* scope in development and
static output. Using either directive without [web.tailwind] is an explicit
build/development error instead of CSS that the browser silently ignores.
[client] is a top-level sibling of [web], not [web.client]. Production
minify and public source_maps belong to [web]; TypeScript typecheck is
the string policy "strict" or "relaxed". Client package declarations live
under [client.dependencies] and [client.dev-dependencies].
Inline TypeScript¶
Use a client TypeScript script when a small progressive enhancement reads most naturally beside its markup:
<button id="calculate">Calculate</button>
<script client lang="ts">
import { add } from "./client/math.ts"
const button = document.querySelector<HTMLButtonElement>("#calculate")!
button.onclick = () => {
document.body.dataset.answer = String(add(20, 22))
}
</script>Only the exact paired form script client lang="ts" (or
lang="typescript"; attributes may be reversed and client=true is accepted)
is bundled. An ordinary <script> remains verbatim raw HTML. Relative imports
start beside the containing .zolo file, so the example resolves
./client/math.ts exactly like a neighboring external entry. Bare npm/JSR
imports use the same sealed package graph, strict TypeScript options, Verniz
virtual modules and browser resolver as client.entry.
The body is compiled only for the client: it does not execute while the View is
rendered. Static output contains one content-addressed module and never leaks
the source through HTML attributes. zolo dev builds it lazily, watches the
containing .zolo plus imported modules and keeps the last-good client product.
Diagnostics and source maps point back to the original .zolo lines; the
temporary projection under target/.zolo is never a public source identity.
The VS Code extension exposes TypeScript semantic tokens, completion, hover,
definition, references, rename, diagnostics and Format Selection inside the
body. Edits are confined to that body: rename and formatting cannot touch the
opening tag, closing tag or surrounding Zolo markup. The complete runnable
example is examples/features/36-web/26-inline-typescript.
Client quality is intentionally separate from the product pipeline. Run
zolo lint --client for embedded Oxlint and zolo fmt --client for embedded
Oxfmt. With no path, both commands discover authored JS/JSX/TS/TSX inside the
project while excluding generated output, package VFS directories and
symlinks. zolo lint --client --fix applies safe fixes only; suggestions and
dangerous alternatives remain editor code actions. zolo fmt --client --check
never writes, and a parse failure in any input aborts before the first file is
changed. These tools are opt-in, require neither Node nor node_modules, and
are not run by zolo build.
The VS Code extension reads the same sections. Oxlint diagnostics work for
unsaved external JS/TS buffers, including authored spans and quick fixes;
Oxfmt is available as a document formatter. During zolo dev, client lint is
advisory: even an error rule does not block HMR or replace the last-good
bundle. Inline blocks use the TypeScript virtual-document bridge described
above; external-file lint/format policy remains independently opt-in.
base accepts normalized mount points such as /docs/. With trailing_slash = "always", /about/ is emitted as about/index.html; with "never", /about becomes about.html. A fixed GET /404 route customizes the fallback and may return either a normal page or http.not_found(page). Without one, Verniz emits a small zero-JavaScript fallback.
Import an authored file through the Asset Graph when its URL may be immutable and content-addressed:
use std::assets
let logo = assets.file("./images/logo.svg")
fn brand() -> View {
<img src={logo} alt="Verniz" />
}The path is relative to the project root, not to the current module. assets.file is pure and filesystem-free: VM, native and LLVM render the same opaque logical URL. During zolo dev, Verniz resolves that URL safely, serves the original bytes and watches the source lazily. During a static build, it reads the file once, computes its BLAKE3 hash, deduplicates identical content with the same extension, emits _zolo/assets/<stem>.<hash>.ext, and rewrites references in generated HTML and CSS relative to each output file. Nested pages, base, Live Server and direct file opening therefore need no authored path adjustment. An imported asset referenced only by unreachable component CSS is removed from the final product too. Font extensions receive stable web MIME types; a font carried through a Verniz CSS interpolation is attributed to that CSS chunk as well as its owning page, so preload and route cost follow the actual rendered edge.
Use assets.font when production should emit a verified subset rather than only import the whole font:
fn typography() -> View {
<>
{assets.font(
"./fonts/inter.ttf",
"Inter",
"Fast, readable Verniz.",
extra_characters: "0123456789!?",
weight: 400,
display: "swap",
)}
<style global>body { font-family: Inter, sans-serif }</style>
</>
}characters is the explicit production coverage and extra_characters covers text that appears only in islands, errors or client-created state. Verniz always adds a space and deduplicates codepoints; it deliberately does not infer glyphs from rendered HTML. Dev, run, native and LLVM keep using the original font. A static build inspects its OpenType embedding permissions and character map, then emits content-addressed CSS and a smaller WOFF2 only when the requested coverage is present and the result revalidates. Restricted, preview/print-only and bitmap-only embedding fail before publication. A font that permits embedding but forbids subsetting falls back to the original. Advanced layout tables that the current subsetter would drop also trigger a safe original fallback unless that declaration explicitly sets allow_layout_loss: true. A failed verification or a result that is not smaller never replaces the original silently.
Each face is recorded in WebManifest v7 with source and output hashes, CSS/font routes, Unicode ranges and coverage hash, embedding/subsetting permission, fallback reason, potentially dropped tables, byte savings and consuming HTML files. The route graph is HTML → generated font CSS → font, so preload, SRI and budgets follow the real edge. zolo preview reopens the deployed font and verifies coverage, permissions, CSS and graph consistency before listening.
For local <img> consumers, the static build also reads intrinsic dimensions from PNG, JPEG, GIF, WebP, BMP, ICO, AVIF and SVG, then writes missing width/height attributes into the emitted HTML. One authored dimension preserves the image ratio by inferring the other; two authored dimensions are kept exactly. An SVG without intrinsic size or viewBox must author both dimensions because Verniz will not guess its aspect ratio. This is build-time metadata only and adds no JavaScript.
Set [web.images] optimize = true to turn each used PNG, JPEG or WebP into responsive AVIF and lossless WebP alternatives. Verniz wraps eligible local images in <picture>, emits content-addressed candidates under _zolo/images/, and keeps the original <img> as the universal fallback. Configured widths are capped by the source and by twice the rendered width; the useful boundary is inserted automatically, so a 400 px image does not generate candidates beyond 800 px merely because the source is larger. The generated sizes default is (max-width: <rendered-width>px) 100vw, <rendered-width>px.
Authoring sizes customizes that policy. An authored srcset, an <img> already inside <picture>, or data-zolo-no-optimize keeps the authored markup in control for that use. The typed HTML surface accepts srcset, sizes, decoding, fetchpriority, <picture> and responsive <source> attributes. SVG/GIF/AVIF, animated PNG/WebP and raster sources whose orientation metadata would change their geometry still receive intrinsic metadata but are not rasterized by this first encoder slice.
Set placeholder = "color" for an alpha-weighted source color, or placeholder = "blur" for a tiny inline PNG generated at placeholder_size. Both modes are zero-JavaScript and zero-request: Verniz writes the placeholder as the image background until the real pixels paint over it, and records its dimensions, byte count and BLAKE3 hash in the manifest. An authored inline style or data-zolo-no-placeholder leaves that image untouched. Animation, orientation transforms and fully transparent sources are preserved rather than guessed. Because the inline bytes are part of the HTML, normal HTML/route budgets already include their real cost.
Imported sources cannot be absolute, contain .., traverse symlinks or read from the output directory. The manifest records them as kind = "imported", including every deduplicated source in sources; every generated route lists the deployed asset URLs it depends on. Use assets.file for images, fonts and other build-owned files whose names should change with their bytes.
Files under public/ are copied recursively with their authored names. This is the escape hatch for robots.txt, favicons, fonts and files whose URL must remain stable; for example, public/images/logo.svg is emitted as dist/images/logo.svg. Set public_dir to another project-relative directory when needed. public_dir and out_dir cannot overlap, symlinks and non-portable names are rejected, and public files cannot use the reserved _zolo/ namespace or replace a generated page.
zolo dev serves both imported files and the configured public tree as binary bytes. Saving one emits a dedicated asset event; the browser cache-busts matching stylesheet/media elements, inline style attributes and <style> blocks in place, without recompiling the VM, fetching the page or resetting island state. Internal /_zolo/ routes always remain owned by Verniz.
zolo run, zolo <file.zolo> and compiled native/LLVM servers also serve the public tree, but as an immutable startup snapshot: binary bytes, MIME and HEAD semantics are preserved without watcher, SSE or HMR instrumentation. The VM runner resolves [web].public_dir from the entry's project even when the entry path is absolute. A compiled executable looks first at ZOLO_PUBLIC_DIR, then for public/ beside the executable and finally under its launch directory. Restart the normal server after changing a public file; use zolo dev when changes must reach an open tab automatically.
Static production CSS follows the rendered route subgraph. A scoped component sheet is linked only by pages whose HTML contains that component's exact zc-* scope class; a page with no used sheet receives no CSS link. <style global> deliberately remains global and is linked by every generated HTML page. Verniz combines only adjacent sheets with the same consumer set, preserving registration and cascade order, then writes content-addressed _zolo/css/app.<hash>.css chunks. This analysis is static-build-only: dev, direct execution and native/LLVM servers retain the inline stylesheet contract.
[web.budgets] makes production cost enforceable in CI. Each value accepts integer bytes or an exact human-readable size using B, KB, KiB, MB, MiB, GB or GiB. html limits one generated page; route limits that page plus its transitive manifest dependencies, counted once; css and javascript limit the reachable bytes of those kinds; asset limits any single non-page output, including the Verniz runtime and generated chunks; total limits the validated payload excluding the manifest itself. Responsive candidates are an exclusive browser choice: the route budget uses the largest possible candidate once per distinct source/sizes policy instead of adding every srcset member, and deduplicates repeated identical policies. If the same source also has an authored non-responsive use, fallback and modern candidate may both contribute. A value of 0 is valid, so javascript = 0 is a direct zero-JS assertion.
The report always shows HTML, CSS, JavaScript and conservative transfer bytes for each route. It also reports how many image assets expose intrinsic dimensions, how many local <img> uses were dimensioned, how many became responsive, how many placeholders/candidates were emitted, how many font preloads are justified by the graph, and how many declared faces became subsets or safe fallbacks. When limits fail, Verniz reports every violation together, includes the largest contributors and does not replace the previous output. Route costs are exact for graph-owned dependencies such as generated CSS, assets.file, assets.font, local <img> references into public/, and the client runtime; responsive choices use the largest valid branch. Other stable literal URLs into public/ remain covered by asset and total; associating arbitrary authored links with individual routes belongs to the upcoming Link Graph slice.
The current WebManifest v16 records every public, imported and generated asset's deployed URL (including base), source provenance, kind, MIME type, byte size, BLAKE3 content hash, SHA-384 integrity and dependencies. Image assets with intrinsic dimensions add image = { format, width, height }; generated candidates add variant = { source_route, format, width, height, quality | lossless }; the top-level fonts collection records each declared face and its audited decision. Generated routes and the 404 receive hashes and integrity too. Each HTML route records its exact style/script/font preloads and every local images consumer—fallback target, rendered src, dimensions, placeholder, sizes, srcset and every candidate's route/bytes—so preview validation proves that manifest metadata, consumer tags, source-derived values and emitted bytes agree. A styled page depends only on its route-reachable CSS chunks, each CSS chunk records every consuming page and can depend on imported assets, an explicit font face forms a route → CSS → font edge, and an island page depends on the shared Verniz client runtime. Removing an asset and rebuilding removes it from the product because the entire validated directory is replaced atomically.
Install, offline, and edge are separate opt-ins¶
[web.pwa] generates a standards Web App Manifest and injects its link into static HTML. It adds zero JavaScript by itself. Launcher icons use stable URLs from public/ and must agree with the sealed Asset Graph MIME type.
[web.pwa.offline] separately generates registration plus sw.js. Its routes list may contain only static pages, and max_bytes is mandatory. The resulting cache contains those documents plus their transitive sealed assets—nothing discovered at runtime. Documents remain network-first; cached HTML is returned only after a real network failure, never in place of an HTTP error, and query-bearing navigation cannot reuse a cached page. An optional fallback must itself be an explicit cached route.
[web.edge] separately emits _zolo/edge/adapter.wasm, host.mjs, and adapter.json. The WebAssembly module imports nothing and embeds the exact static response graph; max_module makes its cost enforceable. runtime = "reject" proves that the edge product has no origin dependency. runtime = "origin" exposes the runtime pattern graph as an explicit forwarding boundary, without choosing a provider or origin binding. Static/MPA remains the base product: removing either table removes its artifacts on the next atomic build.
The build report shows offline cache entries/bytes/budget and edge responses/module bytes/budget. zolo explain web /route --json says whether that route is cached and embedded, with the selected runtime boundary. See examples/features/36-web/27-pwa-edge.
zolo explain web <route> keeps repeated inputs, dependencies, contributors and
links to 20 items per collection by default. Every truncated section reports its
full, matched, shown and omitted cardinality. Use --limit <count> to choose a
different bound, --filter <text> to narrow those collections, or --verbose
when the complete graph is intentionally required. The same contract applies to
--json: zolo.web-explain v2 represents each repeated collection as
{ total, matched, shown, omitted, items } and records the active selection, so
automation never mistakes a bounded result for the whole graph.
Publishing is content-aware. After staging and validation, Verniz compares the
complete emitted tree with the current output. If every path and byte is already
identical, the build reports already up to date; unchanged, removes only its
staging directory and does not rename dist/. This makes consecutive
deterministic builds safe even when a Windows browser, indexer or watcher has an
output file open without delete sharing.
A changed product still prefers the atomic directory swap. When Windows keeps
the output root open, Verniz prepares a durable rollback journal before the
first file changes, installs content-addressed assets and HTML first, and writes
_zolo/web-manifest.json last as the commit marker. The incremental cache uses
the same protocol with state.json. If the process is interrupted before that
marker, the next build restores the previous generation marker-last; if it was
interrupted after the marker, the next build keeps the committed generation and
only reclaims its journal. Internal staging and pending-cache directories carry
their owner PID: a later build removes state from dead processes without
touching a concurrent live build.
Preview the exact validated artifact—without recompiling or enabling development behavior—with:
zolo preview
# or: zolo preview public --port 4173 --openThe preview server honors base, redirects to canonical trailing-slash URLs, serves only manifest-declared routes and assets with their recorded MIME types, verifies BLAKE3, SHA-384, intrinsic image metadata, generated-image provenance, local <img> dimensions, source-derived placeholders, <picture>/srcset candidates, explicit font coverage/permissions/CSS and every HTML style/script/font preload edge before listening, answers unknown paths with the built 404.html, and rejects unsafe encoded path separators.
Dynamic named parameters are exported with explicit entries. Wrap the router in web.app and register one provider per dynamic pattern:
use std::web
fn post_entries() -> [{str: str}] {
return [
#{ "slug": "hello" },
#{ "slug": "verniz-sites" },
]
}
let router = http.router() |> http.get("/posts/:slug", post)
let app = web.app(router).entries("/posts/:slug", post_entries)
http.serve(3000, app)The live server still receives the original router. During a static build, each map produces a concrete page and the handler receives the concrete req.path plus the enumerated req.params. An entry map must contain exactly the named parameters in the route; missing, extra, non-string, unsafe, or duplicate values fail before the previous output is replaced. :slug and {slug} are supported; because ordinary Zolo strings interpolate braces, author the latter as "/posts/\{slug\}". Catch-all patterns remain a later Route Graph slice.
Dynamic routes without entries fail rather than silently emitting an incomplete site. Fixed JSON API routes also fail instead of being mislabeled as .html. Reading request query, headers, or body fails during prerender rather than baking personal data into HTML.
Pure Views remain exactly zero-JavaScript. A rendered @island links the embedded Verniz runtime automatically, copies it to dist/_zolo/client.js, and writes a route-relative URL that naturally stays under the configured public base. An explicit client_script() remains compatible and is never duplicated.
Choose the right entry point¶
- Return a
Viewwhen building pages or HTML fragments. - Return a map for JSON APIs and a string for plain text.
- Use
std::httpwhen handlers explicitly receive the request. - Use
std::webwhen nested code needs request or cookie access through effects. - Mark only interactive regions with
@island; the rest stays server-rendered HTML. - Mark server-callable functions with
@action; public functions are never exposed implicitly.
Try it in the browser¶
The Verniz Lab runs the Zolo compiler and VM through WebAssembly and renders the resulting document in an isolated preview. It supports Views, markup, scoped CSS and client-side island signals without installing anything.
The Lab does not emulate a listening TCP server. Routes, @action, cookies, databases and the zolo dev reload channel require the local CLI.
What is implemented today¶
Verniz currently includes server-rendered Views, HTML-like markup, escaped interpolation, raw style and script bodies, scoped CSS, @island, writable and derived signals, effects, DOM events, reactive attributes, bind:prop, @action, route revalidation, state-preserving development swaps, automatic per-page client runtime in VM/native/LLVM/static, static/hybrid/server products, navigation and TypeScript opt-ins, the validated Route/Asset/WebGraph, package/content/SEO pipelines, payload budgets and explainability, install metadata that remains zero-JS, an auditable network-first offline cache, and a provider-neutral import-free edge/WASM adapter. WebManifest v16 and Deploy Manifest v2 seal those capabilities while keeping every advanced layer removable from the static/MPA base.
Wire schemas, generated codecs, typed virtual modules, mount/action contracts and secret leak checking are implemented. Reactive list reconciliation and refinement-type forms remain separate language work. The complete production matrix is in Verniz Sites production guide.
Next: Verniz Views and Markup.
DOCS / FEEDBACK
Did this page leave a question?
Tell us where the explanation lost you. Documentation is part of the language experience.