SVG Extractor
When to Use
You have individual SVG files stored locally in your theme or module and want automatic discovery and template-controlled rendering, with the SVG markup inlined into the page.
This extractor does not sanitize. SvgExtractor::extractSvg() parses the file with simplexml_load_string(), concatenates each child element's asXML(), and wraps the result in a FormattableMarkup so Twig prints it unescaped (SvgExtractor.php:104-138). Nothing strips <script>, onload, or xlink:href. Core's mitigation is refusal, not cleaning: discoverIcons() filters out every source with a URL scheme, and IconFinder::getFileContents() refuses remote URIs outright. Treat every file in an svg pack as trusted code you are shipping.
Decision
| If you need... | Configuration... | Why |
|---|---|---|
| One icon per file | sources: [icons/{icon_id}.svg] |
Standard pattern, simple discovery |
| Multiple source patterns | Multiple source entries | Fallback paths, organized subdirectories |
| Icons in one subdirectory | icons/category/{icon_id}.svg |
Discovery is not recursive — IconFinder sets Finder::depth(0), so each source matches one directory level only |
| Icons across sibling subdirectories | icons/{group}/{icon_id}.svg |
{group} expands to a wildcard directory and records the folder name as metadata |
| File naming variations | icons/{icon_id}-icon.svg |
Match existing file conventions |
{group} is discovery metadata only. It is stored on the IconDefinition and readable in PHP via getGroup(), but preRenderIcon() puts only icon_id and source into the template context — there is no {{ group }} variable in a pack template.
Pattern
Basic SVG extractor configuration:
svg_pack:
extractor: svg
config:
sources:
- icons/{icon_id}.svg
- icons/special/{icon_id}-icon.svg # Fallback pattern
template: >-
<svg xmlns="http://www.w3.org/2000/svg"
{{ attributes }}
width="{{ size|default(24) }}"
height="{{ size|default(24) }}"
fill="{{ color|default('currentColor') }}"
aria-hidden="true"
focusable="false">
{{ content }}
</svg>
The {icon_id} placeholder is replaced with the icon identifier.
Source files must be complete, well-formed SVG documents with the <svg> root element — that is what simplexml_load_string() parses. The extractor then hands you two variables:
{{ content }}— the concatenated child elements of the root<svg>(<path>,<circle>, …), without the wrapper.{{ attributes }}— aDrupal\Core\Template\Attributeobject carrying every attribute of the source's root<svg>,viewBoxincluded (SvgExtractor.php:132-135). Printing{{ attributes }}is how a pack survives icon sets with different coordinate systems; hardcodingviewBox="0 0 24 24"only works for a homogeneous 24-unit set.preRenderIcon()injects an emptyAttributewhen the extractor did not create one, so{{ attributes }}is safe to print in any pack template.
Reference: /core/lib/Drupal/Core/Theme/Plugin/IconExtractor/SvgExtractor.php
Common Mistakes
- Stripping the
<svg>wrapper from source files → The opposite of what is needed. A file containing only<path/><circle/>has two root nodes, is not well-formed XML,simplexml_load_string()fails,loadIcon()returns NULL, and the icon renders nothing. Core's own fixtures (core/modules/system/tests/modules/icon_test/icons/flat/foo.svg) are full<svg>documents - Hardcoding
viewBoxin the template → Print{{ attributes }}instead and let the source file's own viewBox through - Missing
xmlnsin template → Includexmlns="http://www.w3.org/2000/svg"for proper rendering - Trusting the extractor to sanitize → It does not. Never point an
svgpack at user-uploaded files - Expecting recursive discovery →
Finder::depth(0); use{group}or one source entry per directory
See Also
- Choosing Extractors
- SVG Sprite Extractor
- Reference:
/core/lib/Drupal/Core/Theme/Plugin/IconExtractor/SvgExtractor.php