Views
Caching
Cache controller views, partials, and Turbo frame bodies.
Use the app cache
Every lazyapp.New application has a cache:
app := lazyapp.New(lazyapp.Config{
Name: "sample_app",
Drawer: Draw,
Views: app.Views,
})
When Cache.Backend is omitted, lazyapp uses the in-memory backend from
golazy.dev/lazycache/inmemorycache. Direct lazycache.New calls must pass a
backend explicitly.
You can use the cache from the app value:
err := lazycache.Set(app.Cache, post, "post", post.ID, post.UpdatedAt)
and read typed values:
post, err := lazycache.Get[Post](app.Cache, "post", id, updatedAt)
The cache key is built by joining key parts with -. time.Time values use
UTC RFC3339Nano, so timestamps are stable across time zones. Controller and
template render caches also include the app build version, and include active
render variants when present, so deploys and variant-specific templates do not
share stale rendered bodies.
Cache controller renders
Controllers opt a rendered response body into caching with CacheKey:
func (c *PostsController) Show(postID int) error {
post, err := c.posts.Get(postID)
if err != nil {
return err
}
if c.CacheKey(post.ID, post.UpdatedAt) {
return nil
}
c.Set("post", post)
return nil
}
The key includes the app build version, namespace when present, controller, action, request format, and the parts you pass:
build-v0.1.17-admin-posts-show-html-42-2026-06-25T10:30:00Z
Use CacheKeyF when the action should provide the application-specific key
parts:
if c.CacheKeyF("post", post.ID, post.UpdatedAt) {
return nil
}
CacheKey and CacheKeyF return true when the cached body was written, so
the action should stop and return nil. On a miss they return false; the
normal render path continues and stores the rendered body for the next matching
request. Only the rendered body is cached. Headers and status codes still come
from the current request.
Cache partials
Use cache around a partial:
{{ cache "featured" "post_card" .post }}
That key is scoped to the build version, active render variants, namespace, controller, action, format, partial name, and local name.
Use cache_key for a full explicit key:
{{ cache (cache_key "post" .post.ID .post.UpdatedAt) "post_card" .post }}
cachef is the same idea without a nested helper call:
{{ cachef "post" .post.ID .post.UpdatedAt "post_card" .post }}
Cache Turbo frame bodies
Turbo frames can cache the rendered frame body while keeping current frame attributes:
{{ turbo_frame "post" .post (cache_key "post" .post.ID .post.UpdatedAt) (turbo_src .post.URL) }}
If the same key is reused later, GoLazy reuses the body and applies the current
turbo_frame options again.
Switch caching
Use Off to bypass reads and turn writes into no-ops:
app.Cache.Off()
Use On to resume backend reads and writes. Stats returns the standardized
backend counters:
stats := app.Cache.Stats()
In lazy development mode, the Cache tab exposes the same cache state, stats,
keys, entry sizes, searchable key table, selected entry content, and On/Off
switch through the app's lazydev control plane.
The first backend is in-process and stores Go values. It is useful for a single running process; choose a different backend when multiple app processes must share cached values.