| name | templ |
| description | templ templating: syntax, components, attributes, styling, and JavaScript integration. Invoke when task involves any interaction with templ โ writing .templ files, creating components, composing templates, testing rendered output, or understanding templ syntax. |
templ
Type-safe Go HTML templating. Components defined in .templ files compile to Go functions returning templ.Component
via templ generate. Outside templ blocks = ordinary Go. Inside = templ syntax.
References
Extended examples and detailed patterns for the rules below:
${CLAUDE_SKILL_DIR}/references/syntax.md โ Template syntax, expressions, control flow, raw Go blocks: file
structure, expression types, error propagation, auto-escaping, control flow examples
${CLAUDE_SKILL_DIR}/references/components.md โ Component definition, composition, children, fragments: component
interface, @ composition, children context API, render-once, fragment rendering
${CLAUDE_SKILL_DIR}/references/attributes.md โ Boolean, conditional, spread attributes, key expressions: attribute
types with code examples, spread value behavior, URL/JS/JSON attribute patterns
${CLAUDE_SKILL_DIR}/references/patterns.md โ View models, layouts, context, html/template interop: props struct
pattern, nested layouts, context helpers with middleware, Go template interop
${CLAUDE_SKILL_DIR}/references/javascript.md โ Script/style tags, inline events, data passing to JS:
JSFuncCall/JSExpression/JSONString/JSONScript API, IIFE pattern, method summary
${CLAUDE_SKILL_DIR}/references/styling.md โ Class patterns, CSS components, style attributes: class toggling
approaches (KV, maps, raw Go), CSS component scoping, style sanitization
Syntax
Expressions { }
Output Go values inside templ blocks. Content is automatically HTML-escaped for XSS safety.
Supported types: string, numbers (int, uint, float32, complex64, etc.), booleans, and any type based on these
(e.g. type Name string).
Use variables, field access, and function calls: { name }, { p.Name }, { strings.ToUpper(name) },
{ fmt.Sprintf("%d items", count) }.
Functions returning (T, error) propagate errors to Render() with source location info.
Elements
All tags must close. Write <br/> not <br>. templ is aware of void elements and strips / in output HTML, but source
must always include it.
templ automatically minifies HTML output.
Control Flow
Use bare Go keywords โ if/else, switch/case, for/range. No special syntax.
Text starting with if, for, or switch triggers the parser. Two solutions:
- Wrap as expression:
{ "if you need this text" }
- Capitalize the keyword:
If you need this text
Raw Go {{ }}
Scoped Go statements inside templ blocks for intermediate variables.
{{ total := calculateTotal(items) }}
<p>Total: { fmt.Sprintf("%d", total) }</p>
Use {{ }} to avoid calling expensive functions twice โ cache results in a variable.
Comments
- Inside templ blocks: use HTML comments
<!-- --> (rendered to output). No nesting.
- Outside templ blocks: use Go comments
// (not rendered).
Implicit Variables
Every component has two implicit variables:
ctx โ context.Context from the Render call. Available in all components.
children โ content passed via @component() { ... }. Access with { children... }.
Components
Definition and Visibility
Components compile to Go functions returning templ.Component. Follow Go visibility rules: uppercase name = exported,
lowercase = unexported.
The templ.Component interface: Render(ctx context.Context, w io.Writer) error.
Partial output warning: a component may write partial output to io.Writer before returning an error. To guarantee
all-or-nothing, render to a buffer first.
Composition
Call components with @ prefix: @header(), @components.Header(), @nav.Item("Home", "/").
Children
Pass content to a component with @layout() { <p>children</p> }. Receive with { children... } in the component body.
In code-only components, manage children via context: templ.WithChildren, templ.GetChildren, templ.ClearChildren.
Components as Parameters
Pass components as values via templ.Component type parameters, render with @param.
Joining Components
Aggregate multiple components into one with @templ.Join(header(), nav(), footer()).
Method Components
Attach components to types when a component has many configuration options โ struct fields are self-documenting and can
have defaults. Call inline: @Button{Text: "Submit", Variant: "primary"}.Render().
Code-Only Components
Implement templ.Component in pure Go using templ.ComponentFunc:
func button(text string) templ.Component {
return templ.ComponentFunc(func(ctx context.Context, w io.Writer) error {
_, err := io.WriteString(w, "<button>"+templ.EscapeString(text)+"</button>")
return err
})
}
In code-only components, you must escape HTML yourself with templ.EscapeString. Auto- escaping only applies inside
.templ files.
Render-Once
Ensure content renders once per HTTP response (or per context). Common use: shared <script>, <style>, or <link>
tags.
- Declare handles at package level:
var h = templ.NewOnceHandle().
- Use in component:
@h.Once() { <script src="..."></script> }.
- Never inline
@templ.NewOnceHandle().Once() โ creates a new handle each call, content renders every time,
defeating the purpose.
For cross-package shared dependencies, export render-once components โ wrap the handle and Once() call in an exported
templ function, then call from any package.
Fragments
Render subsections of templates, discarding all other output. The full template still executes (all logic runs), but
only the fragment's output is written.
- Define:
@templ.Fragment("content") { <div>fragment</div> }
- Render via HTTP:
templ.Handler(Page(), templ.WithFragments("content"))
- Render without HTTP:
templ.RenderFragments(ctx, w, Page(), "content")
Custom fragment keys: use typed keys (type contentKey struct{}) to avoid name clashes.
Nested fragments: selecting outer includes inner. Useful for partial page updates (e.g. with htmx).
Attributes
Constant Attributes
Standard HTML attributes with double quotes: <p class="container" data-testid="p">.
Dynamic String Attributes
Set to Go expressions with { }: <div class={ className }>, <div data-id={ fmt.Sprintf("item-%d", id) }>.
String values are automatically HTML-attribute-encoded. Functions returning (string, error) propagate errors to
Render().
Boolean Attributes ?=
Presence/absence based on Go boolean: <input disabled?={ isDisabled }/>, <button hidden?={ !showButton }/>. Static
booleans: <hr noshade/>.
Conditional Attributes
Use if inside element open tags to conditionally add attributes. The conditional attribute replaces the earlier
one of the same name โ include base classes in both branches.
Attribute Key Expressions
Dynamically set the attribute key: <p { "data-" + suffix }="value">.
Warning: key expressions don't get type-specific handling. URL attributes (href) and event handlers (on*)
defined via key expressions are treated as plain strings without special sanitization.
Spread Attributes
Append a dynamic map with { attrs... } where attrs is templ.Attributes (map[string]any).
Value rendering by type:
string โ name="value"
bool โ name (if true) or omitted (if false)
templ.KeyValue[string, bool] โ name="value" if bool is true
templ.KeyValue[bool, bool] โ name if both bools are true
Spread attributes can be conditional using if inside element open tags.
Never mutate a global templ.Attributes var โ create fresh templ.Attributes{} per render call.
Security
HTML Auto-Escaping
All expressions { } are HTML-escaped. Use @templ.Raw() only for trusted content.
URL Sanitization
href, src, action auto-sanitize dynamic values โ javascript: schemes become
about:invalid#TemplFailedSanitizationURL. Bypass with templ.SafeURL() for trusted URLs.
Constant URL values are NOT sanitized: <a href="javascript:..."> renders as-is.
For non-standard URL attributes (e.g. htmx hx-get), use templ.URL() which sanitizes without the special
href/src/action behavior.
CSS Sanitization
Dynamic CSS values are sanitized by default. Unsafe property names become zTemplUnsafeCSSPropertyName, unsafe values
become zTemplUnsafeCSSPropertyValue. Bypass with templ.SafeCSS (full declaration) or templ.SafeCSSProperty (single
value).
JS Sanitization
Function names in templ.JSFuncCall are sanitized โ invalid names become __templ_invalid_function_name.
templ.JSExpression bypasses encoding entirely โ only use with trusted compile-time constants like "event" or
"this". templ.JSUnsafeFuncCall skips function name sanitization โ never use with user-provided input.
Styling
Class Attribute
Multiple approaches for conditional classes, ordered by simplicity:
- Static string โ
class="button primary": unchanging classes
- Dynamic expression โ
class={ className }: single dynamic class
- Multiple values โ
class={ "button", className }: combining static + dynamic
templ.KV โ class={ "btn", templ.KV("active", isActive) }: conditional single class
map[string]bool โ class={ map[string]bool{"tab": true, "active": isActive} }: multiple conditional
- Raw Go
{{ }} โ compute class string in {{ }}, use in class={ computed }: complex logic
- Conditional attribute โ
if cond { class="full-set" } in open tag: replacing full value
CSS component functions (from css blocks) can be used in class expressions:
class={ "button", templ.KV(primaryButton(), isPrimary) }.
Style Attribute
Dynamic styles accept multiple values combined in output: style={ style1, style2 }.
Supported value types:
string โ e.g. "background-color: red"
templ.SafeCSS โ bypasses sanitization
map[string]string โ e.g. map[string]string{"color": "red"}
map[string]templ.SafeCSSProperty โ map with unsanitized values
templ.KeyValue[string, bool] โ conditional: include CSS string if true
templ.KeyValue[templ.SafeCSS, bool] โ conditional unsanitized CSS
- Functions returning any above โ single function may return
(T, error)
Use templ.KV("border-color: red", hasError) for conditional style toggling.
Use map[string]string for computed style sets from Go functions.
CSS Components
Define scoped CSS with auto-generated hash-based class names:
css primaryButton() {
background-color: #ffffff;
color: { red };
}
Key behaviors:
- Class names are auto-generated (hash-based) โ don't rely on them being stable
- CSS is rendered as
<style> tags, once per HTTP request per unique class
- Dynamic values inside
css blocks use { expr } syntax
- CSS components accept arguments โ each unique argument combination generates a separate class
- Dynamic property names/values are sanitized; bypass with
templ.SafeCSSProperty
Raw <style> Elements
Raw <style> tags render without modification. Use CSS components instead if you need once-per-request deduplication.
CSS Middleware
templ.NewCSSMiddleware serves a global stylesheet instead of inline <style> tags. See
${CLAUDE_SKILL_DIR}/references/styling.md.
JavaScript Integration
Script Tags
Standard <script> tags for client-side JavaScript. Use templ.OnceHandle to render a script only once per response.
Passing Data: Go to JavaScript
Three approaches, ordered by preference:
- Data attributes โ
data-config={ templ.JSONString(data) }: component-scoped data
- Script elements โ
@templ.JSONScript("id", data): page-level configuration
- Inline interpolation โ
{{ value }} in <script>: least preferred โ mixing data/code
templ.JSFuncCall
Call a client-side function with server-side data. Arguments are JSON-encoded.
Use in attributes: <button onclick={ templ.JSFuncCall("alert", msg) }>. Use as standalone script:
@templ.JSFuncCall("initApp", config.Name) renders <script>initApp("MyApp");</script>.
templ.JSExpression
Bypass JSON encoding for raw JS expressions: templ.JSExpression("event"), templ.JSExpression("this"). Only use
with trusted compile-time constants โ output goes directly to HTML without encoding.
templ.JSONString
Encode Go data as JSON string for HTML attributes: <div x-data={ templ.JSONString(data) }>. Client reads with
JSON.parse(el.getAttribute('attr')).
templ.JSONScript
Create <script type="application/json"> element: @templ.JSONScript("id", data). Client reads with
JSON.parse(document.getElementById('id').textContent).
Inline {{ }} Interpolation in Scripts
Inside JS strings: value is string-escaped. Outside JS strings: value is JSON-encoded. templ auto-escapes both. Prefer
templ.JSONString or templ.JSONScript over inline interpolation โ separating data from code is easier to maintain.
templ.JSUnsafeFuncCall
Identical to templ.JSFuncCall but skips function name sanitization. Arguments still JSON-encoded. Never use with
user-provided input.
IIFE Pattern
Prevent variable leaking into global scope by wrapping in (() => { ... })(). Use document.currentScript for DOM
traversal relative to the script element.
Best Practice: Avoiding Inline Event Handlers
Separate behavior from markup: load shared functions via templ.OnceHandle, pass data via data-* attributes, isolate
in IIFEs. See ${CLAUDE_SKILL_DIR}/references/javascript.md.
External Scripts
Import via <script src="...">. For TypeScript/NPM projects, bundle with esbuild into a single JS file, then
reference via script tag.
Patterns
View Models
Create a props struct with a NewProps(domain) constructor in Go. Templates receive only pre-transformed data โ no I/O,
no complex logic. Test view models as pure Go functions.
Layout Pattern
Use children for content injection: define a layout component that renders { children... }, then wrap page content
with @BaseLayout("title") { ... }. Compose by nesting layouts.
For multiple content slots, pass templ.Component as parameters alongside children:
templ TwoColumnLayout(sidebar templ.Component) uses @sidebar and { children... }.
Context for Cross-Cutting Data
For auth, theme, locale โ use Go context with private key types and type-safe With*(ctx, value) /
Get*(ctx) (T, bool) helpers. Set in HTTP middleware, access via implicit ctx in templates. Prefer prop drilling for
direct parent-child data. Always use type-safe getters โ direct ctx.Value(key).(Type) panics on missing keys.
Interop with html/template
@templ.FromGoHTML(goTemplate, data) embeds Go templates in templ. templ.ToGoHTML(ctx, component) embeds templ in Go
templates. Useful for gradual migration.
Toolchain
templ generate
templ generate -f file.templ
templ generate -watch
templ fmt .
The -watch flag regenerates on file change. Combine with -cmd and -proxy for live-reload during development.
Run templ generate after every .templ change. Generated *_templ.go files must be committed.
Testing
Expectation Testing
Render to pipe, parse with goquery, assert with CSS selectors:
r, w := io.Pipe()
go func() {
_ = myComponent(data).Render(context.Background(), w)
_ = w.Close()
}()
doc, err := goquery.NewDocumentFromReader(r)
Use data-testid attributes for reliable test selectors.
Snapshot Testing
Compare rendered output against expected HTML using //go:embed expected.html and htmldiff.Diff(component, expected).
Testing Principles
- Component-level tests: verify data renders correctly using
data-testid selectors
- Page-level tests: verify components are present (by
data-testid), don't re-test component internals
- Test view models in pure Go โ no rendering needed for data transformation logic
Application
When writing templ code:
- Apply all conventions silently โ don't narrate each rule being followed
- If an existing codebase contradicts a convention, follow the codebase and flag the divergence once
- Keep templates focused on rendering. Move data transformation, validation, and business logic to Go code
- Run
templ generate after every .templ change before attempting to build or test
When reviewing templ code:
- Cite the specific violation and show the fix inline
- Don't lecture or quote the rule โ state what's wrong and how to fix it
Integration
The golang skill governs Go implementation; this skill governs .templ file authoring.
Templates are renderers, not processors. When in doubt, move logic to Go.