Most Go web applications render HTML through html/template, which escapes every string it prints. This guide shows how to pass rendered icons into templates, why marking them as trusted HTML is safe here, and how to expose the manager as a template function.
A minimal handler
Build the manager once at startup and share it across requests; IconManager is safe for concurrent reads and caches resolved icons (see Thread Safety).
package mainimport ( "html/template" "log" "net/http" swarmicons "github.com/frostybee/go-swarm-icons" "github.com/frostybee/go-swarm-icons/lucide")var manager = swarmicons.Default("lucide", lucide.Provider())var page = template.Must(template.New("page").Parse(`<!DOCTYPE html><html><body> <nav>{{ .HomeIcon }} Home</nav></body></html>`))type pageData struct { HomeIcon template.HTML}func handler(w http.ResponseWriter, r *http.Request) { icon, err := manager.Get("home") if err != nil { http.Error(w, "icon lookup failed", http.StatusInternalServerError) return } w.Header().Set("Content-Type", "text/html; charset=utf-8") data := pageData{HomeIcon: template.HTML(icon.ToHTML())} if err := page.Execute(w, data); err != nil { log.Printf("render: %v", err) }}func main() { http.HandleFunc("/", handler) log.Fatal(http.ListenAndServe(":8080", nil))}Why template.HTML(icon.ToHTML()) is safe
html/template escapes plain strings, so passing icon.ToHTML() directly would print the SVG markup as text. Casting to template.HTML tells the template engine the value is trusted markup and must be emitted verbatim.
That trust is earned by the library's pipeline, not assumed: every icon that reaches ToHTML() through the manager has passed the nine-stage sanitization pipeline (scripts, event handlers, and external references stripped), and all attribute values are HTML-escaped during rendering. There is no path from a provider to ToHTML() that skips sanitization.
An icon template function
Threading every icon through a data struct gets tedious. A template.FuncMap entry lets templates request icons by name:
funcs := template.FuncMap{ "icon": func(name string) (template.HTML, error) { ic, err := manager.Get(name) if err != nil { return "", err } return template.HTML(ic.ToHTML()), nil },}page := template.Must(template.New("page").Funcs(funcs).Parse(`<a href="/">{{ icon "lucide:home" }} Home</a><a href="/search">{{ icon "search" }} Search</a>`))Because the function returns an error as its second value, a missing icon fails Execute instead of rendering a broken page. For pages that should degrade instead of failing, resolve the error inside the function and return a placeholder, the pattern the demo application uses:
func mustIcon(name string) template.HTML { ic, err := manager.Get(name) if err != nil { return template.HTML(`<span class="icon-missing"></span>`) } return template.HTML(ic.ToHTML())}A fallback icon configured on the manager (FallbackIcon with IgnoreNotFound) achieves the same without a custom function; see Aliases & Fallbacks.
Per-request attributes
Get accepts caller attributes, so per-page styling needs no extra machinery:
icon, err := manager.Get("home", map[string]string{ "class": "nav-icon", "style": "color: currentColor",})Caller attributes sit at the top of the five-layer merge, above global and per-prefix defaults configured once on the manager.
Icon-heavy pages
Inlining the same SVG dozens of times adds up. SpriteCollector deduplicates repeated icons into one hidden <symbol> block referenced by <use> elements; see Sprite Sheets. For provider choice and caching behavior under concurrent load, see Performance & Caching.