Skip to content

Grid

The grid is the resource’s listing: columns, search, filters, pagination, actions, and export, all declared on Grid[T]. Every string path is verified against your model’s parsed schema at boot — a typo fails startup, not a click.

posts.Grid(func(g *steward.Grid[Post]) {
    g.Column("ID").Sortable().Width(60)
    g.Column("Title").Limit(40).Sortable()
    g.Column("Status").Badge(map[any]string{"draft": "secondary", "published": "green"})
    g.Column("Featured").Switch()
    g.Column("Author.Name", "Author")
    g.QuickSearch("Title", "Body")
    g.DefaultSort("ID", true)
    g.PerPage(10, 10, 20, 50, 100)
})

Columns

Column(path) takes a field path — a direct field ("Title") or a one-hop relation ("Author.Name", preloaded automatically). Pass a custom heading as the second argument. Presentation helpers chain:

HelperRenders
Badge(map[any]string)value → colored badge ("green", "red", "secondary", …)
Bool()✓ / ✗ dot
Link(func(row *T) string)the value as a link to a computed URL
Image(w, h)the value as an <img> (storage-relative paths resolve)
Limit(n)truncated text with the full value on hover
Copyable()value with a copy-to-clipboard button
Using(map[any]string)raw value → display text
Display(func(v any, row *T) template.HTML)anything you like

Behavior helpers: Sortable(), Hide() (available in the column picker but off by default), Width(px), Help("tooltip").

Fully computed columns don’t need a field at all:

g.ColumnFunc("words", "Words", func(row *Post) template.HTML {
    return template.HTML(fmt.Sprint(len(strings.Fields(row.Body))))
})

Inline editing

Two helpers turn cells into editors that save through the form’s validation pipeline (a PUT per field — hooks and rules included):

g.Column("Title").Editable()   // click → text input, Enter saves
g.Column("Featured").Switch()  // toggle saves immediately

Quick search

QuickSearch(paths...) enables the search box. Besides plain terms it understands a small DSL: field:value targets one column, >n / <n compare numbers, and %text% forces a contains-match.

Filters

g.Filter(func(f *steward.Filters[Post]) {
    f.Equal("Status").Select(steward.Options{"draft": "Draft", "published": "Published"})
    f.Like("Title")
    f.Between("CreatedAt", "Created").Datetime()
})

Constructors: Equal, Like, Gt, Lt, In, Between, Date — each takes a path and optional label. Select(opts) renders a dropdown, Datetime() switches date inputs to date-time, Placeholder(s) hints.

Actions

Three kinds of custom actions, built with the same Action value:

publish := steward.NewAction("publish", "Publish",
    func(c *steward.Context, ids []string) (*steward.Envelope, error) {
        // mutate, then tell the client what to do
        return steward.Success("Published.").Refresh(), nil
    }).Icon("upload").Confirm("Publish the selected posts?")

g.RowAction(publish)   // one button per row (ids = that row)
g.BatchAction(publish) // acts on the checked rows
g.ToolAction(publish)  // toolbar button, no row context

Confirm shows the shared alert dialog; Danger() styles the button (and the dialog’s confirm action) destructively.

Trees, groups, reordering

g.Tree("ParentID")                            // depth-first hierarchy with collapse carets
g.GroupColumns("Publishing", "Status", "Featured") // grouped header over contiguous columns
g.Reorderable("/posts/_order")                // drag-drop row handles, POSTs the new order

Tree mode renders the whole hierarchy (searching or filtering falls back to a flat list).

Pagination & export

PerPage(def, options...) sets the default page size (10) and the selector’s choices. Windowed page links (1 … 18 19 20 … 37) appear automatically. CSV export honors the current filters, search, and sort, and offers all-pages / current-page / selection modes.

Switching features off

DisableCreate, DisableDelete, DisableEdit, DisableView, DisableFilter, DisableExport, DisableRowSelector, DisablePagination, DisableQuickSearch — each removes the UI and the behavior (e.g. DisableDelete also rejects DELETE requests).

JSON

The same grid serves Accept: application/json — filters, search, sort, and pagination included. See JSON API.