Zen is tiny glue for building server-rendered Go apps with Fiber, Vite, Preact, Tailwind, and Air.
It does not replace those tools. It keeps Fiber in charge of routes, Vite in charge of frontend builds and dev behavior, Preact in charge of components and hydration, Tailwind in charge of styling, and Air in charge of Go hot reload.
Use the library from Go apps:
go get github.com/zenith-hosting/zenThe CLI lives in this repo under zencli/:
go install github.com/zenith-hosting/zen/zencli/cmd/zen@latestThat command installs a zen binary. Build a local zen binary from this repo with:
go build -o ./bin/zen ./zencli/cmd/zenZen fits inside ordinary Fiber handlers:
package main
import (
"log"
"os"
"time"
"github.com/gofiber/fiber/v3"
"github.com/zenith-hosting/zen"
)
func main() {
app := fiber.New()
dev := os.Getenv("ZEN_ENV") != "prod"
cfg := zen.Config{
Dev: dev,
DefaultTitle: "Zen App",
RenderTimeout: 5 * time.Second,
}
renderer, err := zen.New(cfg)
if err != nil {
log.Fatal(err)
}
defer renderer.Close()
if !dev {
app.Get("/assets/*", renderer.Static())
}
app.Get("/", func(c fiber.Ctx) error {
return renderer.RenderPage(c, "Home", map[string]any{
"title": "Zen App",
}, zen.WithTitle("Home"))
})
log.Fatal(app.Listen(":3000"))
}The Go app reads renderer ports and frontend paths from zen.config.json, then calls the Node renderer over HTTP:
POST /__zen/render
GET /__zen/health
If the app does not run from the project root, set ProjectRoot in zen.Config so Zen can find zen.config.json.
Create a starter project:
zen init
zen devzen init writes the starter project, runs go mod tidy, installs frontend dependencies, and approves required pnpm builds.
Build and start production artifacts:
zen build
zen startzen dev starts the Vite/Preact renderer and the Go app through Air. zen build builds the frontend and Go binary. zen start starts existing production artifacts and does not build.
This repository is the Zen library module:
github.com/zenith-hosting/zen
The CLI lives in this repository as its own Go module:
github.com/zenith-hosting/zen/zencli
Canonical frontend runtime sources are in js/entries and js/renderers. The starter-template copies live under zencli/internal/zencli/init_template. Sync them with:
node scripts/sync-renderers.mjsRun the main checks:
go test ./...
node --test js/renderers/*.test.mjs
node --test scripts/*.test.mjsRun CLI checks from the zencli module:
cd zencli
go test ./...
go build -o /tmp/zen ./cmd/zen