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

Directory Provider

Load SVG icons from a local folder with lazy caching and path traversal protection.

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

Go
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.svg

Config builder

Go
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.
Go
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.

Go
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

Go
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

Go
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

Edit this page

Last updated: