The Visual Website Builder
You Actually Own. React + Next.js, low-code, self-hosted. Your data, your infrastructure, every feature included.
© ChaiBuilder. All rights reserved.

ChaiBuilder is built for content-heavy, public-facing sites, and the rendering model follows from that: pages are static once built, and an edit goes live by invalidating exactly the page that changed. No rebuild, no redeploy.
This page is the conceptual view. For cache layers, hosting differences, and the manual revalidation route, see Caching & Revalidation.
The same block tree is rendered by two different engines, and most confusing rendering questions come from mixing them up.
| Builder canvas | Published page | |
|---|---|---|
| Where it runs | The client, inside the editor | Your server |
| Data | Live, fetched per interaction | Resolved once per render, then cached |
| Purpose | Show the editor what they are doing | Serve visitors |
| Reads | The draft row | The published row |
The canvas is a faithful preview, not the production renderer. When behaviour differs between the two, the published output is the one that counts - so confirm real changes with a preview or on the live page, not only in the canvas.
Every page exists as up to two rows: a draft and a published version.
Site-level settings (theme and design tokens) follow the same split and publish together as one site settings row, which is why a theme change can appear to publish more than the page you were looking at. See Preview & Publish.
/pricing. The catch-all route is marked static, so Next.js checks the
cache first.Two consequences worth internalising: deploys are fast because no pages are generated at build time, and the first visit after any invalidation is the slow one. Nothing here requires a rebuild to publish content.
A single page render does more than concatenate HTML:
{{ }} paths and pipes are resolved on the server against page-type data,
global data, and the current repeater item. A missing value becomes an empty string.next.config.ts.Data providers run on the server during step 2, and they are not all cached the same way: global and page-type data persists across renders, while collection fetches and block providers run on every render. A slow one of those is a slow first render for every invalidation.
Previewing from the builder enables Next.js draft mode for your session and reopens the page on the public route. In that mode the page is rendered per request against draft content, with no persistent caching, and a banner marks the session. Exiting preview turns it back off.
This is why preview always looks current while the live page can lag by one invalidation: they are deliberately different paths.
Cached HTML is shared by every visitor. That is the whole performance story, and it sets a boundary:
When you need per-visitor behaviour, the escape hatches are ordinary Next.js: a client component that fetches after hydration, a dynamic route you own outside the builder's catch-all, or edge logic in front of the page. The builder does not stand in your way; it just will not do it for you inside a static page.
