tako docs guide reference api
GitHub

6 · blog — a static generator with dynamic islands

examples/blog/app.go is the static-site showcase: Hugo builds the site, tako ships it, and htmx islands call back into tako fragments for the one dynamic thing a blog needs — comments.

var comments = tako.Entity[Comment]("comments")

var site = tako.Site("./site/public",
    tako.SiteConfig{Build: tako.Build{Command: "cd site && hugo --minify", Out: "./site/public"}})

var Comments   = tako.Page("GET /frag/comments", listComments)
var AddComment = tako.Page("POST /frag/comments", addComment)

The division of labor

Hugo owns ./site — content, layouts, styling, the whole shell. SiteConfig.Build makes its build part of tako's: tako dev reruns Hugo on every save (and doesn't watch the output dir, so no rebuild loops), tako deploy runs it once and embeds site/public in the service binary. There are no page routes for posts here — the analyzer's graph shows two fragment routes and nothing else, because static pages aren't handlers.

The island

Each Hugo post template carries one dynamic slot:

<section id="comments" hx-get="/frag/comments?post={{ .RelPermalink }}"
         hx-trigger="load" hx-swap="outerHTML"></section>

The fragment renders the comments and the form that grows them; posting re-renders the island in place (htmx) or 303s back to the static page (no JS). The comment's key is the post's RelPermalink — the one stable identity a generator gives every page — and because the handler queries Where("Post", =).OrderBy("Created"), the composite index (post, created) is derived, planned, and destroyed with everything else.

The templates trick

Fragment templates live in site/static/templates/ — Hugo copies them into public/templates/, and templates/ is the one subtree a tako Site never serves raw. One build feeds both worlds, and the site the tests serve is the declared one: site/public, Hugo's output. So tako test runs Hugo first, the same way tako dev and tako deploy do — the site under test is the site that ships, and a checkout that has never built (site/public is gitignored) is not a failing test.

A site without templates/layout.html is fine — the shell belongs to Hugo. Only site.Render (full pages) needs a layout, and it says so if called without one.

Honesty at the edges

Run it: cd examples/blog && tako dev (Hugo required: brew install hugo).