zigapagos
Rich interfaces.
Simple output.
Build with HTML, CSS, and TSX. Zigapagos turns pages, interactive components, and application routes into static files — fast. Deliver the interaction you need, with control over the JavaScript you ship.
Build a site or app See what ships
That button is a real island on this page. It was server-rendered into the HTML you received, then hydrated when it scrolled into view. View source — there is no framework runtime wrapping the rest of the page.
Zero JS by default
A page with no islands ships no framework JavaScript.
Two honest exceptions, both of which devtools will show you. This page is a demo, so it ships the shared runtime plus the islands you can click. And every page — including the docs — carries about 1 KB of inline script for the theme toggle (closer to 550 bytes over the wire once gzipped), which is exactly why the toggle is inline script and not an island: making it a component would have put a framework runtime on every page of a generator whose pitch is that most pages need none. Open the network tab on any docs page and that is all you will find.
One binary and Bun
From source to production files, fast
content/*.smd ──► native core ──┐
(SuperMD/SuperHTML) │
├──► static site
components/*.island.tsx ──► Bun SSR ───┤ + import map
└──► bundle ───┘ + one runtime
The native core generates your content and templates. Bun renders and bundles TSX at build time. You get a production output directory without maintaining a separate JavaScript bundler configuration. Building sites needs no Zig compiler; the shell installer supplies the toolchain.
Production build speed is a product priority. Measure your own project: content, images, type checking, and component code all affect the work. The backlog requires reproducible evidence for performance improvements.
Static files to deploy
Build once. Serve the output.
Your release is HTML, CSS, JavaScript where needed, and assets. Upload it to a compatible static host. The frontend needs no production Node or Bun process, and your source toolchain stays on the build machine.
For SPAs, configure the host's fallback routes. Zigapagos generates routing manifests for ZigBase, Nginx, and Apache, plus CSP and caching configuration. Apply the configuration for your host and verify deep links.
Static frontend output works with dynamic application data: connect to your existing backend API, or serve it alongside ZigBase on one origin. Client route guards guide navigation; the backend enforces authorization.
zigapagos release --output=public
public/
├── index.html
├── styles.css
├── images/
└── …
# With interactive components:
# + JavaScript bundles and shared runtime
# + routing and security configuration
# Deploy the generated directory.
Familiar materials
Your markup. Your styles. Your components.
Work close to the web
HTML templates, CSS stylesheets, and Preact-compatible TSX components. SuperHTML adds template directives; SuperMD and Ziggy handle content and configuration. Those conventions are explicit and documented.
Explore the source model →Bring your design
Write plain CSS, use a CSS framework's generated output, or build on your own design system. Zigapagos imposes no visual theme or component library. Keep any stylesheet compiler in your own build workflow.
Choose your styling workflow →Choose the interaction
Use HTML for static pages, islands for individual interactions, and native SPA routes for application screens. Share one runtime across islands and choose when each component hydrates.
Build application routes →Islands in 30 seconds
Write a component. Say when it should wake up.
import { useState } from "@z/runtime";
export interface Props { start?: number }
export default function Counter({ start = 0 }: Props) {
const [n, setN] = useState(start);
return <button onClick={() => setN(n + 1)}>Clicked {n} times</button>;
}
An island is a TSX component with a client: directive.
Zigapagos renders it to HTML at build time so the page is complete
without JavaScript, then ships just that component's code plus one
shared runtime.
Props are typechecked at build time against the component's exported
Props type. A misspelled prop fails the build rather than
rendering undefined in production.
Native SPAs
Give your application routes, layouts, and guarded navigation
Export a route table from a .spa.tsx and Zigapagos
prerenders a real HTML shell for every static route, then hands over to
a client-side router. Nested layouts, route guards, code-split routes
and declarative redirects are part of the route table, not a library
you add.
The shells are static files, so a guarded route still deploys to a CDN — the guard runs in the browser after the shell is served.
Navigation is tuned by default: a <Link> to a
code-split route starts fetching that route's chunk on hover, focus
or touch, so the click usually finds it already loaded —
prefetch="viewport" and prefetch={false}
adjust it per link, and a browser's data-saver signal turns it off.
Add viewTransitions: true to the spa
export and every soft navigation crossfades instead of flipping
— feature-detected, off by default, no other code. Content
pages get the declarative counterpart:
.speculation_rules = true on the site injects a
browser-native speculation-rules prefetch hint into every page,
zero runtime JS.
export const spa = { base: "/app" };
export const routes = [
{ path: "/", component: Home },
{ path: "/guides", component: Layout, children: [
{ path: "/:slug", component: Guide },
]},
{ path: "/account", component: Account,
guard: requireSession },
];
One attribute picks the moment
Say when an island should wake up. Nothing wakes up by default.
client:load
Hydrates immediately, as soon as the runtime script runs.
client:idle
Waits for requestIdleCallback, so it never competes with the page's first paint.
client:visible
Hydrates when an IntersectionObserver reports the island entered the viewport.
client:media
Hydrates only while a matchMedia query matches — a sidebar that is never interactive on mobile ships no JS there.
client:only
Hydrates immediately, like load, but is never server-rendered — the placeholder starts empty and the component exists only in the browser.
no directive
Server-rendered HTML, no JavaScript, no runtime cost. The default for every island until you add one.
Images
Responsive images from a build step, not a service
Set .image_optimize = {} in the site config and every
content image is resampled at build time into WebP variants at your
configured widths, emitted as a <picture> with a
full srcset/sizes and the untouched
original as the <img> fallback. Variants are
never upscaled past the source's own width, and derived files are
cached, so a full rebuild after the first pays file-copy cost.
AVIF is an explicit hatch: point
.image_optimize.avif_encoder at an
avifenc-compatible binary you already have and every
planned width gets an AVIF <source> ahead of the
WebP one — Zigapagos never vendors or downloads an encoder.
All of it is off by default: a site that never sets the field
builds byte-identical output.
<picture>
<source type="image/webp"
srcset="cover.a1b2c3d4.480.webp 480w,
cover.a1b2c3d4.960.webp 960w"
sizes="100vw">
<img src="cover.jpg" width="1600"
height="900" alt="…">
</picture>
Agent-ready
AI-friendly. AI not required.
Write the code yourself, work with a coding agent, or mix both. The same source, documented APIs, and local verification tools support every workflow. No AI account or service is needed to build or run your site.
Diagnostics as data
validate, doctor and
explain-code take --format=json and emit
NDJSON with stable ZP_* codes, so a fix loop matches
on the code, never on message prose.
A dev server that detaches
zigapagos dev --background waits for the server to be
ready, prints its URL, and exits; /_zigapagos/status
then reports a build generation counter and pass/fail state, so an
agent polls for its own edit to land instead of watching a
terminal. In a recognized agent environment, dev
backgrounds itself.
Context that ships
zigapagos init scaffolds AGENTS.md and
CLAUDE.md into a new site, and both the Astro and Rails
migrations are installable Agent Skills in the open agentskills.io
format.
Zigapagos + ZigBase
Bring the whole application together
Pair Zigapagos with ZigBase for data, authentication, files, realtime, jobs, and custom backend logic. Serve the static frontend and backend API on one origin, with each project independently buildable.
Zigapagos also works as a standalone frontend toolkit with your existing API. It is pre-1.0: test library compatibility and pin your releases. Backend capacity depends on the application and deployment.
Migrating an existing app
A target, a handoff, and the gaps in between
zigapagos migrate <source> --target <new-site>
recognizes Astro, Next.js, Gatsby, Nuxt/Vue, Hugo, Jekyll, Eleventy,
Hexo, and Rails. It writes a buildable target from the deterministic
parts and a MIGRATION.md that keeps everything uncertain
visible instead of guessing.
Rails goes further: static route and ERB analysis produce versioned discovery and handoff manifests, operator decisions drive conversion, ZigBase operations bind forms and authentication, and generated parity runners state what the migrated presentation must preserve.