iconify/json-tools
PHP utilities for working with Iconify JSON icon sets: parse and validate icon data, look up icons and metadata, optimize collections, and generate custom subsets for lightweight SVG icon bundles and build pipelines.
Install via Composer:
composer require iconify/json-tools
Everything centers on one class, Iconify\JSONTools\Collection. A Collection holds one or more named IconSet objects (one per prefix like mdi, tabler, lucide), and knows how to load Iconify JSON, mutate icons, and export to JSON / SVG / CSS / fonts / components.
Look here first, in order:
src/Collection.php — the entry point for load/export/merge. Read the public methods before anything else.src/IconSet.php — per-prefix icon storage; addIcon, getIconData, removeIcon, validate, resolveAlias.src/Tools.php — static helpers (svgToIcon, iconToSVG, validateIconName, mergeIconData). These are what you'll reach for when transforming individual icons.src/Config.php — defaults and constants (icon info fields, validation rules).tests/ directory — the fastest way to see idiomatic usage of each export method.First use case — subset a huge collection and write it out:
use Iconify\JSONTools\Collection;
$source = new Collection();
$source->loadIconifyCollection('mdi'); // pulls from Iconify's CDN
// or: $source->loadJSON(__DIR__ . '/data/mdi.json');
$subset = new Collection();
$subset->addIconSet($source->getIconSet('mdi'), 'mdi');
$subset->getIconSet('mdi')->removeIcon('account-outline'); // prune
$subset->exportToDirectory(__DIR__ . '/build'); // writes mdi.json
That's the core loop for 80% of real work: load → filter/merge → export. Everything else is variations.
Curating a custom icon set from multiple sources. Collection can hold many IconSets at once. Load several Iconify collections, prune what you don't need, then export each prefix separately or merge() them into one for a combined sprite/font:
$c = new Collection();
$c->loadIconifyCollection('tabler');
$c->loadIconifyCollection('lucide');
$c->getIconSet('tabler')->removeIcon('brand-php'); // etc.
$c->exportToDirectory(__DIR__ . '/build');
Adding your own icons from raw SVG. Use Tools::svgToIcon() (it normalizes viewBox into width/height/left/top), then push into an IconSet:
use Iconify\JSONTools\Tools;
$set = $collection->getIconSet('my-icons');
$set->addIcon('logo', Tools::svgToIcon(file_get_contents('logo.svg')));
Rendering single icons on the fly. Tools::iconToSVG() converts an icon data array into a full SVG string (with <svg> wrapper). Combine with getIconData() to serve SVGs from a route or embed in Blade without writing files:
$data = $collection->getIconSet('mdi')->getIconData('home');
echo Tools::iconToSVG($data, ['height' => 24]);
Export pipeline targets. Collection has dedicated exporters — pick the one that matches your delivery surface:
exportToDirectory() / exportToJSON() — canonical Iconify JSON.exportToSVG($dir) — one .svg per icon.exportToSVGSprite() — a single sprite file (great for server-rendered pages).exportToCSS() — CSS classes backed by data-URIs.exportToIconFont() — for legacy font workflows.exportToReact(), exportToVue(), exportToSvelte() — component libraries for JS frontends.Cache-first loading in CI/build steps. Remote loadIconifyCollection() hits Iconify's API every run. In a build pipeline, commit the raw JSON under resources/icons/ and call loadJSON() instead. That makes builds deterministic and offline-safe.
Validation gate in tests. IconSet::validate() checks icon name syntax, coordinate sanity, and required fields. Call it on any custom IconSet before exporting — it catches malformed SVG-derived data before it lands in your frontend bundle:
$errors = $collection->getIconSet('my-icons')->validate();
if ($errors) { throw new \RuntimeException(implode("\n", $errors)); }
Alias resolution on read. Iconify JSON allows aliases ("aliases": { "home": { "parent": "house" } }). If you're iterating icons manually, use resolveAlias() or the alias-aware lookup rather than getIconData() on the alias name directly — an alias with no body will otherwise come back incomplete.
Memory and time limits on big collections. mdi is ~7k icons and multi-megabyte. Loading it in a web request is a bad idea — always do collection manipulation in a CLI build step (artisan icons:build), and consider ini_set('memory_limit', '512M') for large sets. The subset you export is what you ship.
Remote loads need curl and network. loadIconifyCollection() downloads from api.iconify.design. In locked-down CI, this fails silently-ish (throws Exception from the Collection). Check curl is installed and, again, prefer committing JSON.
left/top are optional. Icon data only includes left/top when the icon isn't anchored at 0,0. Don't assume keys exist — use Tools::mergeIconData() or defaults when computing positioning. width/height on the icon override the collection-level defaults.
Icon names are validated, not enforced, on write. addIcon() will happily store my icon or 123. validateIconName() (or validate() on the whole set) is the check — run it explicitly, especially for icons derived from file names.
exportToSVG() writes a directory of files, not one file. It creates/uses the directory and does not clean stale files. If you're regenerating, rm -rf the output dir first or you'll accumulate orphaned SVGs after renaming icons.
Config is global-ish. Config holds defaults (info fields to preserve, validation rules). If you need non-default behavior — e.g., keeping extra metadata fields through export — set them through the collection/export options rather than mutating Config globally; global mutation leaks between tests.
exportToIconFont() output depends on font tooling. It generates intermediate svg/.json data intended to be fed into a font generator (e.g., fantasticon or FontForge scripts). It is not a self-contained .ttf writer — plan the follow-up step.
exportToJSON() returns a string, exportToDirectory() returns booleans/void. Different call sites, different contracts. In tests, prefer exportToJSON() and assert on the string rather than diffing files.
Extension point — subclass IconSet for custom lookups. If you have a naming convention (e.g., cat-* groups), override getIconData on a subclass and addIconSet($custom, 'prefix'). Cleaner than post-processing exports.
Debugging empty exports. Nine times out of ten, the IconSet you exported was a different object than the one you added icons to — getIconSet('foo') returns the stored instance, but calling addIconSet() again replaces it. Always operate on the returned reference ($set = $collection->getIconSet('foo');).
Version pin. With 15 stars and 0 dependents in the snapshot, this is a small-maintainer package. Pin an exact version in composer.json and add an integration test that runs one full load→export cycle — that test is your upgrade alarm.
How can I help you explore Laravel packages today?