Customizing the UI
The whole UI — templates, compiled CSS/JS, icons — ships inside your binary
via go:embed. Two overlay filesystems on Config let you replace any
piece without forking:
steward.New(steward.Config{
// ...
Brand: "Acme Ops",
TemplatesFS: os.DirFS("admin/templates"), // your files win over embedded ones
AssetsFS: os.DirFS("admin/assets"), // extra icons, overridden CSS, …
Dev: true, // re-parse templates per request
})Overriding templates
Copy the built-in views into your project and edit:
steward publish views --dir admin/templatesTemplate names are full relative paths (grid/table.html,
layout/sidebar.html, auth/login.html, …). A file at the same path in
TemplatesFS replaces the embedded one — override a single partial or the
whole layout. With Dev: true changes apply on refresh; in production
templates parse once at build.
Useful template funcs available everywhere: {{icon "name"}} (inline SVG),
{{url "posts" "create"}} (prefix-aware URLs), {{asset "dist/app.css"}}
(content-hashed asset paths), and {{dict}} for passing values into
partials.
Icons
Icons are inline SVGs resolved as icons/<name>.svg through the assets
overlay — a curated Lucide subset is embedded. To add
one, drop the SVG into your AssetsFS under icons/ and reference it:
steward.Register[Invoice](app).Icon("receipt") // AssetsFS: icons/receipt.svgBranding & chrome
Config.Brandnames the panel in the sidebar and titles.Config.Prefixmoves the whole panel (/adminby default).- Dark mode is built in — the toggle stores a cookie and the server renders the right theme with no flash of the wrong mode.
Custom pages
Resources can mount extra routes next to their CRUD ones, rendered with your own templates:
posts.Page("GET", "stats", func(c *steward.Context) error {
return c.Render("pages/post_stats.html", "Post stats", data)
})The template lives in your TemplatesFS (here at pages/post_stats.html)
and renders inside the admin layout; the handler can also return
c.JSON(...), c.Envelope(...), or c.Redirect(...).
Rebuilding the frontend bundle
You rarely need this — the compiled bundle is committed and embedded. If
you fork the styles themselves: make tailwind-bin downloads the Tailwind
standalone binary once, then make assets (esbuild via its Go API + that
binary) rebuilds assets/dist. No Node at any point.
no_ui builds
go build -tags no_ui compiles the entire UI out — templates, assets, and
handlers replaced by stubs — for API-only deployments of an app that
embeds Steward.