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

Performance & Caching

Compare provider caching behavior and choose a strategy suited for high-throughput rendering.

For an SSG that renders thousands of pages, or a web server handling concurrent icon lookups, the right provider and caching strategy make the difference between predictable performance and unexpected latency spikes.

Caching model

All four providers use an in-memory cache backed by sync.RWMutex. The read path acquires only a read lock, so concurrent reads do not contend. On a cache miss, a write lock is acquired, the icon is resolved, and the result is stored. Concurrent callers racing on the same name are safe: the first writer stores the result, and subsequent callers return the cached value.

Provider Parse cost Cache scope Warm-up
DirectoryProvider Disk I/O + SVG parse per icon Per-icon, lazy Preload()
JsonCollectionProvider JSON unmarshal (once) + per-icon resolution sync.Once for parse, per-icon for resolution First access
IconifyProvider HTTP request per icon Per-icon, lazy First access
ChainProvider None (delegates) None (delegates) N/A

DirectoryProvider

Each Get() call on a cache miss reads the SVG file from disk, parses it, sanitizes the content, and caches the result. Subsequent calls return the cached *Icon with no disk I/O.

Pre-warming with Preload

Call Preload() after construction to read all icons into the cache upfront:

Go
import swarmicons "github.com/frostybee/go-swarm-icons"
p, _ := swarmicons.NewDirectoryProvider("./icons")
p.Preload()

Preload walks the directory (respecting the recursive setting), calls Get() for each file, and populates the cache. After Preload, every subsequent Get() is a cache hit.

Use Preload in production deployments where the first-request latency of lazy loading is unacceptable.

JsonCollectionProvider

The JSON file (or embedded bytes) is parsed once on the first call to Get(), Has(), or All(), using sync.Once. This initial parse deserializes the entire JSON structure into memory. Subsequent calls skip parsing entirely.

Individual icons are resolved from the parsed data on first access (applying alias chains, transforms, and root-level dimension defaults) and cached per-name. The per-icon cache is separate from the parse step.

For large collections (thousands of icons), the first-access cost is dominated by the JSON unmarshal. Subsequent icon resolutions are fast map lookups.

IconifyProvider

Each Get() call on a cache miss sends an HTTP request to the Iconify API. The response is parsed, and the resolved icon is cached. Subsequent calls return the cached *Icon with no network I/O.

Failed fetches (network errors, non-200 responses, invalid JSON) are not cached. The provider retries the HTTP request on the next Get() call for that name.

Implications for build pipelines

Reserve IconifyProvider for development, prototyping, or applications where network access is acceptable.

ChainProvider

The chain provider has no cache of its own. Each Get() call delegates to the wrapped providers in order. The wrapped providers manage their own caches, so repeated calls through a chain still benefit from per-provider caching.

Icon immutability

Fluent methods on *Icon (Size, Class, Fill, Rotate, etc.) return new instances with deep-copied attribute maps. The cached icon is never mutated by caller transformations.

This means:

  • Multiple goroutines can call Get() for the same icon and apply different transformations concurrently.
  • The cached icon remains in its original state regardless of how many derived copies exist.
  • No defensive copying is needed when passing icons between functions.

Recommendations

Static site generators (SSG)

  • Use DirectoryProvider or JsonCollectionProvider (or the embedded Lucide set) for deterministic, offline builds.
  • Call Preload() on directory providers at startup to avoid lazy-load latency spikes during page rendering.
  • Avoid IconifyProvider in CI/CD pipelines where network access may be restricted or unreliable.

Web servers

  • All providers are safe for concurrent use. No additional synchronization is needed.
  • The lazy-load model works well for web servers: the first request for each icon pays the resolution cost, and all subsequent requests are cache hits.
  • For predictable latency, call Preload() on directory providers during server startup.

High icon count

  • JsonCollectionProvider with embedded bytes (NewJsonCollectionFromBytes) avoids disk I/O entirely. The JSON parse happens once, and all icons are resolved from memory.
  • For sets with thousands of icons where only a fraction are used, the lazy per-icon cache avoids resolving unused icons.

See also

  • Thread Safety: concurrency model per component, including the sync.RWMutex caching pattern
  • Providers: comparison table and overview of all five provider types
  • Cache Commands: cache warm for pre-fetching Iconify API responses before offline builds
Edit this page

Last updated: