Icon Pack Architecture
When to Use
You need to understand how icon packs are discovered, loaded, and rendered to design efficient icon systems or troubleshoot icon discovery issues.
Decision
| Component | Purpose | When to customize |
|---|---|---|
YAML definition (*.icons.yml) |
Declares icon packs | Every theme/module with icons |
| Extractor plugin | Discovers and loads icons | Custom icon sources (API, database, computed) |
| Template | Renders icon markup | Custom HTML structure, CSS classes, attributes |
| Settings schema | Builds the admin settings form | Site builders need to pick size/color per placement |
| Library | CSS/JS dependencies | Icon fonts, custom styling, JavaScript interactions |
settings: never injects values into the template. It is consumed by IconExtractorSettingsForm::generateSettingsForm() to build Form API elements for contrib integrations (field widget, CKEditor, menu). The template gets whatever the caller passed as the third icon() argument, so every template must supply its own fallbacks with |default().
Pattern
Icon pack lifecycle:
1. Discovery: YamlDiscovery scans EXTENSION.icons.yml at each module/theme root.
Every top-level key becomes an icon-pack plugin ID.
2. Validation: IconPackManager::validateDefinition() checks the definition against
core/assets/schemas/v1/icon_pack.schema.json -- but ONLY if the optional
justinrainbow/json-schema library is installed. Without it, validation is skipped
entirely and bad definitions fail later, at render time.
3. Extraction: IconPackManager::processDefinition() instantiates the extractor plugin
and stores the full icon list in $definition['icons'], keyed by "pack_id:icon_id".
4. Caching: definitions cached in cache.discovery under the cid 'icon_pack',
tagged 'icon_pack_plugin' and 'icon_pack_collector'.
5. Loading: IconCollector (cache bin 'icon_info') calls the extractor's loadIcon()
on first use of a specific icon and returns an IconDefinition.
6. Rendering: Render\Element\Icon::preRenderIcon() builds an inline_template whose
context is extractor data + #settings + icon_id/source, and attaches `library:`.
Access via service container:
$icon_manager = \Drupal::service('plugin.manager.icon_pack');
$definitions = $icon_manager->getDefinitions();
$icon = $icon_manager->getIcon('pack_id:icon_id');
Reference: /core/lib/Drupal/Core/Theme/Icon/ for interfaces and base classes.
Common Mistakes
- Defining icons in
config/install/→ Use*.icons.ymlin the extension root for YAML discovery - Adding a top-level
$schema:key to*.icons.yml→ Fatal. Plugin YamlDiscovery turns every top-level key into a plugin ID, so$schemais treated as an icon pack. None of the 15*.icons.ymlfiles shipped by core and contrib carry one - Expecting the rendered icon to carry cache tags →
Render\Element\Iconadds no#cacheat all; invalidation happens at the plugin-definition layer, via theicon_pack_plugin/icon_pack_collectortags - Complex logic in templates → Keep templates lean, move logic to preprocess or extractor
- Hardcoded library dependencies → Use the
libraryproperty;preRenderIcon()attaches it automatically
See Also
- What is Icon API
- Icon Pack Definition
- Reference:
/core/lib/Drupal/Core/Theme/Icon/Plugin/IconPackManagerInterface.php