Loading...
Loading...
Guide for building interactive web UIs with Datastar and gomponents-datastar. Use this skill when adding frontend interactivity to Go web applications with Datastar attributes.
npx skill4agent add maragudk/skills datastardata-*<script type="module" src="https://cdn.jsdelivr.net/gh/starfederation/datastar@v1.0.0-RC.7/bundles/datastar.js"></script>go get maragu.dev/gomponents-datastar<div data-signals="{count: 0}">
<span data-text="$count"></span>
<button data-on:click="$count++">Increment</button>
</div>$nullundefined$user.name<div data-signals="{name: 'World', count: 0}"></div><div data-computed="{doubled: $count * 2}"></div><div data-init="console.log('Loaded')"></div><span data-text="'Hello, ' + $name"></span><input data-bind="$name" type="text"><div data-show="$isVisible">Only shown when true</div><div data-class="{'active': $isActive, 'error': $hasError}"></div><div data-style="{'color': $textColor, 'opacity': $opacity}"></div><button data-attr="{'disabled': $isLoading}">Submit</button><button data-on:click="$count++">Click me</button>
<input data-on:input="$search = evt.target.value">
<form data-on:submit__prevent="@post('/submit')">evt<div data-on-intersect="@get('/load-more')">Loading...</div><div data-on-interval="$elapsed++">Timer: <span data-text="$elapsed"></span></div><div data-on-signal-patch="console.log('Signals changed:', patch)"></div><input data-ref="$inputEl" type="text"><div data-ignore>Third-party widget here</div><div data-ignore-morph>Preserve this DOM structure</div><input data-preserve-attr="value" type="text"><button data-on:click="@get('/api/data')">Load</button>
<button data-on:click="@post('/api/submit')">Submit</button>__debounce__debounce_500ms__throttle__throttle_1s__delay__delay_200ms__preventpreventDefault()__stopstopPropagation()__capture__passive__once__self__outside__window<input data-on:input__debounce_300ms="@get('/search?q=' + $query)">
<button data-on:click__once="@post('/track-click')">Track</button>
<form data-on:submit__prevent="@post('/submit')">text/event-streamevent: datastar-patch-elements
data: elements <div id="content">Updated content</div>event: datastar-patch-signals
data: signals {count: 42}event: datastar-remove-elements
data: selector #old-elementimport (
. "maragu.dev/gomponents"
. "maragu.dev/gomponents/html"
data "maragu.dev/gomponents-datastar"
)datago doc maragu.dev/gomponents-datastardata.Signals(map[string]any{
"count": 0,
"name": "World",
})data.Computed("doubled", "$count * 2")data.Init("console.log('Component loaded')")Span(data.Text("'Hello, ' + $name"))Input(Type("text"), data.Bind("$name"))Div(data.Show("$isVisible"), Text("Shown when visible"))data.Class("active", "$isActive", "error", "$hasError")data.Style("color", "$textColor", "opacity", "$opacity")data.Attr("disabled", "$isLoading", "aria-busy", "$isLoading")data.On("click", "$count++")
data.On("click", "@post('/submit')", data.ModifierPrevent)
data.On("input", "$search = evt.target.value", data.ModifierDebounce)data.OnIntersect("@get('/load-more')")data.OnInterval("$elapsed++")data.OnSignalPatch("console.log('Updated')")data.Ref("$inputEl")data.Ignore()data.IgnoreMorph()data.PreserveAttr("value", "checked")data.Indicator("$isLoading")data.JSONSignals(data.Filter{Include: "form.*"})
data.JSONSignals(data.Filter{Exclude: "internal.*"})// Timing
data.ModifierDebounce // __debounce
data.ModifierThrottle // __throttle
data.ModifierDelay // __delay
// Event behavior
data.ModifierPrevent // __prevent
data.ModifierStop // __stop
data.ModifierCapture // __capture
data.ModifierPassive // __passive
data.ModifierOnce // __once
data.ModifierSelf // __self
data.ModifierOutside // __outside
data.ModifierWindow // __window
// Duration/threshold helpers
data.Duration(500 * time.Millisecond) // __500ms
data.Threshold(0.5) // __threshold_0.5package views
import (
"net/http"
. "maragu.dev/gomponents"
. "maragu.dev/gomponents/html"
ghttp "maragu.dev/gomponents/http"
ds "maragu.dev/gomponents-datastar"
)
func CounterPage() Node {
return HTML5(HTML5Props{
Title: "Counter",
Language: "en",
Head: []Node{
Script(
Type("module"),
Src("https://cdn.jsdelivr.net/gh/starfederation/[email protected]/bundles/datastar.js"),
),
},
Body: []Node{
Div(
data.Signals(map[string]any{"count": 0}),
H1(data.Text("'Count: ' + $count")),
Button(
data.On("click", "$count++"),
Text("Increment"),
),
Button(
data.On("click", "$count--"),
Text("Decrement"),
),
Button(
data.On("click", "@post('/api/save')"),
Text("Save to Server"),
),
),
},
})
}
func SearchForm() Node {
return Form(
data.Signals(map[string]any{"query": "", "results": []any{}}),
data.On("submit", "@get('/search?q=' + $query)", data.ModifierPrevent),
Input(
Type("text"),
data.Bind("$query"),
data.On("input", "@get('/search?q=' + $query)", data.ModifierDebounce, data.Duration(300*time.Millisecond)),
Placeholder("Search..."),
),
Div(
ID("results"),
data.Show("$results.length > 0"),
data.Text("'Found ' + $results.length + ' results'"),
),
)
}func handleSSE(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/event-stream")
w.Header().Set("Cache-Control", "no-cache")
w.Header().Set("Connection", "keep-alive")
flusher, ok := w.(http.Flusher)
if !ok {
http.Error(w, "SSE not supported", http.StatusInternalServerError)
return
}
// Patch HTML elements
fmt.Fprintf(w, "event: datastar-patch-elements\n")
fmt.Fprintf(w, "data: elements <div id=\"content\">Updated!</div>\n\n")
flusher.Flush()
// Update signals
fmt.Fprintf(w, "event: datastar-patch-signals\n")
fmt.Fprintf(w, "data: signals {\"count\": 42}\n\n")
flusher.Flush()
}$data.Text()Text()data.On("click", "...", data.ModifierPrevent, data.ModifierOnce)id