Template Variables
When to Use
You're writing icon pack templates and need to understand available variables and how to use them effectively.
Decision
| Variable | Available in... | Use for... |
|---|---|---|
icon_id |
Always | CSS classes, IDs, data attributes, sprite fragments |
source |
svg, svg_sprite, path (empty string for font) |
File paths, URLs, sprite references |
content |
svg extractor; font only for .codepoints sources |
SVG inner markup (paths, circles, …) |
attributes |
Always — populated by svg from the source root, empty Attribute otherwise |
Passing the source SVG's own viewBox, fill, stroke through |
Whatever the caller passed as icon()'s third argument |
Always | Size, color, extra classes |
Render\Element\Icon::preRenderIcon() builds the context as array_merge($extractor_data, $element['#settings'], $context), so:
- The pack definition itself leaks into the context —
label,id,provider,extractor,settings,library,version,licenseare all readable in a pack template. Do not name a setting after one of them. - Caller settings override extractor data, but
icon_idandsourceare merged last and win over everything. A setting namedicon_idorsourceis silently discarded. {{ group }}does not exist.{group}in a source pattern is stored on theIconDefinitionand reachable only from PHP viagetGroup().
Pattern
Template with all common variables. Prefer {{ attributes }} over a hardcoded viewBox — for the svg extractor it carries the source file's own root attributes:
<svg xmlns="http://www.w3.org/2000/svg"
{{ attributes }}
width="{{ size|default(24) }}"
height="{{ size|default(24) }}"
fill="{{ color|default('currentColor') }}"
stroke="{{ stroke|default('none') }}"
stroke-width="{{ stroke_width|default(0) }}"
class="icon icon-{{ icon_id|clean_class }}{{ class ? ' ' ~ class : '' }}"
aria-hidden="{{ decorative|default(true) ? 'true' : 'false' }}"
{% if not decorative and aria_label %}aria-label="{{ aria_label }}"{% endif %}
focusable="false">
{{ content }}
</svg>
Settings definition for the above. This builds the admin form for the pack; the default: values below never reach the template, which is why every one of them is repeated as a |default() above:
settings:
size:
type: "integer"
default: 24
color:
type: "string"
default: "currentColor"
stroke:
type: "string"
default: "none"
stroke_width:
type: "number"
default: 0
class:
type: "string"
default: ""
decorative:
type: "boolean"
default: true
aria_label:
type: "string"
default: ""
Reference: /core/lib/Drupal/Core/Render/Element/Icon.php:59-99 for how the template context is assembled; /core/lib/Drupal/Core/Theme/Icon/IconExtractorBase.php:24-30 for which definition keys are stripped before it.
Common Mistakes
- Expecting
settings: default:to populate a variable → It builds a form; the template must use|default() - Declaring a setting in YAML and assuming that is what makes it available → Any key in
icon()'s third argument reaches the template, declared or not. Thesettingsschema only controls the admin form - Naming a setting
icon_idorsource→ Overwritten by the render element, always - Hardcoding
viewBoxin ansvgpack template → Print{{ attributes }}and let the source file decide - Missing
clean_classfilter → Use|clean_classfor icon_id in class names - Printing
{{ content }}with|raw→ Unnecessary and unsafe.SvgExtractoralready returns aFormattableMarkup, which Twig prints unescaped