This is the recommended way to use Iconify icon sets in production. Download the sets ahead of time, load them from disk or embed them in the binary, and resolve icons with no HTTP requests at runtime.
What it does
JsonCollectionProvider loads icons from an Iconify JSON collection file. The JSON is parsed once on first access via sync.Once, and resolved icons are cached in memory for all subsequent calls.
When to use it
Reach for this provider when working with Iconify-format JSON icon sets (Tabler, Heroicons, Material Design, and 200+ others), whether loaded from disk at runtime or embedded in the binary via go:embed. It handles alias resolution, per-icon transforms, and root-level dimension defaults automatically.
For .svg files on disk, see Directory Provider. For the pre-bundled Lucide set, see Embedded Lucide.
Setup
From a file on disk
import swarmicons "github.com/frostybee/go-swarm-icons"p, err := swarmicons.NewJsonCollectionProvider("./tabler.json")if err != nil { log.Fatal(err)}manager := swarmicons.Default("tabler", p)From embedded bytes
Use go:embed to bundle the JSON file in the binary:
import ( _ "embed" swarmicons "github.com/frostybee/go-swarm-icons")//go:embed icons.jsonvar iconsData []bytefunc main() { p := swarmicons.NewJsonCollectionFromBytes(iconsData) manager := swarmicons.Default("icons", p)}NewJsonCollectionFromBytes does not return an error. The JSON is parsed lazily on the first call to Get, Has, or All.
Config builder
import swarmicons "github.com/frostybee/go-swarm-icons"manager, err := swarmicons.NewConfig(). AddJsonCollection("tabler", "./tabler.json"). DefaultPrefix("tabler"). Build()Options & behavior
Lazy parsing
The JSON collection is not parsed at construction time. Parsing happens once, on the first call to Get(), Has(), or All(), using sync.Once. All subsequent calls use the parsed data without re-parsing.
If the JSON is malformed or missing the "icons" key, the provider returns false from Get() and Has(), and nil from All().
JSON schema
The provider expects the Iconify JSON format:
{ "prefix": "tabler", "width": 24, "height": 24, "icons": { "home": { "body": "<path d=\"...\"/>", "width": 24, "height": 24 }, "alert": { "body": "<path d=\"...\"/>" } }, "aliases": { "house": { "parent": "home" }, "arrow-right": { "parent": "arrow-left", "hFlip": true } }}icons(required): map of icon name to Iconify icon entries. Each entry has abody(SVG inner content) and optionalwidth,height,left,top,hFlip,vFlip,rotatefields. When the provider builds anIconfrom an entry, it applies the rotation (quarter turns, 0-3) and flip transforms, constructs theviewBoxattribute, and sanitizes the body; an entry with an emptybodyyieldsErrInvalidSVG.aliases(optional): map of alias name to an object with aparentfield and optional override fields.- Root-level
width/height: default dimensions inherited by icons that omit their own.
Alias resolution
Aliases reference a parent icon (or another alias) and can override transform fields:
| Field | Type | Effect |
|---|---|---|
parent |
string |
Name of the parent icon or alias (required). |
hFlip |
*bool |
Horizontal flip. Pointer type allows explicit false to override a parent's true. |
vFlip |
*bool |
Vertical flip. Same pointer semantics. |
rotate |
*int |
Rotation in quarter turns (0-3). |
width |
*int |
Override width. |
height |
*int |
Override height. |
body |
string |
Replace the parent's SVG body entirely. |
Aliases can chain (alias → alias → icon). Resolution stops at a depth of 10 to prevent infinite loops. A chain deeper than 10 levels returns false from Get().
Icon listing
All() returns the names of all icons and all aliases in the collection. Both are treated as valid icon names.
Caching
Resolved icons are cached in a thread-safe sync.RWMutex-protected map. The first Get() for a name resolves the icon (including alias chains and transforms), caches the result, and returns it. Subsequent calls return the cached *Icon directly.
Examples
Load from file
import ( "fmt" "log" swarmicons "github.com/frostybee/go-swarm-icons")func main() { p, err := swarmicons.NewJsonCollectionProvider("./tabler.json") if err != nil { log.Fatal(err) } manager := swarmicons.Default("tabler", p) icon, err := manager.Get("home") if err != nil { log.Fatal(err) } fmt.Println(icon.ToHTML())}Embed with go:embed
import ( _ "embed" "fmt" swarmicons "github.com/frostybee/go-swarm-icons")//go:embed heroicons.jsonvar heroiconsData []bytefunc main() { p := swarmicons.NewJsonCollectionFromBytes(heroiconsData) manager := swarmicons.Default("heroicons", p) icon, err := manager.Get("check-circle") if err != nil { fmt.Println("not found") return } fmt.Println(icon.Size(20).ToHTML())}Discover all JSON sets in a directory
The Config builder can scan a directory for *.json files and register each as a provider using the filename (without extension) as the prefix:
import swarmicons "github.com/frostybee/go-swarm-icons"manager, err := swarmicons.NewConfig(). DiscoverJsonSets("./json/"). DefaultPrefix("tabler"). Build()Given a ./json/ directory containing tabler.json and mdi.json, this registers two providers under prefixes "tabler" and "mdi".
Choosing a provider
| Provider | Source | Network | Disk | Caching | Best for |
|---|---|---|---|---|---|
| Directory | .svg files |
No | Yes | Per-icon on first access | Custom/brand icons |
| JSON Collection | Iconify JSON | No | Yes (or go:embed) |
Full parse on first call | Production icon sets |
| Iconify API | HTTP API | Yes | No | Per-icon on first fetch | Prototyping, unknown sets |
| Chain | Multiple providers | Depends | Depends | Per-provider | Local-first + fallback |
| Embedded Lucide | go:embed in binary |
No | No | Full parse on first call | Quick start, Lucide-only |
See also
- Providers API reference: full
JsonCollectionProviderconstructor, methods, and JSON schema details - Embedded Lucide: a ready-made JSON Collection provider with ~1,500 Lucide icons bundled via
go:embed - Performance & Caching: lazy parsing costs,
go:embedpatterns, and SSG throughput