Skip to content

Twig Rendering

When to Use

Rendering an icon directly in a custom Twig template (block, paragraph, layout).

Pattern

Use core's icon(pack_id, icon_id, settings), from Drupal\Core\Template\IconsTwigExtension. It is the general-purpose renderer and needs no contrib module:

{# Render with pack defaults #}
{{ icon('my_theme_icons', 'menu') }}

{# Render with overrides #}
{{ icon('my_theme_icons', 'menu', {
  size: 32,
  color: '#0066cc',
  decorative: true,
}) }}

It returns the #type: icon render array below, so cache metadata bubbles correctly.

icon_preview() is not the same function

UI Icons also registers icon_preview(pack_id, icon_id, settings), but it is the admin-preview renderer behind the Library page and the picker — not the one to reach for in a theme template. It returns IconPreview::getPreview(), which forces a size of 48 when you omit the settings argument, rather than using the pack's own defaults: IconPreview::ICON_DEFAULT_SIZE is 48 on the preview:-template path, and templates/icon-preview.html.twig writes {{ settings.size|default(48) }} on the fallback path.

(The ?? ['size' => 32] in IconPreviewTwigExtension::getIconPreview() is dead code from Twig's point of view — the parameter default is [], not NULL, so ?? never fires on a two-argument call.)

Decision

Twig context Approach
Static, theme-author-defined icon {{ icon(...) }} with literal arguments
Rendering a preview in an admin/config UI {{ icon_preview(...) }}
Icon from a field on the entity Pass through field rendering — the field formatter handles it
Icon from a UI Patterns prop Pattern templates render the prop automatically

Pattern: Programmatic Render Array (PHP)

$build = [
  '#type' => 'icon',
  '#pack_id' => 'my_theme_icons',
  '#icon_id' => 'menu',
  '#settings' => ['size' => 32],
];

The property is #icon_id. #icon is not a recognized property of core's Icon element — it is ignored, #icon_id stays empty, and the element renders nothing.

Or via the plugin manager:

$icon = \Drupal::service('plugin.manager.icon_pack')->getIcon('my_theme_icons:menu');
$build = $icon->getRenderable(['size' => 32]);

Common Mistakes

  • Wrong: calling icon() or icon_preview() with the full ID string (my_theme_icons:menu) → Right: both expect (pack_id, icon_id, settings) as separate arguments
  • Wrong: using icon_preview() as the general renderer in theme templates → Right: it's the admin preview helper and forces size: 48 when settings are omitted
  • Wrong: hardcoding the SVG content from the source file → Right: bypasses settings, accessibility templating, and cache metadata

See Also