Active Search

Type in the search box and the result list re-renders on the server as you go — no client-side filtering. A single Change action carries the current query, the handler re-runs the search, and the table updates. The client debounces input (300ms) so each keystroke doesn't fire a round trip.

Active Search

Type in the search box. The Change() handler fires after a 300ms debounce and re-renders the result list server-side — no client-side filtering logic.

NameEmail
Marcus Chen marcus.chen@example.com
Priya Patel priya.patel@example.com
Diana Okonkwo diana.okonkwo@example.com
Rafael Hernandez rafael.hernandez@example.com
Yuki Tanaka yuki.tanaka@example.com
Fatima Al-Rashid fatima.alrashid@example.com
Liam O'Brien liam.obrien@example.com
Sofia Rossi sofia.rossi@example.com
Kwame Asante kwame.asante@example.com
Ingrid Nilsson ingrid.nilsson@example.com
Arjun Sharma arjun.sharma@example.com
Elena Volkov elena.volkov@example.com
Mateo Silva mateo.silva@example.com
Aisha Bello aisha.bello@example.com
Thomas Weber thomas.weber@example.com
Nadia Haddad nadia.haddad@example.com
Jin-ho Park jinho.park@example.com
Olivia Bennett olivia.bennett@example.com
Samuel Adeyemi samuel.adeyemi@example.com
Carmen Reyes carmen.reyes@example.com
Henrik Larsen henrik.larsen@example.com
Meera Krishnan meera.krishnan@example.com
Luca Bianchi luca.bianchi@example.com
Zara Ahmed zara.ahmed@example.com
Gabriel Martinez gabriel.martinez@example.com

Template

A plain <form method="POST"> with one search input whose name is query; the table ranges over .Results. The input's value="{{.Query}}" echoes the server's view so the box stays in sync after each render.

{{define "content"}}
<article>
    <h3>Active Search</h3>
    <p><small>Type in the search box. The Change() handler fires after a 300ms debounce and re-renders the result list server-side — no client-side filtering logic.</small></p>
    <form method="POST">
        <label>Search contacts
            <input type="search" name="query" value="{{.Query}}" placeholder="Name or email…" autocomplete="off">
        </label>
    </form>
    <table>
        <thead><tr><th>Name</th><th>Email</th></tr></thead>
        <tbody>
        {{range .Results}}
        <tr data-key="{{.ID}}">
            <td>{{.Name}}</td>
            <td>{{.Email}}</td>
        </tr>
        {{end}}
        </tbody>
    </table>
    {{if and .Query (eq (len .Results) 0)}}
    <p><small>No contacts match "<strong>{{.Query}}</strong>".</small></p>
    {{end}}
</article>
{{end}}

active-search.tmpl

Handler & state

Change reads query when present, re-runs searchContacts, and stores both the query and the matching contacts back on state.

type ActiveSearchController struct{}

func (c *ActiveSearchController) Mount(state ActiveSearchState, ctx *livetemplate.Context) (ActiveSearchState, error) {
	// Full directory visible on initial render so the "filter down" story is obvious.
	state.Results = searchContacts(state.Query)
	return state, nil
}

func (c *ActiveSearchController) Change(state ActiveSearchState, ctx *livetemplate.Context) (ActiveSearchState, error) {
	if ctx.Has("query") {
		state.Query = ctx.GetString("query")
		state.Results = searchContacts(state.Query)
	}
	return state, nil
}

func activeSearchHandler() http.Handler {
	tmpl := newLayoutTmpl("templates/layout.tmpl", "templates/search/active-search.tmpl")
	return tmpl.Handle(&ActiveSearchController{}, livetemplate.AsState(&ActiveSearchState{
		Title:    "Active Search",
		Category: "Search & Filtering",
	}))
}

handlers_search.go:12-35

// ActiveSearchState holds the state for the Active Search pattern (#12).
type ActiveSearchState struct {
	Title    string
	Category string
	Query    string
	Results  []Contact
}

state_search.go:4-11

When to use

Reach for URL-Preserved Filters when the filter state should be bookmarkable and survive a reload.

source: livetemplate/docs · path: examples/patterns/templates/search/active-search.tmpl