| First name | Last name arrow_upward | First and last arrow_upward name | Age | State | ||
|---|---|---|---|---|---|---|
| Christopher | Garcia | Christopher Garcia | 48 | christopher.garcia@example.com | TX | more_horiz |
| Linda | Jackson | Linda Jackson | 38 | linda.jackson@example.com | OH | more_horiz |
| Margaret | Johnson | Margaret Johnson | 73 | margaret.johnson@example.com | NC | more_horiz |
Declarative typed widgets in the shape of SwiftUI, Jetpack Compose, or Flutter — except the renderer is your net/http server. BESPA composes a tree of Go structs, ships HTML to the browser, and quietly swaps in updated fragments as state changes. No JavaScript framework. No build step. No bundler. Material Design 3 is the default.
func(w http.ResponseWriter, r *http.Request) {
wf.Page().Add("Hello, World!").Draw(w, r)
}
package main
import (
"net/http"
"github.com/microbus-io/bespa"
"github.com/microbus-io/bespa/widget"
)
var wf = bespa.DefaultFactory{}
func handleHome(w http.ResponseWriter, r *http.Request) {
state := wf.StateOf(r)
page := wf.Page().Add(
wf.AppBar("Hello"),
wf.Form().Add(
wf.InputText("name", "").
WithPlaceholder("Your name").
WithAutoSubmit(true),
),
wf.HeadlineMedium("Hello, ", state.Get("name"), "!").
HideIfEmpty(r, "name").
RedrawIfChanged(r, "name"),
)
page.Draw(w, r)
}
func main() {
http.HandleFunc("/bespa/", widget.AssetRegistry.ServeHTTP)
http.HandleFunc("/", handleHome)
http.ListenAndServe(":8080", nil)
}
http://localhost:8080.RedrawIfChanged and re-render whenever it moves. A counter looks like this:state := wf.StateOf(r)
count, _ := strconv.Atoi(state.Get("count"))
page := wf.Page().Add(
wf.AppBar("Counter"),
wf.HeadlineLarge(count).
RedrawIfChanged(r, "count"),
wf.Toolbar().AddLeft(
wf.ButtonFilled("").Add("-").
WithHref("?count=" + strconv.Itoa(count-1)).
RedrawIfChanged(r, "count"),
wf.ButtonFilled("").Add("+").
WithHref("?count=" + strconv.Itoa(count+1)).
RedrawIfChanged(r, "count"),
),
)