SDC Icon Props
When to Use
You're building Single Directory Components that need configurable icons as props for reusable, flexible component APIs.
Decision
| Pattern | Use when... | Flexibility | Type safety |
|---|---|---|---|
| Object prop (recommended) | Full icon control needed | High | Medium |
| Enum prop | Limited icon set | Low | High |
| String prop | Icon ID only | Medium | Low |
UI Patterns $ref |
Using UI Icons module | High | High |
Pattern
Object-based icon prop (recommended):
# components/card/card.component.yml
$schema: https://git.drupalcode.org/project/drupal/-/raw/HEAD/core/assets/schemas/v1/metadata.schema.json
name: Card
props:
type: object
properties:
icon:
title: Icon
type: object
properties:
pack_id:
type: string
icon_id:
type: string
settings:
type: object
required:
- icon_id
title:
type: string
Two things this YAML does not do, both of which the template below has to compensate for:
default:is never applied.ComponentValidator::validateProps()runs withCHECK_MODE_TYPE_CASTonly — it casts and validates, it never fills in defaults. The effective default is whatever the Twig writes with??or|default().- Validation is not a runtime guarantee.
ComponentsTwigExtension::validateProps()wraps the check inassert()(ComponentsTwigExtension.php:106), so on a productionzend.assertions=-1it does not run, and the validator takes the context by value and never strips anything. Undeclared props reach the template and work;required: [icon_id]will not stop a render in production.
Write the template so it is correct with no props declared at all, and treat the YAML as documentation for the component's consumers.
Component template:
{# components/card/card.twig #}
<div class="card">
{% if icon and icon.icon_id %}
<div class="card__icon">
{{ icon(
icon.pack_id|default('my_theme'),
icon.icon_id,
icon.settings|default({})
) }}
</div>
{% endif %}
{% if title %}
<h3 class="card__title">{{ title }}</h3>
{% endif %}
{% block content %}{% endblock %}
</div>
A local variable named icon does not shadow the icon() function — Twig resolves name(...) as a function call at compile time — so the prop and the function can share a name safely.
The component's card.component.yml must also declare the slot it renders:
slots:
content:
title: Content
Usage:
{{ include('my_theme:card', {
title: 'Welcome',
icon: {
icon_id: 'home',
settings: { size: 32, color: '#007bff' }
}
}) }}
Enum-based prop for limited sets:
props:
type: object
properties:
status_icon:
type: string
enum:
- success
- warning
- error
default: "success"
{{ icon('my_theme', status_icon, { size: 20 }) }}
Reference: /core/assets/schemas/v1/metadata.schema.json for the component schema. (core/modules/sdc/ is an empty stub since SDC moved into core proper — it holds only sdc.info.yml, and the core/modules/sdc/src/metadata.schema.json URL is a Drupal 10.1-era path.)
Common Mistakes
- Using a
pack:idprop and passing it straight toicon()→ Fatal. Either model the prop as{pack_id, icon_id}or split the string in the template - Relying on the YAML
default:forpack_idorsettings→ Never applied; put the fallback in the Twig - Relying on
required:to guarantee a value at runtime → Validation is behindassert()and is off in production - Not handling missing icons gracefully → Use
{% if icon and icon.icon_id %}checks - Complex nested objects → Keep icon prop structure flat for easier usage
See Also
- Twig Icon Function
- Icon Slots
- Reference: SDC documentation