Point the provider at a directory of .svg files and its icons become available by name, with lazy loading and automatic caching.
What it does
DirectoryProvider loads SVG icons from a directory on disk. Each icon is read once on first access, sanitized, and cached in memory for all subsequent calls.
When to use it
Reach for this provider when the project has its own .svg files: custom icons, brand assets, or exports from design tools. It keeps the icon source under version control and requires no network access or external data formats.
For bundled icon sets distributed as JSON, see JSON Collection Provider. For on-demand access to remote icon sets, see Iconify Provider.
Setup
Direct construction
import swarmicons "github.com/frostybee/go-swarm-icons"p, err := swarmicons.NewDirectoryProvider("./icons")if err != nil { // directory does not exist or is not a directory log.Fatal(err)}manager := swarmicons.Default("custom", p)icon, err := manager.Get("home") // resolves ./icons/home.svgConfig builder
import swarmicons "github.com/frostybee/go-swarm-icons"manager, err := swarmicons.NewConfig(). AddDirectory("custom", "./icons"). DefaultPrefix("custom"). Build()AddDirectory creates the DirectoryProvider internally during Build() and returns an error if the directory is invalid.
Options & behavior
Functional options
Pass options after the directory path in NewDirectoryProvider:
| Option | Default | Description |
|---|---|---|
WithRecursive(bool) |
true |
Scan subdirectories recursively. Subdirectory names become part of the icon name. |
WithExtension(string) |
"svg" |
File extension to match when scanning. |
import swarmicons "github.com/frostybee/go-swarm-icons"p, err := swarmicons.NewDirectoryProvider("./icons", swarmicons.WithRecursive(false), swarmicons.WithExtension("svg2"),)Recursive scanning
When recursive mode is enabled (the default), subdirectory names form part of the icon name, separated by /:
icons/├── home.svg → "home"├── outline/│ ├── home.svg → "outline/home"│ └── star.svg → "outline/star"└── solid/ └── check.svg → "solid/check"Disable recursion with WithRecursive(false) to scan only the top-level directory.
Preloading
Preload() reads all icons from the directory into the in-memory cache upfront. This is useful for production deployments where the first-request latency of lazy loading is unacceptable.
import swarmicons "github.com/frostybee/go-swarm-icons"p, err := swarmicons.NewDirectoryProvider("./icons")if err != nil { log.Fatal(err)}p.Preload()After Preload(), all subsequent Get() calls return cached results with no disk I/O.
Path traversal protection
The provider guards against path traversal attacks. The resolved path is checked with filepath.EvalSymlinks and must have the provider's root directory as a prefix. Any name that escapes the root returns false from both Get() and Has().
Caching
Icons are cached in a thread-safe sync.RWMutex-protected map. The read path acquires only a read lock. On a cache miss, a write lock is acquired, the icon is loaded from disk, and the result is stored. Concurrent callers racing on the same name are safe: the first to acquire the write lock stores the result, and subsequent callers return the cached value.
Error handling
NewDirectoryProvider returns an error wrapping swarmicons.ErrProviderError if the path does not exist or is not a directory. Individual Get() calls return (nil, false) for missing or unreadable files.
Examples
Minimal setup
import ( "fmt" swarmicons "github.com/frostybee/go-swarm-icons")func main() { p, err := swarmicons.NewDirectoryProvider("./icons") if err != nil { panic(err) } manager := swarmicons.Default("local", p) icon, err := manager.Get("home") if err != nil { panic(err) } fmt.Println(icon.ToHTML())}Recursive directory with preload
import ( "fmt" "log" swarmicons "github.com/frostybee/go-swarm-icons")func main() { p, err := swarmicons.NewDirectoryProvider("./icons", swarmicons.WithRecursive(true), ) if err != nil { log.Fatal(err) } p.Preload() manager := swarmicons.Default("app", p) // Access icons from subdirectories outline, _ := manager.Get("outline/home") solid, _ := manager.Get("solid/check") fmt.Println(outline.ToHTML()) fmt.Println(solid.ToHTML()) // List all available icons for _, name := range manager.All("app") { fmt.Println(name) }}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
DirectoryProviderconstructor, methods, and functional options - JSON Collection Provider: load Iconify-format JSON sets instead of raw SVG files
- Performance & Caching: preloading, cache behavior, and throughput guidance