The component data model
A single JSON tree is the source of truth behind every site. Here's its shape and why every editing layer writes through it.
One tree, three schemas
Every Zitegen site is a structured tree, not a pile of .astro files. The shape is defined as Zod schemas in the @zitegen/component-model package and nests three levels deep:
- SiteModel — siteId plus an ordered array of pages.
- PageTree — one route: pageId, path (like /about), title, and a single root node.
- ComponentNode — the recursive building block every element maps to.
A ComponentNode carries: id (a UUID), type (the element kind), props (HTML attributes, including the class string), styles (per-breakpoint CSS), states (optional hover/focus overrides), content (text for leaf nodes), html (raw markup for custom-code nodes), dynamicValue (build-time values like the current year), sharedComponentId (marks a Reusable Component instance), and children.
What a node's type can be
data-zitegen-id: the bridge
A node's id is what ties the JSON tree to the rendered page. When the compiler emits an element, it writes that id onto the markup as a data-zitegen-id attribute, and per-node styles target it with a high-specificity html [data-zitegen-id="…"] selector.
That attribute is the anchor for every editing layer: it's how the visual editor knows which node you clicked, how the style panel scopes a change, and how the AI targets an element to revise. IDs are minted server-side when a tree is created, so they stay stable as you edit.
Styles are stored per breakpoint
A node's styles aren't one flat object — they're split into mobile, tablet, and desktop maps of CSS property to value. The compiler emits them as a desktop-first cascade: desktop is the base with no media query, tablet adds a max-width: 1023.98px override, and mobile a max-width: 767.98px one — emitted last so it wins on small screens.
This is why a style-panel change is always scoped to the breakpoint you're editing, and never silently leaks across sizes. Pseudo-state overrides (hover, focus) live in states and follow the same three-tier pattern.
Every layer writes through one path
The tree is the single source of truth, and every writer funnels through one server-side function — updatePageTreeCore. AI chat, the canvas and style panel, code-side saves, and the MCP tools all hand it a tree; it checks access, validates against ComponentNodeSchema, and persists — recording a version as it goes.
Because there is exactly one chokepoint, the layers can't drift out of sync or corrupt each other's work. Whatever made the last edit, the Source view, a download, and the next AI prompt all see the same tree.
Validate before you write
What's next
Questions?