Loading...
Loading...
WHEN: User is building Go/Templ web apps, using templUI components, converting sites to Templ, or asking about templ syntax, Script() templates, HTMX/Alpine integration, or JavaScript in templ WHEN NOT: Non-Go projects, general web development without templ
npx skill4agent add gopherguides/gopher-ai templui| 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) |
<!-- 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><script src="https://unpkg.com/htmx.org/dist/ext/alpine-morph.js"></script>
<div hx-ext="alpine-morph" hx-swap="morph">...</div>x-if<template x-if="showForm">
<form hx-post="/submit" x-init="htmx.process($el)">...</form>
</template><button @click="htmx.trigger($refs.form, 'submit')">Submit</button>{ value }<script>GET http://localhost:8008/app/quotes/%7B%20id.String()%20%7D 400 (Bad Request)%7B%7D{}data-*<button
data-quote-id={ quote.ID.String() }
onclick="openPublishModal(this.dataset.quoteId)">
Publish
</button><div
data-id={ item.ID.String() }
data-name={ item.Name }
data-status={ item.Status }
onclick="handleClick(this.dataset)"><button onclick={ templ.JSFuncCall("openPublishModal", quote.ID.String()) }>
Publish
</button><button onclick={ templ.JSFuncCall("updateItem", item.ID.String(), item.Name, item.Active) }>templ.JSExpression<button onclick={ templ.JSFuncCall("handleClick", templ.JSExpression("event"), quote.ID.String()) }><script>{{ value }}<script>
const quoteId = "{{ quote.ID.String() }}";
const itemName = "{{ item.Name }}";
openPublishModal(quoteId);
</script><script>
const config = {{ templ.JSONString(config) }};
const isActive = {{ item.Active }}; // outputs: true or false
</script><div data-config={ templ.JSONString(config) }>
<script>
const el = document.querySelector('[data-config]');
const config = JSON.parse(el.dataset.config);
</script>templ.JSONScript@templ.JSONScript("config-data", config)
<script>
const config = JSON.parse(document.getElementById('config-data').textContent);
</script>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>
}| 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 | |
// 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)">go install github.com/templui/templui/cmd/templui@latesttemplui 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 versionScript()<head>// 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: copybutton| Component | Requires Script() from |
|---|---|
| dropdown | dropdown, popover |
| tooltip | popover |
| combobox | popover |
| sheet | dialog |
| alertdialog | dialog |
| collapsible | accordion |
classclassclassNameclass{ variable }@component()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
}templui add@popover.Script()@dialog.Script()import "github.com/templui/templui/components/button"
import "github.com/templui/templui/components/dropdown"
import "github.com/templui/templui/components/dialog"{%7B<script>// WRONG: <script>fetch(`/api/{ id }`)</script>
// RIGHT:
<button data-id={ id } onclick="doFetch(this.dataset.id)">@dropdown.Script()@popover.Script()templui add -f dropdown popover@popover.Script()templui add -f popover@dialog.Script()templui add -f dialog