datastar

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Datastar

Datastar

Overview

概述

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.
Datastar是一个轻量级前端框架,通过超媒体优先的方式实现后端驱动的交互式UI。它结合了后端响应性(类似htmx)与前端响应性(如Alpine.js),使用标准HTML的
data-*
属性。

When to Use This Skill

适用场景

Use this skill when:
  • Adding frontend interactivity to server-rendered HTML
  • Building reactive UIs driven by backend state
  • Using Datastar with gomponents in Go applications
  • Working with Server-Sent Events (SSE) for real-time updates
Prerequisite: When using Datastar with Go, also use the gomponents skill for HTML component patterns.
在以下场景中使用本技能:
  • 为服务器渲染的HTML添加前端交互性
  • 构建由后端状态驱动的响应式UI
  • 在Go应用中结合Datastar与gomponents使用
  • 利用Server-Sent Events (SSE)实现实时更新
前置要求: 在Go中使用Datastar时,需同时搭配gomponents技能来实现HTML组件模式。

Installation

安装

Browser (CDN)

浏览器(CDN)

html
<script type="module" src="https://cdn.jsdelivr.net/gh/starfederation/datastar@v1.0.0-RC.7/bundles/datastar.js"></script>
html
<script type="module" src="https://cdn.jsdelivr.net/gh/starfederation/datastar@v1.0.0-RC.7/bundles/datastar.js"></script>

Go (gomponents-datastar)

Go(gomponents-datastar)

go get maragu.dev/gomponents-datastar

go get maragu.dev/gomponents-datastar

Part 1: Datastar Fundamentals

第一部分:Datastar基础

Core Concepts

核心概念

Signals

信号(Signals)

Signals are reactive state containers. When a signal's value changes, all dependent expressions automatically update.
html
<div data-signals="{count: 0}">
    <span data-text="$count"></span>
    <button data-on:click="$count++">Increment</button>
</div>
  • Signal names are prefixed with
    $
    in expressions
  • Setting a signal to
    null
    or
    undefined
    removes it
  • Use dot-notation for nested signals:
    $user.name
信号是响应式状态容器。当信号值改变时,所有依赖该信号的表达式会自动更新。
html
<div data-signals="{count: 0}">
    <span data-text="$count"></span>
    <button data-on:click="$count++">Increment</button>
</div>
  • 信号名称在表达式中需以
    $
    为前缀
  • 将信号设为
    null
    undefined
    会将其移除
  • 嵌套信号使用点符号:
    $user.name

DOM Patching

DOM修补(DOM Patching)

Datastar uses morphing to update only changed DOM parts while preserving state. The backend sends HTML fragments that patch into the existing page.
Datastar使用形态匹配(morphing)技术,仅更新DOM中发生变化的部分,同时保留原有状态。后端发送HTML片段,将其修补到现有页面中。

Attributes Reference

属性参考

State Management

状态管理

data-signals - Initialize reactive signals:
html
<div data-signals="{name: 'World', count: 0}"></div>
data-computed - Create derived read-only signals:
html
<div data-computed="{doubled: $count * 2}"></div>
data-init - Run expressions when element loads:
html
<div data-init="console.log('Loaded')"></div>
data-signals - 初始化响应式信号:
html
<div data-signals="{name: 'World', count: 0}"></div>
data-computed - 创建只读的派生信号:
html
<div data-computed="{doubled: $count * 2}"></div>
data-init - 元素加载时执行表达式:
html
<div data-init="console.log('Loaded')"></div>

Data Binding

数据绑定

data-text - Bind text content to an expression:
html
<span data-text="'Hello, ' + $name"></span>
data-bind - Two-way binding for form elements:
html
<input data-bind="$name" type="text">
data-show - Conditionally show/hide elements:
html
<div data-show="$isVisible">Only shown when true</div>
data-text - 将文本内容绑定到表达式:
html
<span data-text="'Hello, ' + $name"></span>
data-bind - 表单元素的双向绑定:
html
<input data-bind="$name" type="text">
data-show - 条件性显示/隐藏元素:
html
<div data-show="$isVisible">Only shown when true</div>

Styling

样式控制

data-class - Conditionally apply CSS classes:
html
<div data-class="{'active': $isActive, 'error': $hasError}"></div>
data-style - Set inline styles dynamically:
html
<div data-style="{'color': $textColor, 'opacity': $opacity}"></div>
data-attr - Set HTML attributes dynamically:
html
<button data-attr="{'disabled': $isLoading}">Submit</button>
data-class - 条件性应用CSS类:
html
<div data-class="{'active': $isActive, 'error': $hasError}"></div>
data-style - 动态设置内联样式:
html
<div data-style="{'color': $textColor, 'opacity': $opacity}"></div>
data-attr - 动态设置HTML属性:
html
<button data-attr="{'disabled': $isLoading}">Submit</button>

Events

事件处理

data-on - Attach event listeners:
html
<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:
html
<div data-on-intersect="@get('/load-more')">Loading...</div>
data-on-interval - Run at regular intervals:
html
<div data-on-interval="$elapsed++">Timer: <span data-text="$elapsed"></span></div>
data-on-signal-patch - Execute when signals update:
html
<div data-on-signal-patch="console.log('Signals changed:', patch)"></div>
data-on - 绑定事件监听器:
html
<button data-on:click="$count++">Click me</button>
<input data-on:input="$search = evt.target.value">
<form data-on:submit__prevent="@post('/submit')">
evt
变量代表事件对象。
data-on-intersect - 元素进入视口时触发:
html
<div data-on-intersect="@get('/load-more')">Loading...</div>
data-on-interval - 定期执行表达式:
html
<div data-on-interval="$elapsed++">Timer: <span data-text="$elapsed"></span></div>
data-on-signal-patch - 信号更新时执行:
html
<div data-on-signal-patch="console.log('Signals changed:', patch)"></div>

DOM Control

DOM控制

data-ref - Create signal referencing DOM element:
html
<input data-ref="$inputEl" type="text">
data-ignore - Exclude element from Datastar processing:
html
<div data-ignore>Third-party widget here</div>
data-ignore-morph - Keep Datastar active but skip morphing:
html
<div data-ignore-morph>Preserve this DOM structure</div>
data-preserve-attr - Preserve attributes during morphing:
html
<input data-preserve-attr="value" type="text">
data-ref - 创建指向DOM元素的信号:
html
<input data-ref="$inputEl" type="text">
data-ignore - 排除元素,不进行Datastar处理:
html
<div data-ignore>Third-party widget here</div>
data-ignore-morph - 保持Datastar激活,但跳过形态匹配:
html
<div data-ignore-morph>Preserve this DOM structure</div>
data-preserve-attr - 形态匹配期间保留指定属性:
html
<input data-preserve-attr="value" type="text">

Backend Actions

后端操作

@get(), @post(), @put(), @patch(), @delete() - Send requests to backend:
html
<button data-on:click="@get('/api/data')">Load</button>
<button data-on:click="@post('/api/submit')">Submit</button>
@get(), @post(), @put(), @patch(), @delete() - 向后端发送请求:
html
<button data-on:click="@get('/api/data')">Load</button>
<button data-on:click="@post('/api/submit')">Submit</button>

Modifiers

修饰符

Modifiers extend attribute behavior using double-underscore syntax:
修饰符通过双下划线语法扩展属性行为:

Timing Modifiers

时序修饰符

  • __debounce
    /
    __debounce_500ms
    - Debounce execution
  • __throttle
    /
    __throttle_1s
    - Throttle execution
  • __delay
    /
    __delay_200ms
    - Delay execution
  • __debounce
    /
    __debounce_500ms
    - 防抖执行
  • __throttle
    /
    __throttle_1s
    - 节流执行
  • __delay
    /
    __delay_200ms
    - 延迟执行

Event Modifiers

事件修饰符

  • __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
  • __prevent
    - 调用
    preventDefault()
  • __stop
    - 调用
    stopPropagation()
  • __capture
    - 使用捕获阶段
  • __passive
    - 标记为被动监听器
  • __once
    - 仅执行一次
  • __self
    - 仅当事件目标为元素自身时触发
  • __outside
    - 事件发生在元素外部时触发
  • __window
    - 将监听器绑定到window

Example with Modifiers

修饰符示例

html
<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')">
html
<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')">

Server-Sent Events (SSE)

Server-Sent Events (SSE)

Datastar uses SSE for streaming responses. The backend sends events with
text/event-stream
content type.
Datastar使用SSE实现流式响应。后端发送
text/event-stream
内容类型的事件。

SSE Event Types

SSE事件类型

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

datastar-patch-elements - 将HTML修补到DOM中:
event: datastar-patch-elements
data: elements <div id="content">Updated content</div>
datastar-patch-signals - 更新信号值:
event: datastar-patch-signals
data: signals {count: 42}
datastar-remove-elements - 通过选择器移除元素:
event: datastar-remove-elements
data: selector #old-element

Part 2: gomponents-datastar

第二部分:gomponents-datastar

Overview

概述

gomponents-datastar provides Go functions that generate Datastar attributes as gomponents nodes. It integrates seamlessly with the gomponents library.
gomponents-datastar提供Go函数,用于生成作为gomponents节点的Datastar属性。它与gomponents库无缝集成。

Import Convention

导入约定

Use dot imports for a clean DSL:
go
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
使用点导入以获得简洁的DSL:
go
import (
    . "maragu.dev/gomponents"
    . "maragu.dev/gomponents/html"
    data "maragu.dev/gomponents-datastar"
)
注意:推荐使用
data
作为datastar的别名。
如需获取最新API文档,运行:
go doc maragu.dev/gomponents-datastar

Function Reference

函数参考

State Management

状态管理

Signals - Initialize reactive signals:
go
data.Signals(map[string]any{
    "count": 0,
    "name":  "World",
})
Computed - Create computed signals (key-value pairs):
go
data.Computed("doubled", "$count * 2")
Init - Run expression on load:
go
data.Init("console.log('Component loaded')")
Signals - 初始化响应式信号:
go
data.Signals(map[string]any{
    "count": 0,
    "name":  "World",
})
Computed - 创建计算信号(键值对):
go
data.Computed("doubled", "$count * 2")
Init - 加载时执行表达式:
go
data.Init("console.log('Component loaded')")

Data Binding

数据绑定

Text - Bind text content:
go
Span(data.Text("'Hello, ' + $name"))
Bind - Two-way form binding:
go
Input(Type("text"), data.Bind("$name"))
Show - Conditional visibility:
go
Div(data.Show("$isVisible"), Text("Shown when visible"))
Text - 绑定文本内容:
go
Span(data.Text("'Hello, ' + $name"))
Bind - 表单双向绑定:
go
Input(Type("text"), data.Bind("$name"))
Show - 条件性显示:
go
Div(data.Show("$isVisible"), Text("Shown when visible"))

Styling

样式控制

Class - Conditional classes (key-value pairs):
go
data.Class("active", "$isActive", "error", "$hasError")
Style - Dynamic inline styles (key-value pairs):
go
data.Style("color", "$textColor", "opacity", "$opacity")
Attr - Dynamic attributes (key-value pairs):
go
data.Attr("disabled", "$isLoading", "aria-busy", "$isLoading")
Class - 条件性类(键值对):
go
data.Class("active", "$isActive", "error", "$hasError")
Style - 动态内联样式(键值对):
go
data.Style("color", "$textColor", "opacity", "$opacity")
Attr - 动态属性(键值对):
go
data.Attr("disabled", "$isLoading", "aria-busy", "$isLoading")

Events

事件处理

On - Attach event listeners:
go
data.On("click", "$count++")
data.On("click", "@post('/submit')", data.ModifierPrevent)
data.On("input", "$search = evt.target.value", data.ModifierDebounce)
OnIntersect - Viewport intersection:
go
data.OnIntersect("@get('/load-more')")
OnInterval - Periodic execution:
go
data.OnInterval("$elapsed++")
OnSignalPatch - React to signal changes:
go
data.OnSignalPatch("console.log('Updated')")
On - 绑定事件监听器:
go
data.On("click", "$count++")
data.On("click", "@post('/submit')", data.ModifierPrevent)
data.On("input", "$search = evt.target.value", data.ModifierDebounce)
OnIntersect - 视口交叉触发:
go
data.OnIntersect("@get('/load-more')")
OnInterval - 周期性执行:
go
data.OnInterval("$elapsed++")
OnSignalPatch - 响应信号变化:
go
data.OnSignalPatch("console.log('Updated')")

DOM Control

DOM控制

Ref - Reference DOM element:
go
data.Ref("$inputEl")
Ignore - Skip Datastar processing:
go
data.Ignore()
IgnoreMorph - Skip morphing only:
go
data.IgnoreMorph()
PreserveAttr - Preserve attributes during morph:
go
data.PreserveAttr("value", "checked")
Ref - 引用DOM元素:
go
data.Ref("$inputEl")
Ignore - 跳过Datastar处理:
go
data.Ignore()
IgnoreMorph - 仅跳过形态匹配:
go
data.IgnoreMorph()
PreserveAttr - 形态匹配期间保留属性:
go
data.PreserveAttr("value", "checked")

Request Helpers

请求助手

Indicator - Show loading state:
go
data.Indicator("$isLoading")
JSONSignals - Control which signals are sent:
go
data.JSONSignals(data.Filter{Include: "form.*"})
data.JSONSignals(data.Filter{Exclude: "internal.*"})
Indicator - 显示加载状态:
go
data.Indicator("$isLoading")
JSONSignals - 控制发送的信号:
go
data.JSONSignals(data.Filter{Include: "form.*"})
data.JSONSignals(data.Filter{Exclude: "internal.*"})

Modifiers

修饰符

Use modifier constants with event functions:
go
// 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
在事件函数中使用修饰符常量:
go
// 时序修饰符
data.ModifierDebounce  // __debounce
data.ModifierThrottle  // __throttle
data.ModifierDelay     // __delay

// 事件行为修饰符
data.ModifierPrevent   // __prevent
data.ModifierStop      // __stop
data.ModifierCapture   // __capture
data.ModifierPassive   // __passive
data.ModifierOnce      // __once
data.ModifierSelf      // __self
data.ModifierOutside   // __outside
data.ModifierWindow    // __window

// 时长/阈值助手
data.Duration(500 * time.Millisecond)  // __500ms
data.Threshold(0.5)                     // __threshold_0.5

Complete Example

完整示例

go
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'"),
        ),
    )
}
go
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'"),
        ),
    )
}

SSE Handler Pattern

SSE处理器模式

go
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()
}
go
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()
}

Tips

技巧提示

  1. Signal naming: Use
    $
    prefix in expressions, not in Go code
  2. Avoid conflicts: Use
    data.Text()
    for Datastar text binding, gomponents
    Text()
    for static content
  3. Modifiers: Chain multiple modifiers:
    data.On("click", "...", data.ModifierPrevent, data.ModifierOnce)
  4. SSE IDs: Elements patched via SSE need matching
    id
    attributes
  5. Morphing: Datastar preserves form input state during DOM updates by default
  1. 信号命名: 在表达式中使用
    $
    前缀,但在Go代码中不要添加
  2. 避免冲突: 使用
    data.Text()
    进行Datastar文本绑定,使用gomponents的
    Text()
    处理静态内容
  3. 修饰符组合: 可链式使用多个修饰符:
    data.On("click", "...", data.ModifierPrevent, data.ModifierOnce)
  4. SSE元素ID: 通过SSE修补的元素需要匹配的
    id
    属性
  5. 形态匹配: Datastar默认在DOM更新期间保留表单输入状态