Skip to main content
go-swarm-icons
On this page

Architecture

See how the manager, provider registry, and renderer cooperate to resolve each icon request.

Not required for normal use. This page explains the resolution flow, module boundaries, and provider registry design for readers who want to understand how the library works internally.

System overview

The library is organized around a central IconManager that coordinates icon resolution through a provider registry and an attribute renderer.

Consumer Code
manager.Get("lucide:home", map{"class": "icon"})
icon.Size(24).Class("text-blue").Fill("currentColor").ToHTML()
|
v
IconManager
1. Resolve alias
2. Parse prefix
3. Find provider
4. Fallback chain
5. Render attrs
| |
v v
Provider IconRenderer
Registry
prefix->impl Merge attrs
| Apply ARIA
|
+--+--+--+
| | | |
v v v v
Dir JSON Iconify Chain
|
v
*Icon
content (SVG inner)
attributes (map)
Fluent mutations
ToHTML() rendering

Consumer code interacts with two entry points: the IconManager (for resolution) and the *Icon (for transformation and output). Everything else is internal plumbing.

Three-module design

The repository contains three independent Go modules, each with its own go.mod. This boundary design prevents dependency leakage to consumers.

Core module

github.com/frostybee/go-swarm-icons

Contains the icon manager, all four providers, the renderer, the fluent Icon API, SVG parsing, sanitization, the sprite collector, and the error types. Its only dependency is the Go standard library (net/http for the Iconify provider, encoding/json for JSON parsing, sync for concurrency primitives).

Lucide submodule

github.com/frostybee/go-swarm-icons/lucide

Embeds the Lucide icon set (~800 KB) via go:embed lucide.json. Depends only on the core module. Consumers who do not need Lucide never add this binary weight.

Goldmark submodule

github.com/frostybee/go-swarm-icons/goldmark

Adds inline :icon[prefix:name] syntax to Goldmark-processed Markdown. Depends on the core module and github.com/yuin/goldmark. Consumers who do not use Goldmark never pull in the Goldmark dependency.

Import paths

Go
import swarmicons "github.com/frostybee/go-swarm-icons"
import "github.com/frostybee/go-swarm-icons/lucide"
import swarmgoldmark "github.com/frostybee/go-swarm-icons/goldmark"

Resolution flow

When manager.Get("home", map{"class": "icon"}) is called, the icon passes through six stages.

1. Alias resolution

The manager checks the aliases map. If the name matches an alias, the target name is substituted. This is a single-level lookup (manager-level aliases do not chain). The check happens under a read lock.

2. Name parsing

The name is split on the first : character. The left side is the prefix, the right side is the icon name. If the name has no colon, the default prefix is applied. An empty prefix, empty name, or bare name without a default prefix returns ErrInvalidIconName.

3. Provider lookup

The manager looks up the provider registered for the resolved prefix. If no provider exists, the flow moves to the fallback stage (or returns ErrProviderNotFound if no fallback is configured).

4. Icon retrieval

The provider's Get(iconName) is called. Each provider implementation handles its own loading and caching:

  • DirectoryProvider reads from disk on first access, caches the result.
  • JsonCollectionProvider parses JSON on first access (via sync.Once), resolves aliases up to depth 10, and caches resolved icons.
  • IconifyProvider makes an HTTP request on first access and caches the response.
  • ChainProvider iterates its providers in order and returns the first hit.

If the provider returns false (not found), the flow moves to the fallback stage.

5. Fallback chain

Two fallback levels are checked in order:

  1. Per-prefix fallback: if a fallback is registered for the resolved prefix via FallbackIconForPrefix, it is tried first.
  2. Global fallback: if a global fallback is registered via FallbackIcon, it is tried next.

Each fallback is resolved through the same get flow, with a resolvingFallback flag to prevent infinite recursion. If the fallback also fails, ErrIconNotFound is returned (or an empty icon if IgnoreNotFound is enabled).

6. Attribute rendering

The IconRenderer.Render method applies the five-layer attribute merge and ARIA injection, producing a new *Icon with the merged attributes. The original provider-cached icon is not modified.

File inventory

Core module

File Purpose
icon.go Icon type, fluent methods, ToHTML, constructors (New, FromFile, FromString)
icon_transform.go Rotate, Flip, Width, Height, CSS transform composition
iconify_data.go Internal Iconify entry parsing and transform application
manager.go IconManager, resolution algorithm, Default()
renderer.go IconRenderer, five-layer attribute merge, ARIA rules
config.go Config builder, deferred registration, Build()
provider.go Provider interface definition
directory_provider.go DirectoryProvider, path traversal guard
json_provider.go JsonCollectionProvider, lazy parse, alias resolution
chain_provider.go ChainProvider, first-found composite
iconify_provider.go IconifyProvider, HTTP with host fallback
internal/svgparse/svgparse.go SVG parsing and the nine-stage sanitization pipeline
cache.go iconCache, shared sync.RWMutex cache
sprite.go SpriteCollector, ID namespacing
errors.go Sentinel error values
doc.go Package documentation

Lucide submodule

File Purpose
lucide/lucide.go go:embed directive, Provider() factory
lucide/lucide.json Embedded Iconify JSON (~800 KB)

Goldmark submodule

File Purpose
goldmark/extension.go Extension struct, Extend() registration
goldmark/parser.go InlineParser for :icon[...] syntax
goldmark/node.go IconNode AST node, KindIconNode
goldmark/renderer.go NodeRenderer, transform vs. pass-through dispatch

See also

  • Core Concepts: a shorter introduction to the same ideas for readers who want the overview without the implementation detail
  • Providers: comparison table and overview of all five provider types
Edit this page

Last updated: