Gastro

Guestbook (Datastar)

A working guestbook powered by Datastar (opens in new tab) and server-sent events. Search, add entries, and edit inline — all without page reloads.

Colette

If you can read, you can cook. If you know Go, you can use Gastro.

Linguini

I don't really know what I'm doing, but the file-based routing makes it easy.

Remy

Anyone can cook... and anyone can build web apps with Gastro!

How It Works

This demo showcases four common hypermedia patterns using Datastar and Gastro's SSE support.

Search-as-you-type

The search input uses data-bind to create a reactive signal and data-on:input__debounce.300ms to send a debounced request. The server filters entries and patches the list:

<input
    data-bind:search
    data-on:input__debounce.300ms="@get('/api/ds/search')"
    data-indicator:searching
>

Form Submission

The form uses data-on:submit to intercept submission and send the data via Datastar's @post. The server validates, adds the entry, and patches both the list and the form:

func handleDsAdd(w http.ResponseWriter, r *http.Request) {
    name := strings.TrimSpace(r.FormValue("name"))
    message := strings.TrimSpace(r.FormValue("message"))

    demo.AddEntry(name, message)

    // Patch the list with new entries
    sse := datastar.NewSSE(w, r)
    sse.PatchElements(listHTML)

    // Patch the form with a success message
    sse.PatchElements(formHTML)
}

Inline Editing

Each entry has an edit button that sends @get('/api/ds/edit/{id}'). The server returns a GuestbookEntryEdit component that replaces the read-only row. Saving sends @post('/api/ds/save/{id}') and swaps back to the read-only view.

Loading States

The data-indicator attribute creates a boolean signal that's true while a request is in flight. Combined with data-show and data-attr:disabled, this provides visual feedback without any JavaScript:

<button data-indicator:submitting data-attr:disabled="$submitting">
    <span data-show="!$submitting">Sign Guestbook</span>
    <span data-show="$submitting">Sending...</span>
</button>