datastar/SKILL.md
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 skillsauth add maragudk/skills datastarInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Datastar is a lightweight frontend framework that enables backend-driven, interactive UIs through a hypermedia-first approach. It combines backend reactivity (similar to htmx) with frontend reactivity (like Alpine.js) using standard HTML data-* attributes.
Use this skill when:
Prerequisite: When using Datastar with Go, also use the gomponents skill for HTML component patterns.
<script type="module" src="https://cdn.jsdelivr.net/gh/starfederation/[email protected]/bundles/datastar.js"></script>
go get maragu.dev/gomponents-datastar
Signals are reactive state containers. When a signal's value changes, all dependent expressions automatically update.
<div data-signals="{count: 0}">
<span data-text="$count"></span>
<button data-on:click="$count++">Increment</button>
</div>
$ in expressionsnull or undefined removes it$user.nameDatastar uses morphing to update only changed DOM parts while preserving state. The backend sends HTML fragments that patch into the existing page.
data-signals - Initialize reactive signals:
<div data-signals="{name: 'World', count: 0}"></div>
data-computed - Create derived read-only signals:
<div data-computed="{doubled: $count * 2}"></div>
data-init - Run expressions when element loads:
<div data-init="console.log('Loaded')"></div>
data-text - Bind text content to an expression:
<span data-text="'Hello, ' + $name"></span>
data-bind - Two-way binding for form elements:
<input data-bind="$name" type="text">
data-show - Conditionally show/hide elements:
<div data-show="$isVisible">Only shown when true</div>
data-class - Conditionally apply CSS classes:
<div data-class="{'active': $isActive, 'error': $hasError}"></div>
data-style - Set inline styles dynamically:
<div data-style="{'color': $textColor, 'opacity': $opacity}"></div>
data-attr - Set HTML attributes dynamically:
<button data-attr="{'disabled': $isLoading}">Submit</button>
data-on - Attach event listeners:
<button data-on:click="$count++">Click me</button>
<input data-on:input="$search = evt.target.value">
<form data-on:submit__prevent="@post('/submit')">
The evt variable references the event object.
data-on-intersect - Trigger when element enters viewport:
<div data-on-intersect="@get('/load-more')">Loading...</div>
data-on-interval - Run at regular intervals:
<div data-on-interval="$elapsed++">Timer: <span data-text="$elapsed"></span></div>
data-on-signal-patch - Execute when signals update:
<div data-on-signal-patch="console.log('Signals changed:', patch)"></div>
data-ref - Create signal referencing DOM element:
<input data-ref="$inputEl" type="text">
data-ignore - Exclude element from Datastar processing:
<div data-ignore>Third-party widget here</div>
data-ignore-morph - Keep Datastar active but skip morphing:
<div data-ignore-morph>Preserve this DOM structure</div>
data-preserve-attr - Preserve attributes during morphing:
<input data-preserve-attr="value" type="text">
@get(), @post(), @put(), @patch(), @delete() - Send requests to backend:
<button data-on:click="@get('/api/data')">Load</button>
<button data-on:click="@post('/api/submit')">Submit</button>
Modifiers extend attribute behavior using double-underscore syntax:
__debounce / __debounce_500ms - Debounce execution__throttle / __throttle_1s - Throttle execution__delay / __delay_200ms - Delay execution__prevent - Call preventDefault()__stop - Call stopPropagation()__capture - Use capture phase__passive - Mark as passive listener__once - Execute only once__self - Only trigger if target is the element itself__outside - Trigger when event occurs outside element__window - Attach listener to 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')">
Datastar uses SSE for streaming responses. The backend sends events with text/event-stream content type.
datastar-patch-elements - Patch HTML into the DOM:
event: datastar-patch-elements
data: elements <div id="content">Updated content</div>
datastar-patch-signals - Update signal values:
event: datastar-patch-signals
data: signals {count: 42}
datastar-remove-elements - Remove elements by selector:
event: datastar-remove-elements
data: selector #old-element
gomponents-datastar provides Go functions that generate Datastar attributes as gomponents nodes. It integrates seamlessly with the gomponents library.
Use dot imports for a clean DSL:
import (
. "maragu.dev/gomponents"
. "maragu.dev/gomponents/html"
data "maragu.dev/gomponents-datastar"
)
Note: The data alias is recommended for datastar.
For up-to-date API documentation, run:
go doc maragu.dev/gomponents-datastar
Signals - Initialize reactive signals:
data.Signals(map[string]any{
"count": 0,
"name": "World",
})
Computed - Create computed signals (key-value pairs):
data.Computed("doubled", "$count * 2")
Init - Run expression on load:
data.Init("console.log('Component loaded')")
Text - Bind text content:
Span(data.Text("'Hello, ' + $name"))
Bind - Two-way form binding:
Input(Type("text"), data.Bind("$name"))
Show - Conditional visibility:
Div(data.Show("$isVisible"), Text("Shown when visible"))
Class - Conditional classes (key-value pairs):
data.Class("active", "$isActive", "error", "$hasError")
Style - Dynamic inline styles (key-value pairs):
data.Style("color", "$textColor", "opacity", "$opacity")
Attr - Dynamic attributes (key-value pairs):
data.Attr("disabled", "$isLoading", "aria-busy", "$isLoading")
On - Attach event listeners:
data.On("click", "$count++")
data.On("click", "@post('/submit')", data.ModifierPrevent)
data.On("input", "$search = evt.target.value", data.ModifierDebounce)
OnIntersect - Viewport intersection:
data.OnIntersect("@get('/load-more')")
OnInterval - Periodic execution:
data.OnInterval("$elapsed++")
OnSignalPatch - React to signal changes:
data.OnSignalPatch("console.log('Updated')")
Ref - Reference DOM element:
data.Ref("$inputEl")
Ignore - Skip Datastar processing:
data.Ignore()
IgnoreMorph - Skip morphing only:
data.IgnoreMorph()
PreserveAttr - Preserve attributes during morph:
data.PreserveAttr("value", "checked")
Indicator - Show loading state:
data.Indicator("$isLoading")
JSONSignals - Control which signals are sent:
data.JSONSignals(data.Filter{Include: "form.*"})
data.JSONSignals(data.Filter{Exclude: "internal.*"})
Use modifier constants with event functions:
// 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.5
package 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()
}
$ prefix in expressions, not in Go codedata.Text() for Datastar text binding, gomponents Text() for static contentdata.On("click", "...", data.ModifierPrevent, data.ModifierOnce)id attributesdevelopment
Guide for using git worktrees to parallelize development with coding agents. Use this skill when the user requests to work in a new worktree or wants to work on a separate feature in isolation (e.g., "Work in a new worktree", "Create a worktree for feature X").
development
Guide for working with SQL queries, in particular for SQLite. Use this skill when writing SQL queries, analyzing database schemas, designing migrations, or working with SQLite-related code.
tools
Guide for saving a web page for offline use using the monolith CLI. Use this when instructed to save a web page.
development
# Observable Plot Skill Observable Plot is a JavaScript library for exploratory data visualization. It's built on D3 and provides a concise, declarative API for creating charts. ## Installation ```bash npm install @observablehq/plot ``` Or via CDN: ```html <script type="module"> import * as Plot from "https://cdn.jsdelivr.net/npm/@observablehq/[email protected]/+esm"; </script> ``` ## Core Concepts ### Plot.plot(options) The main function that renders a visualization. Returns an SVG or HTML figure