Developer guidesShip icons from a theme or plugin

Ship icons from a theme or plugin

Deliver icons with your code: a theme's assets/icons folder, drop-in folders, a manifest.json and easy_svg_register_icon_collection(), checked with WP-CLI.

You can deliver icons as files in your theme or plugin, instead of uploading them in wp-admin. Easy SVG Pro reads the files, sanitizes them and offers them everywhere icons can be picked: the block inserter, the icon fields, easy_svg_icon() and Twig. This tutorial shows the three ways in, how to describe icons with a manifest.json, and how to check that everything arrived.

All of this needs an active licence and Theme & folder sources switched on under Icons → Features. The free Easy SVG Support plugin has to be active and new enough to harden icons. Without it, no folder registers anything.

The three ways in

WayWhere the files liveWho chooses the slugCached
Theme folderassets/icons in the parent or child themethe manifest, else theme, else easy_svg_pro_theme_collection_slugyes, one day, rescanned on change
Drop-in folderwp-content/easy-svg-icons/<folder>, or any folder added with easy_svg_pro_source_dirsthe manifest, else the folder nameyes, one day, rescanned on change
Codeany folder, or an array of markupyou, in easy_svg_register_icon_collection()no, scanned on every request

Every icon is then addressed as collection/icon, for example acme/phone.

Pick the way that fits your project

A theme that owns the design: the theme folder. Icons that must survive a theme switch: a drop-in or a plugin. A slug that must be fixed in code: the register function.

Put SVG files in the folder

One file per icon. The file name becomes the icon name, so Phone Ringing.svg is the icon phone-ringing.

Optionally add a manifest.json

Readable labels, search keywords, renamed icons and licence information.

Check what registered

wp easy-svg icons list and wp easy-svg icons problems, or the Packs tab.

Way 1: the theme folder

Put your icons in assets/icons of your theme:

my-theme/
  assets/
    icons/
      phone.svg
      mail.svg
      manifest.json   (optional)

Both the parent and the active child theme are read and merged into one collection:

  • An icon in the child theme replaces the parent's icon of the same name. The panel notes that the child overrides the parent.
  • The child's manifest slug and label win over the parent's.
  • Without a manifest slug the collection is called theme and labelled "Theme icons".

In a template:

<?php
if ( function_exists( 'easy_svg_icon' ) ) {
	echo easy_svg_icon( 'theme/phone', array( 'label' => __( 'Call us', 'acme' ) ) ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- sanitized SVG.
}

To give the collection a brand name, either put "slug": "acme" in assets/icons/manifest.json or use the filter:

function acme_theme_icon_slug( $slug ) {
	return 'acme';
}
add_filter( 'easy_svg_pro_theme_collection_slug', 'acme_theme_icon_slug' );
The slug is part of every address stored in blocks, fields and templates. Decide it before editors start using the icons. Renaming it later makes all of them render nothing.

Way 2: drop-in folders

Every folder directly inside wp-content/easy-svg-icons is its own collection:

wp-content/
  easy-svg-icons/
    house-icons/
      front-door.svg
      manifest.json
    social/
      github.svg

This is also where an exported pack can simply be unzipped. The folder name gives the slug unless the manifest names one. A slug decided for a folder name is recorded in the option easy_svg_pro_dropin_collection_slugs, so it stays stable.

When two collections want the same name, the folder moves aside on its own: it takes the manifest slug when that is free, otherwise the folder's own slug, otherwise that slug with a number (house-icons-2). This is decided fresh on every request, so removing the other collection gives the folder its preferred name back.

Folders elsewhere are added with a filter, and behave exactly like a drop-in:

/**
 * Ship a cached icon folder from a plugin.
 */
function acme_plugin_icon_dir( $dirs ) {
	$dirs[] = plugin_dir_path( __FILE__ ) . 'icons/acme-social';

	return $dirs;
}
add_filter( 'easy_svg_pro_source_dirs', 'acme_plugin_icon_dir' );

Folders are keyed by their folder name, so give every folder a distinct name or a manifest slug.

Way 3: register in code

function acme_register_icons() {
	if ( ! function_exists( 'easy_svg_register_icon_collection' ) ) {
		return;
	}

	easy_svg_register_icon_collection(
		'acme',
		__( 'ACME Icons', 'acme' ),
		get_stylesheet_directory() . '/resources/svg'
	);
}
add_action( 'init', 'acme_register_icons' );

What makes it different:

  • The slug is exactly what you pass. A manifest.json in the folder names the icons, never the collection.
  • Call it on init at priority 10. The registry is handed to the core Icon API on priority 11, so a later call still works for easy_svg_icon() but misses the Icon block.
  • A folder registered this way is not cached: every request reads and sanitizes all files. For large folders prefer a drop-in through easy_svg_pro_source_dirs.
  • A second call with the same slug adds icons to the existing collection and replaces its label.
You can pass an array of slug => markup instead of a folder. That markup is registered and printed as it stands, without the sanitizer. Use it only for markup you wrote yourself.

Describing icons with manifest.json

A folder works without a manifest. Add one when you want labels, keywords or licence information:

{
  "version": 1,
  "slug": "house-icons",
  "label": "House icons",
  "icons": {
    "front-door": {
      "file": "front-door.svg",
      "label": "Front door",
      "keywords": ["entrance", "door"]
    },
    "window": {
      "label": "Window"
    }
  },
  "license": "CC0-1.0",
  "author": "Your agency",
  "source": "https://example.com/packs/house-icons"
}

The rules, as the plugin applies them:

  • version is the schema version. A version newer than your Easy SVG Pro understands refuses the whole folder rather than reading it half understood. Leave it at 1.
  • slug and label name the collection. An invalid slug is ignored and the folder's own slug is used. In a code registration they are ignored for the collection name.
  • icons is required and may not be empty. It can be an object keyed by icon name, as above, or a list of entries. Each entry may give file, name (alias slug), label and keywords (alias tags).
  • An entry without file means the file named after the entry: window above is window.svg.
  • An icon name must be lowercase letters, digits, - and _. An invalid name is skipped, never rewritten.
  • file must be a plain file name in the same folder. Subfolders, .. and symlinks that leave the folder are refused.
  • The manifest describes, it does not restrict. An SVG in the folder without an entry still loads, named after its file. A file the manifest renamed appears once, under the manifest's name.
  • license, author, source are optional free text. They are shown under the collection in Icons → Packs and never acted on: source is never fetched and never made a link.
  • A manifest that is not valid JSON, has no icons, or is a JSON list instead of an object registers nothing from that folder. That is on purpose: a typo must not publish every file in the folder.
The SVG sanitizer removes comments from every file, and with them the licence notice many icon sets ship inside each SVG. Put the attribution in license, author and source so it survives.

Limits

LimitDefaultChange it with
Icons per folder1000easy_svg_pro_source_max_icons
Bytes per file2 MBeasy_svg_pro_source_file_max_bytes

The limits apply to the theme folder, drop-ins and folders passed to the register function. A changed limit takes effect when a folder is scanned again, so touch a file or wait for the cached scan to expire.

How a folder becomes a collection

Every file goes through the site's sanitizer and the hardener before it is stored in the cache or the registry. The cached scan is invalidated when an SVG, the manifest or the free plugin's sanitizer changes.

The other source hooks

Besides the two documented filters, the scanner has three more hooks. They exist and are listed in this reference, but the readme does not promise them, so treat them as not guaranteed to stay:

  • easy_svg_pro_parse_manifest receives a valid manifest in its canonical shape plus the folder path, for example to add keywords. Its result is cached with the scan.
  • easy_svg_pro_collection_icons receives the final slug => markup list of every source collection on every request. Markup you add here is not sanitized again.
  • easy_svg_pro_registered_collection fires once per source collection that registered at least one icon, with slug, label, count and folder.

Icon sets made in wp-admin do not pass through any of these. The one hook every collection passes on its way to the core Icon block is easy_svg_pro_registered_icon.

Checking what arrived

On the command line (needs WP-CLI switched on under Icons → Features as well):

wp easy-svg icons list
+-------------+--------------+-------+--------------------+----------+
| collection  | label        | count | origin             | problems |
+-------------+--------------+-------+--------------------+----------+
| acme        | ACME Icons   | 24    | theme my-theme     | 0        |
| house-icons | House icons  | 11    | dropin house-icons | 2        |
+-------------+--------------+-------+--------------------+----------+

A collection with problems gets a hint line. Then:

wp easy-svg icons problems

lists one row per file with collection, source, file, icon and reason. The same reasons appear under the collection in Icons → Packs. A folder whose manifest is broken still shows up there, with zero icons and its reason.

ReasonMeaning
manifest-invalidThe manifest is not valid JSON, not an object, or has no icons. Nothing from the folder loads.
unsupported-versionThe manifest declares a newer schema than this plugin reads. Nothing loads.
bad-nameThe icon name is not lowercase letters, digits, - and _.
duplicateTwo entries give the same icon name. The first wins.
outside-folderThe file is missing, sits in a subfolder, is not an .svg, or is a symlink leaving the folder.
too-largeOver the per-file byte limit. Not read at all.
unreadableEmpty or could not be read.
not-svgThe sanitizer did not accept it as SVG.
no-drawingNothing visible was left after sanitizing and hardening.
too-manyPast the per-folder icon limit. Never read.
Problems are only reported for files a manifest names and for files past the icon limit. In a folder without a manifest, an SVG the sanitizer refuses, or one that is too large, is skipped without a problem row. Before you ship a folder, run wp easy-svg icons validate <dir> --strict on it.

Common mistakes

  • Nothing shows up at all. Check Icons → Features: Theme & folder sources must be on, and the licence active. For the core Icon block you also need Multiple collections and WordPress 7.1 or newer.
  • Icons show in templates but not in the Icon block. The code registration ran after init priority 10, or Multiple collections is off.
  • Icons look empty in the Icon block. Core keeps only paths and polygons. Strokes and shapes are rewritten by the free plugin; gradients, masks, use and text cannot be carried. They still render through easy_svg_icon().
  • A folder got the slug name-2. Another collection holds the name. Rename one of them or give the folder a free slug in its manifest.
  • A subfolder is ignored. Only SVG files directly in the folder are read, and only one level of folders under wp-content/easy-svg-icons is scanned.