templui
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
Chinese<!-- cache:start -->
<!-- cache:start -->
templUI & HTMX/Alpine Best Practices
templUI & HTMX/Alpine 最佳实践
Apply templUI patterns and HTMX/Alpine.js best practices when building Go/Templ web applications.
在构建Go/Templ Web应用时,遵循templUI模式与HTMX/Alpine.js最佳实践。
The Frontend Stack
前端技术栈
The Go/Templ stack uses three complementary tools for interactivity:
| Tool | Purpose | Use For |
|---|---|---|
| HTMX | Server-driven interactions | AJAX requests, form submissions, partial page updates, live search |
| Alpine.js | Client-side state & reactivity | Toggles, animations, client-side filtering, transitions, local state |
| templUI | Pre-built UI components | Dropdowns, dialogs, tabs, sidebars (uses vanilla JS via Script() templates) |
Note: templUI components use vanilla JavaScript (not Alpine.js) via Script() templates. This is fine - Alpine.js is still part of the stack for your custom client-side needs.
Go/Templ技术栈使用三个互补工具实现交互性:
| 工具 | 用途 | 适用场景 |
|---|---|---|
| HTMX | 服务器驱动交互 | AJAX请求、表单提交、页面局部更新、实时搜索 |
| Alpine.js | 客户端状态与响应式处理 | 开关控制、动画、客户端筛选、过渡效果、本地状态管理 |
| templUI | 预构建UI组件 | 下拉菜单、对话框、标签页、侧边栏(通过Script()模板使用原生JavaScript) |
注意: templUI组件通过Script()模板使用原生JavaScript(而非Alpine.js)。这是正常的——Alpine.js仍可用于满足你的自定义客户端需求,属于技术栈的一部分。
HTMX + Alpine.js Integration
HTMX + Alpine.js 集成
HTMX and Alpine.js work great together. Use HTMX for server communication, Alpine for client-side enhancements.
HTMX与Alpine.js可以完美协作。使用HTMX处理服务器通信,使用Alpine.js实现客户端增强。
When to Use Each
适用场景区分
html
<!-- HTMX: Server-driven (fetches HTML from server) -->
<button hx-get="/api/users" hx-target="#user-list">Load Users</button>
<!-- Alpine: Client-side state (no server call) -->
<div x-data="{ open: false }">
<button @click="open = !open">Toggle</button>
<div x-show="open">Content</div>
</div>
<!-- Combined: HTMX loads data, Alpine filters it -->
<div x-data="{ filter: '' }">
<input x-model="filter" placeholder="Filter...">
<div hx-get="/users" hx-trigger="load">
<template x-for="user in users.filter(u => u.name.includes(filter))">
<div x-text="user.name"></div>
</template>
</div>
</div>html
<!-- HTMX:服务器驱动交互(从服务器获取HTML) -->
<button hx-get="/api/users" hx-target="#user-list">加载用户</button>
<!-- Alpine:客户端状态管理(无需服务器调用) -->
<div x-data="{ open: false }">
<button @click="open = !open">切换</button>
<div x-show="open">内容</div>
</div>
<!-- 结合使用:HTMX加载数据,Alpine进行筛选 -->
<div x-data="{ filter: '' }">
<input x-model="filter" placeholder="筛选...">
<div hx-get="/users" hx-trigger="load">
<template x-for="user in users.filter(u => u.name.includes(filter))">
<div x-text="user.name"></div>
</template>
</div>
</div>Key Integration Patterns
核心集成模式
Alpine-Morph Extension: Preserves Alpine state across HTMX swaps:
html
<script src="https://unpkg.com/htmx.org/dist/ext/alpine-morph.js"></script>
<div hx-ext="alpine-morph" hx-swap="morph">...</div>htmx.process() for Alpine Conditionals: When Alpine's renders HTMX content:
x-ifhtml
<template x-if="showForm">
<form hx-post="/submit" x-init="htmx.process($el)">...</form>
</template>Triggering HTMX from Alpine:
html
<button @click="htmx.trigger($refs.form, 'submit')">Submit</button>Alpine-Morph扩展:在HTMX内容替换时保留Alpine状态:
html
<script src="https://unpkg.com/htmx.org/dist/ext/alpine-morph.js"></script>
<div hx-ext="alpine-morph" hx-swap="morph">...</div>htmx.process()处理Alpine条件渲染:当Alpine的渲染HTMX内容时:
x-ifhtml
<template x-if="showForm">
<form hx-post="/submit" x-init="htmx.process($el)">...</form>
</template>从Alpine触发HTMX操作:
html
<button @click="htmx.trigger($refs.form, 'submit')">提交</button>templUI Components (Vanilla JS)
templUI 组件(原生JavaScript)
templUI components handle their own interactivity via Script() templates using vanilla JavaScript and Floating UI for positioning.
templUI组件通过Script()模板使用原生JavaScript和Floating UI进行定位,自行处理交互逻辑。
CRITICAL: Templ Interpolation in JavaScript
重点注意:Templ中的JavaScript插值
Go expressions do NOT interpolate inside tags or inline event handlers. They are treated as literal text, causing errors like:
{ value }<script>GET http://localhost:8008/app/quotes/%7B%20id.String()%20%7D 400 (Bad Request)The and are URL-encoded and - proof the expression wasn't evaluated.
%7B%7D{}Go表达式在标签或内联事件处理程序中不会被插值,它们会被当作字面文本处理,导致如下错误:
{ value }<script>GET http://localhost:8008/app/quotes/%7B%20id.String()%20%7D 400 (Bad Request)其中和是URL编码后的和——这证明表达式未被解析。
%7B%7D{}Pattern 1: Data Attributes (Recommended)
模式1:数据属性(推荐)
Use attributes to pass Go values, then access via JavaScript:
data-*templ
<button
data-quote-id={ quote.ID.String() }
onclick="openPublishModal(this.dataset.quoteId)">
Publish
</button>For multiple values:
templ
<div
data-id={ item.ID.String() }
data-name={ item.Name }
data-status={ item.Status }
onclick="handleClick(this.dataset)">使用属性传递Go值,然后通过JavaScript访问:
data-*templ
<button
data-quote-id={ quote.ID.String() }
onclick="openPublishModal(this.dataset.quoteId)">
发布
</button>传递多个值:
templ
<div
data-id={ item.ID.String() }
data-name={ item.Name }
data-status={ item.Status }
onclick="handleClick(this.dataset)">Pattern 2: templ.JSFuncCall (for onclick handlers)
模式2:templ.JSFuncCall(用于点击事件处理)
Automatically JSON-encodes arguments and prevents XSS:
templ
<button onclick={ templ.JSFuncCall("openPublishModal", quote.ID.String()) }>
Publish
</button>With multiple arguments:
templ
<button onclick={ templ.JSFuncCall("updateItem", item.ID.String(), item.Name, item.Active) }>To pass the event object, use :
templ.JSExpressiontempl
<button onclick={ templ.JSFuncCall("handleClick", templ.JSExpression("event"), quote.ID.String()) }>自动对参数进行JSON编码并防止XSS攻击:
templ
<button onclick={ templ.JSFuncCall("openPublishModal", quote.ID.String()) }>
发布
</button>传递多个参数:
templ
<button onclick={ templ.JSFuncCall("updateItem", item.ID.String(), item.Name, item.Active) }>需要传递事件对象时,使用:
templ.JSExpressiontempl
<button onclick={ templ.JSFuncCall("handleClick", templ.JSExpression("event"), quote.ID.String()) }>Pattern 3: Double-Braces Inside Script Strings
模式3:脚本字符串中的双括号
Inside tags, use (double braces) for interpolation:
<script>{{ value }}templ
<script>
const quoteId = "{{ quote.ID.String() }}";
const itemName = "{{ item.Name }}";
openPublishModal(quoteId);
</script>Outside strings (bare expressions), values are JSON-encoded:
templ
<script>
const config = {{ templ.JSONString(config) }};
const isActive = {{ item.Active }}; // outputs: true or false
</script>在标签内,使用(双括号)进行插值:
<script>{{ value }}templ
<script>
const quoteId = "{{ quote.ID.String() }}";
const itemName = "{{ item.Name }}";
openPublishModal(quoteId);
</script>在字符串外(裸表达式),值会被JSON编码:
templ
<script>
const config = {{ templ.JSONString(config) }};
const isActive = {{ item.Active }}; // 输出:true 或 false
</script>Pattern 4: templ.JSONString for Complex Data
模式4:templ.JSONString传递复杂数据
Pass complex structs/maps to JavaScript via attributes:
templ
<div data-config={ templ.JSONString(config) }>
<script>
const el = document.querySelector('[data-config]');
const config = JSON.parse(el.dataset.config);
</script>Or use :
templ.JSONScripttempl
@templ.JSONScript("config-data", config)
<script>
const config = JSON.parse(document.getElementById('config-data').textContent);
</script>通过属性将复杂结构体/映射传递给JavaScript:
templ
<div data-config={ templ.JSONString(config) }>
<script>
const el = document.querySelector('[data-config]');
const config = JSON.parse(el.dataset.config);
</script>或者使用:
templ.JSONScripttempl
@templ.JSONScript("config-data", config)
<script>
const config = JSON.parse(document.getElementById('config-data').textContent);
</script>Pattern 5: templ.OnceHandle for Reusable Scripts
模式5:templ.OnceHandle复用脚本
Ensures scripts are only rendered once, even when component is used multiple times:
templ
var publishHandle = templ.NewOnceHandle()
templ QuoteRow(quote Quote) {
@publishHandle.Once() {
<script>
function openPublishModal(id) {
fetch(`/api/quotes/${id}/publish`, { method: 'POST' });
}
</script>
}
<button
data-id={ quote.ID.String() }
onclick="openPublishModal(this.dataset.id)">
Publish
</button>
}确保脚本仅渲染一次,即使组件被多次使用:
templ
var publishHandle = templ.NewOnceHandle()
templ QuoteRow(quote Quote) {
@publishHandle.Once() {
<script>
function openPublishModal(id) {
fetch(`/api/quotes/${id}/publish`, { method: 'POST' });
}
</script>
}
<button
data-id={ quote.ID.String() }
onclick="openPublishModal(this.dataset.id)">
发布
</button>
}When to Use Each Pattern
各模式适用场景
| Scenario | Use |
|---|---|
| Simple onclick with one value | Data attribute or |
| Multiple values needed in JS | Data attributes |
| Need event object | |
| Inline script with Go values | |
| Complex object/struct | |
| Reusable script in loop | |
| 场景 | 推荐使用 |
|---|---|
| 带单个值的简单点击事件 | 数据属性或 |
| JavaScript中需要多个值 | 数据属性 |
| 需要传递事件对象 | 结合 |
| 内联脚本中使用Go值 | |
| 复杂对象/结构体 | |
| 循环中复用脚本 | |
Common Mistakes
常见错误
templ
// WRONG - won't interpolate, becomes literal text
onclick="doThing({ id })"
// WRONG - single braces don't work in scripts
<script>const x = { value };</script>
// WRONG - Go expression in URL string inside script
<script>
fetch(`/api/quotes/{ id }/publish`) // BROKEN
</script>
// CORRECT alternatives:
onclick={ templ.JSFuncCall("doThing", id) }
<script>const x = "{{ value }}";</script>
<button data-id={ id } onclick="doFetch(this.dataset.id)">templ
// 错误写法 - 不会插值,会变成字面文本
onclick="doThing({ id })"
// 错误写法 - 单括号在脚本中无效
<script>const x = { value };</script>
// 错误写法 - 脚本中的URL字符串包含Go表达式
<script>
fetch(`/api/quotes/{ id }/publish`) // 失效
</script>
// 正确写法:
onclick={ templ.JSFuncCall("doThing", id) }
<script>const x = "{{ value }}";</script>
<button data-id={ id } onclick="doFetch(this.dataset.id)">templUI CLI Tool
templUI CLI工具
Install CLI:
bash
go install github.com/templui/templui/cmd/templui@latestKey Commands:
bash
templui init # Initialize project, creates .templui.json
templui add button card # Add specific components
templui add "*" # Add ALL components
templui add -f dropdown # Force update existing component
templui list # List available components
templui new my-app # Create new project
templui upgrade # Update CLI to latest versionALWAYS use the CLI to add/update components - it fetches the complete component including Script() templates that may be missing if copied manually.
安装CLI:
bash
go install github.com/templui/templui/cmd/templui@latest核心命令:
bash
templui init # 初始化项目,创建.templui.json文件
templui add button card # 添加指定组件
templui add "*" # 添加所有组件
templui add -f dropdown # 强制更新现有组件
templui list # 列出可用组件
templui new my-app # 创建新项目
templui upgrade # 将CLI更新到最新版本务必使用CLI添加/更新组件——它会获取完整的组件,包括手动复制可能缺失的Script()模板。
Script() Templates - REQUIRED for Interactive Components
Script()模板 - 交互式组件必备
Components with JavaScript include a template function. You MUST add these to your base layout's :
Script()<head>templ
// In your base layout <head>:
@popover.Script() // Required for: popover, dropdown, tooltip, combobox
@dropdown.Script() // Required for: dropdown
@dialog.Script() // Required for: dialog, sheet, alertdialog
@accordion.Script() // Required for: accordion, collapsible
@tabs.Script() // Required for: tabs
@carousel.Script() // Required for: carousel
@toast.Script() // Required for: toast/sonner
@clipboard.Script() // Required for: copybuttonComponent Dependencies:
| Component | Requires Script() from |
|---|---|
| dropdown | dropdown, popover |
| tooltip | popover |
| combobox | popover |
| sheet | dialog |
| alertdialog | dialog |
| collapsible | accordion |
If a component doesn't work (no click events, no positioning), check that:
- The Script() template is called in the layout
- The component was installed via CLI (not manually copied)
- All dependency scripts are included
包含JavaScript的组件会有一个模板函数。必须将这些函数添加到基础布局的中:
Script()<head>templ
// 在你的基础布局<head>中:
@popover.Script() // 以下组件需要:弹出框、下拉菜单、提示框、组合框
@dropdown.Script() // 下拉菜单需要
@dialog.Script() // 以下组件需要:对话框、侧边面板、警告对话框
@accordion.Script() // 以下组件需要:折叠面板、可折叠区域
@tabs.Script() // 标签页需要
@carousel.Script() // 轮播图需要
@toast.Script() // 以下组件需要:提示消息/Sonner
@clipboard.Script() // 复制按钮需要组件依赖关系:
| 组件 | 依赖的Script() |
|---|---|
| dropdown | dropdown, popover |
| tooltip | popover |
| combobox | popover |
| sheet | dialog |
| alertdialog | dialog |
| collapsible | accordion |
如果组件无法正常工作(无点击事件、定位错误),请检查:
- 基础布局中是否调用了所需的Script()
- 组件是否通过CLI安装(而非手动复制)
- 是否包含了所有依赖脚本
Converting Sites to Templ/templUI
将网站迁移到Templ/templUI
When converting HTML/React/Vue to Go/Templ:
Conversion Process:
- Analyze existing UI patterns
- Map to templUI base components
- Convert syntax:
- stays as
classin templclass - (React) →
classNameclass - React/Vue event handlers → vanilla JS via Script() or HTMX
- Dynamic content → templ expressions or
{ variable }@component()
- Add required Script() templates to layout
- Set up proper Go package structure
Templ Syntax Quick Reference:
templ
package components
type ButtonProps struct {
Text string
Variant string
}
templ Button(props ButtonProps) {
<button class={ "btn", props.Variant }>
{ props.Text }
</button>
}
// Conditional
if condition {
<span>Shown</span>
}
// Loops
for _, item := range items {
<li>{ item.Name }</li>
}
// Composition
@Header()
@Content() {
// Children
}将HTML/React/Vue项目迁移到Go/Templ时:
迁移流程:
- 分析现有UI模式
- 映射到templUI基础组件
- 转换语法:
- 在templ中保持为
classclass - React的→
classNameclass - React/Vue事件处理程序 → 通过Script()或HTMX使用原生JavaScript
- 动态内容 → templ表达式或
{ variable }@component()
- 将所需的Script()模板添加到布局中
- 搭建正确的Go包结构
Templ语法快速参考:
templ
package components
type ButtonProps struct {
Text string
Variant string
}
templ Button(props ButtonProps) {
<button class={ "btn", props.Variant }>
{ props.Text }
</button>
}
// 条件渲染
if condition {
<span>显示内容</span>
}
// 循环渲染
for _, item := range items {
<li>{ item.Name }</li>
}
// 组件组合
@Header()
@Content() {
// 子内容
}Auditing for Better Component Usage
组件使用优化审计
Audit Checklist:
- Script() Templates: Are all required Script() calls in the base layout?
- CLI Installation: Were components added via or manually copied?
templui add - Component Consistency: Same patterns using same components?
- Base Component Usage: Custom code that could use templUI?
- Dark Mode: Tailwind dark: variants used?
- Responsive: Mobile breakpoints applied?
Common Issues to Check:
- Missing → dropdowns/tooltips don't open
@popover.Script() - Missing → dialogs/sheets don't work
@dialog.Script() - Manually copied components missing Script() template files
审计清单:
- Script()模板:基础布局中是否包含所有必需的Script()调用?
- CLI安装:组件是通过添加还是手动复制的?
templui add - 组件一致性:相同场景是否使用相同组件?
- 基础组件复用:自定义代码是否可以替换为templUI组件?
- 深色模式:是否使用了Tailwind的dark:变体?
- 响应式设计:是否适配了移动端断点?
常见问题检查:
- 缺少→ 下拉菜单/提示框无法打开
@popover.Script() - 缺少→ 对话框/侧边面板无法工作
@dialog.Script() - 手动复制的组件缺失Script()模板文件
Import Pattern
导入模式
go
import "github.com/templui/templui/components/button"
import "github.com/templui/templui/components/dropdown"
import "github.com/templui/templui/components/dialog"go
import "github.com/templui/templui/components/button"
import "github.com/templui/templui/components/dropdown"
import "github.com/templui/templui/components/dialog"Troubleshooting
故障排查
JavaScript URL contains literal or (URL-encoded brace):
Go expressions don't interpolate in tags. Use data attributes:
{%7B<script>templ
// WRONG: <script>fetch(`/api/{ id }`)</script>
// RIGHT:
<button data-id={ id } onclick="doFetch(this.dataset.id)">See "CRITICAL: Templ Interpolation in JavaScript" section above.
Component not responding to clicks:
- Check Script() is in layout: ,
@dropdown.Script()@popover.Script() - Reinstall:
templui add -f dropdown popover - Check browser console for JS errors
Dropdown/Tooltip not positioning correctly:
- Ensure is in layout (uses Floating UI)
@popover.Script() - Reinstall popover:
templui add -f popover
Dialog/Sheet not opening:
- Add to layout
@dialog.Script() - Reinstall:
templui add -f dialog
JavaScript URL中包含字面量或(URL编码后的大括号):
Go表达式在标签中不会被插值,请使用数据属性:
{%7B<script>templ
// 错误写法:<script>fetch(`/api/{ id }`)</script>
// 正确写法:
<button data-id={ id } onclick="doFetch(this.dataset.id)">请参考上方“重点注意:Templ中的JavaScript插值”章节。
组件无点击响应:
- 检查布局中是否包含Script():、
@dropdown.Script()@popover.Script() - 重新安装组件:
templui add -f dropdown popover - 检查浏览器控制台的JavaScript错误
下拉菜单/提示框定位错误:
- 确保布局中包含(依赖Floating UI)
@popover.Script() - 重新安装弹出框组件:
templui add -f popover
对话框/侧边面板无法打开:
- 将添加到布局中
@dialog.Script() - 重新安装组件:
templui add -f dialog
Resources
参考资源
templUI:
- Documentation: https://templui.io/docs
- GitHub: https://github.com/templui/templui
HTMX + Alpine.js:
- HTMX and Alpine.js: How to combine two great, lean front ends
- Full-Stack Go App with HTMX and Alpine.js
- When to Add Alpine.js to htmx
- HTMX Alpine-Morph Extension
Templ:
- Templ Docs: https://templ.guide
This skill provides templUI and HTMX/Alpine.js best practices for Go/Templ web development.
<!-- cache:end -->templUI:
HTMX + Alpine.js:
Templ:
- Templ文档:https://templ.guide
本技能为Go/Templ Web开发提供templUI与HTMX/Alpine.js的最佳实践指导。
<!-- cache:end -->