Symfony UX TwigComponent: Wiederverwendbare Blöcke bauen
AI generated
SF
{ }
Symfony · UX · TwigComponent · PHP · Design-System
Symfony UX TwigComponent:
Wiederverwendbare Blöcke professionell bauen

Twig-Templates, die immer wieder dieselben Alert-Boxen, Card-Layouts und Button-Varianten kopieren, werden schnell zur Wartungslast. Symfony UX TwigComponent bringt echte Komponenten-Architektur in Twig — mit Props, Slots und PHP-Backing-Klassen für Logik, alles im Symfony-Ökosystem.

16 Min. Lesezeit Anonymous Components · Props · Slots · Backing-Klassen · Tailwind Symfony 7.x · symfony/ux-twig-component · PHP 8.3+

1. Warum TwigComponent statt Twig-Include

Das klassische Twig-include teilt Templates in Partials auf, die an beliebigen Stellen eingebunden werden. Das funktioniert gut für einfache Fälle, stößt aber an Grenzen, sobald eine Komponente Logik benötigt. Ein Button, der automatisch seine Route aufruft, eine Karte, die Daten aus dem aktuellen Kontext liest, oder ein Alert, der verschiedene Varianten (success, error, warning) kennt — all das erfordert mit include entweder viele with-Parameter oder Logik im aufrufenden Template. Symfony UX TwigComponent löst das mit einer sauberen Komponentenarchitektur: Props definieren die Eingabe, eine optionale PHP-Klasse enthält die Logik, und Slots ermöglichen flexible Inhalts-Einbettung.

Der Unterschied zu Twig-Macros: Macros sind reine Template-Funktionen ohne Zugang zum Twig-Kontext und ohne PHP-Backing. Sie können keine Services injizieren, keine Datenbank-Queries durchführen und keine PHP-Logik enthalten. TwigComponents sind vollständige PHP-Klassen, die Symfony-Services über Constructor Injection erhalten — mit demselben Dependency-Injection-Container wie Controller und Services. Eine Breadcrumb-Komponente kann den aktuellen Pfad direkt aus dem Request-Stack lesen. Eine Produktkarte kann ihren Preis mit dem Pricing-Service berechnen. Das macht TwigComponents zu echten Bausteinen einer skalierenden Frontend-Architektur in Symfony.

2. Installation und Ordnerstruktur

Die Installation von symfony/ux-twig-component erfolgt über Composer. Das Flex-Recipe registriert das Bundle und legt die Standardkonfiguration an. Komponenten-Templates liegen standardmäßig in templates/components/, PHP-Backing-Klassen in src/Twig/Components/. Die Konvention verknüpft Klasse und Template automatisch: Die Klasse App\Twig\Components\Alert verwendet das Template templates/components/Alert.html.twig. Custom-Pfade können in der Bundle-Konfiguration angepasst werden.

Für Namespace-Hierarchien innerhalb des Komponentensystems legt man Unterordner an: templates/components/Form/Input.html.twig entspricht dem Komponentennamen Form:Input. Das ermöglicht eine logische Gruppierung von Formular-Komponenten, Layout-Komponenten und UI-Komponenten. Die PHP-Klassen folgen derselben Hierarchie: App\Twig\Components\Form\Input. In großen Projekten mit einem vollständigen Design-System sind Unterordner unverzichtbar — ohne sie werden templates/components/ schnell zu einem unübersichtlichen Sammelsurium aus Dutzenden flachen Dateien.


<?php

declare(strict_types=1);

namespace App\Twig\Components;

use Symfony\UX\TwigComponent\Attribute\AsTwigComponent;
use Symfony\UX\TwigComponent\Attribute\ExposeInTemplate;

/**
 * Alert component with configurable type and dismissible behavior.
 * Used as: <twig:Alert type="success" dismissible>Meldung</twig:Alert>
 */
#[AsTwigComponent]
final class Alert
{
    /**
     * The visual type of the alert: success, error, warning, or info.
     * Determines the background color and icon shown.
     */
    public string $type = 'info';

    /**
     * Whether the user can dismiss this alert via a close button.
     */
    public bool $dismissible = false;

    /**
     * Maps type to Tailwind CSS classes for background and border.
     * Exposed to the template via getTypeClasses().
     */
    #[ExposeInTemplate]
    public function getTypeClasses(): string
    {
        return match ($this->type) {
            'success' => 'bg-green-50 border-green-400 text-green-800',
            'error'   => 'bg-red-50 border-red-400 text-red-800',
            'warning' => 'bg-yellow-50 border-yellow-400 text-yellow-800',
            default   => 'bg-blue-50 border-blue-400 text-blue-800',
        };
    }
}

3. Anonymous Components: reine Template-Blöcke

Nicht jede TwigComponent braucht eine PHP-Klasse. Anonymous Components sind Twig-Templates in templates/components/, die keine Backing-Klasse haben. Sie empfangen Props über Twig-Variablen und eignen sich für rein strukturelle UI-Bausteine ohne Logik: Card-Container, Divider, Badge, Avatar. Der Aufruf erfolgt mit der HTML-ähnlichen Syntax: <twig:Card title="Produkt">Inhalt</twig:Card>. Diese Syntax ist für Teams, die aus React oder Vue kommen, intuitiv — Komponenten sehen aus wie HTML-Elemente und werden im Template eingesetzt.

Anonymous Components definieren ihre Props über den {% props %}-Tag am Anfang des Templates: {% props title, variant = 'default', class = '' %}. Übergebene Werte sind direkt als Twig-Variablen verfügbar. Unbekannte Props — Props, die nicht im {% props %}-Tag deklariert sind — landen in der speziellen Variable attributes, die alle nicht-deklarierten HTML-Attribute enthält. Das ermöglicht das Durchreichen von HTML-Attributen wie id, data-* und ARIA-Attributen an das Root-Element der Komponente, ohne sie explizit zu deklarieren. Diese Funktion ist vergleichbar mit $attrs in Vue oder ...rest in React-Komponenten.

4. Props: typsichere Eingabe in Komponenten

In PHP-Backing-Klassen werden Props als öffentliche Properties der Klasse definiert. PHP-Typsystem und Symfony-Validierung stehen vollständig zur Verfügung. Eine public string $type = 'info' ist ein String-Prop mit Standardwert. Eine public bool $dismissible = false ist ein Boolean-Prop. Das Symfony-Serializer-System kümmert sich um die Typkonvertierung — Strings aus dem Twig-Template werden automatisch in die korrekten PHP-Typen konvertiert. Für komplexe Typen wie Enums sind spezielle Hydratoren verfügbar.

PHP 8.1-Enums eignen sich ideal als Prop-Typen für TwigComponent. Ein AlertType-Enum mit Werten Success, Error, Warning und Info macht die erlaubten Prop-Werte im Code explizit — PHPStan und IDE-Autocompletion erkennen sofort, wenn ein ungültiger Wert übergeben wird. Das ist deutlich sicherer als ein String-Prop, bei dem Tippfehler erst zur Laufzeit auffallen. Die Hydration von String-Werten aus Twig-Templates auf Enum-Instanzen übernimmt das TwigComponent-Paket automatisch, wenn der Prop-Typ ein BackedEnum ist.

5. PHP-Backing-Klassen für Logik und Services

Der wesentliche Vorteil von PHP-Backing-Klassen in Symfony UX TwigComponent ist die vollständige Symfony-DI-Integration. Jede Klasse kann Services über Constructor Injection empfangen — genau wie ein Controller oder ein Repository. Eine Breadcrumb-Komponente injiziert den Router und den Request-Stack, um die aktuelle URL zu ermitteln und die Breadcrumb-Struktur automatisch aufzubauen. Eine Produktkarte injiziert einen Pricing-Service, um den nutzerabhängigen Preis direkt in der Komponente zu berechnen.

Methoden der Backing-Klasse, die mit #[ExposeInTemplate] markiert sind, stehen im Twig-Template direkt als Variablen zur Verfügung. Eine Methode getTypeClasses(): string ist im Template als { { typeClasses } } zugänglich — ohne expliziten Methodenaufruf. Das hält das Twig-Template sauber von PHP-Logik und ermöglicht Unit-Tests der Backing-Klasse ohne HTTP-Layer. Die Komponente kann isoliert getestet werden: Props setzen, Methoden aufrufen, Rückgabewerte prüfen. Das ist ein großer Vorteil gegenüber Twig-Macros oder komplexen include-Konstrukten, die nicht direkt testbar sind.


<?php
{# templates/components/Alert.html.twig #}
{# This template is automatically linked to App\Twig\Components\Alert #}
<div class="border-l-4 p-4 rounded { { typeClasses } }" role="alert">
  <div class="flex items-start gap-3">
    {# Icon based on type — rendered server-side, no JS needed #}
    {% if type == 'success' %}
      <svg class="w-5 h-5 mt-0.5 shrink-0" fill="currentColor" viewBox="0 0 20 20">
        <path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd"/>
      </svg>
    {% endif %}

    {# The default slot — content between <twig:Alert>...</twig:Alert> tags #}
    <div class="flex-1 text-sm font-medium">{ { content } }</div>

    {# Conditional dismiss button — only rendered when dismissible=true #}
    {% if dismissible %}
      <button type="button" class="ml-auto -mx-1.5 -my-1.5 rounded-lg p-1.5 inline-flex items-center justify-center h-8 w-8 opacity-70 hover:opacity-100">
        <span class="sr-only">Schließen</span>
        <svg class="w-3 h-3" fill="none" viewBox="0 0 14 14">
          <path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m1 1 6 6m0 0 6 6M7 7l6-6M7 7l-6 6"/>
        </svg>
      </button>
    {% endif %}
  </div>
</div>

{# Usage in any Twig template:
   <twig:Alert type="success" :dismissible="true">
     Ihre Bestellung wurde erfolgreich aufgegeben!
   </twig:Alert>
   <twig:Alert type="error">
     Bitte überprüfen Sie Ihre Eingaben.
   </twig:Alert>
#}

6. Slots: flexible Inhalte in Komponenten einbetten

Slots sind benannte Bereiche innerhalb einer TwigComponent, die der aufrufende Code mit beliebigem Inhalt füllen kann. Der Standard-Slot ist das Twig-Konstrukt { { content } } — alles, was zwischen den öffnenden und schließenden Komponenten-Tags steht, wird als content verfügbar. Für Komponenten mit mehreren anpassbaren Bereichen — z. B. eine Karte mit Header, Body und Footer — definiert man benannte Slots mit dem {% block %}-Muster des TwigComponent-Slots-Systems.

Ein konkretes Beispiel: Eine Modal-Komponente hat einen Titel-Slot, einen Inhalts-Slot und einen Footer-Slot mit Aktions-Buttons. Der aufrufende Code füllt diese Slots mit beliebigem Twig-Inhalt, ohne die Grundstruktur des Modals zu kennen oder zu duplizieren. Das Modal-Template ist einmal definiert, alle Modalfenster in der Applikation sehen identisch aus und sind konsistent — Änderungen am Modal-Design müssen nur an einer Stelle gemacht werden. Dieses Muster entspricht dem Slot-Konzept aus Vue, Web Components und Svelte — in Symfony TwigComponent vollständig im Twig-Ökosystem.

7. Tailwind-CSS-Integration und Varianten

Tailwind CSS und Symfony UX TwigComponent passen sehr gut zusammen. Komponenten-Varianten werden über Tailwind-Klassen gesteuert, die PHP-Logik in der Backing-Klasse erzeugt. Das Klassenset für eine Schaltfläche mit Varianten primary, secondary und danger wird durch eine getClasses()-Methode berechnet, die den Prop-Wert in Tailwind-Klassen-Strings übersetzt. Das hält die Klassen-Logik aus dem Template heraus und macht sie unit-testbar.

Ein wichtiger Aspekt bei Tailwind und dynamischen Klassen: Tailwind scannt Quellcode nach Klassen-Strings zur Build-Zeit. Dynamisch zusammengesetzte Strings wie 'bg-' ~ color ~ '-500' werden nicht gefunden und aus dem CSS entfernt. Das korrekte Pattern ist daher, vollständige Klassen-Strings in PHP-Code oder Twig-Templates zu schreiben, nie zusammenzusetzen. In der Backing-Klasse verwendet man eine match-Expression, die vollständige Klassen-Strings zurückgibt: 'bg-green-500 text-white hover:bg-green-600'. Der Tailwind-Scanner findet diese vollständigen Strings und belässt sie im CSS-Output. Das ist dasselbe Muster, das aus der React-Tailwind-Integration bekannt ist — in TwigComponent ist es genau gleich.

8. Design-System mit TwigComponent aufbauen

Ein vollständiges Design-System mit Symfony UX TwigComponent besteht aus drei Schichten: Atom-Komponenten (Button, Badge, Icon, Input, Avatar), Molekül-Komponenten (AlertBanner, Card, FormGroup, Dropdown) und Organismus-Komponenten (Header, Sidebar, DataTable, Modal). Atom-Komponenten sind meist Anonymous Components ohne Backing-Klasse — sie sind reine Template-Blöcke mit Props. Molekül-Komponenten haben oft einfache Backing-Klassen, die Varianten-Logik enthalten. Organismus-Komponenten können Services injizieren und komplexe Daten aufbereiten.

Die konsistente Benennung folgt dem Muster DesignSystem:Atom:Button für den Komponentennamen, was im Twig-Template als <twig:DesignSystem:Atom:Button> erscheint. Das ist ausdrucksstärker als generische Namen und verhindert Namenskonflikte in großen Teams. Für Design-System-Dokumentation innerhalb des Projekts reicht ein einfaches Twig-Template, das alle Komponenten in ihren Varianten zeigt — Storybook-ähnlich, aber vollständig in Twig und PHP. Symfony UX TwigComponent macht die Wartung eines solchen Design-Systems zur PHP-Aufgabe ohne separates Frontend-Tooling.

9. TwigComponent vs. Twig-Include vs. Twig-Macro

Der direkte Vergleich der drei Ansätze für Twig-Wiederverwendung zeigt, wann welches Werkzeug die beste Wahl ist.

Kriterium Twig Include Twig Macro TwigComponent
PHP-Services injizieren Nicht möglich Nicht möglich Ja, Constructor DI
Slot-Inhalt einbetten Nicht nativ Begrenzt über caller() Nativ, benannte Slots
Twig-Kontext Voller Zugang Kein Twig-Kontext Eigener Scope + Props
Unit-Tests möglich Nicht isoliert Nicht isoliert Backing-Klasse testbar
HTML-ähnliche Syntax {% include %} { { macros.fn() } } <twig:Alert>

Für einfache Template-Partials ohne Logik ist include weiterhin legitim und ausreichend. Für wiederverwendbare UI-Blöcke mit Varianten und flexiblem Inhalt ist die Anonymous Component die richtige Wahl. Für Komponenten, die Services brauchen oder komplexe Ausgaben berechnen, ist die PHP-Backing-Klasse unverzichtbar. TwigComponent ersetzt Twig-Includes und Macros nicht vollständig — es ergänzt sie für den Bereich, wo eine Komponentenarchitektur wirklich gefragt ist.

Mironsoft

Symfony-Frontend-Architektur, TwigComponent und Design-System-Entwicklung

Design-System und UI-Komponenten für Symfony-Projekte?

Wir entwickeln vollständige Design-Systeme mit Symfony UX TwigComponent — von Atom-Komponenten und Tailwind-Varianten über PHP-Backing-Klassen bis zur Design-System-Dokumentation für euer Team.

Komponenten-Bibliothek

Button, Card, Alert, Modal, Form-Elemente als TwigComponent mit Tailwind-Varianten für konsistentes UI

Backing-Klassen-Architektur

PHP-Logik und Service-Injection in TwigComponents — unit-testbar, wartbar, Symfony-nativ

Design-System-Audit

Bestehende Twig-Templates auf TwigComponent migrieren und in ein wartbares Design-System überführen

10. Zusammenfassung

Symfony UX TwigComponent bringt echte Komponentenarchitektur in das Twig-Ökosystem. Anonymous Components ohne PHP-Backing eignen sich für reine Template-Blöcke mit Props und Slots. PHP-Backing-Klassen ermöglichen Service-Injection, Logik-Kapselung und Unit-Tests. Das Slot-System erlaubt flexible Inhalts-Einbettung für generische Layout-Komponenten. Tailwind-CSS-Varianten werden durch match-Expressions in der Backing-Klasse gesteuert — vollständige Klassen-Strings, die der Tailwind-Scanner findet.

Der größte Gewinn liegt in der Konsistenz und Wartbarkeit. Wenn zehn Templates denselben Alert-Block kopieren und das Design sich ändert, müssen zehn Stellen angepasst werden. Mit einem Alert-TwigComponent ist es eine. Neue Varianten, Barrierefreiheits-Verbesserungen und Design-Updates propagieren automatisch durch alle Stellen, die die Komponente verwenden. Kombiniert mit Live Component für reaktive Varianten und Turbo für Navigation, entsteht ein vollständiges, wartbares Frontend-System — vollständig in PHP und Twig ohne separates JavaScript-Framework.

Symfony UX TwigComponent — Das Wichtigste auf einen Blick

Anonymous Components

Twig-Template in templates/components/ ohne PHP-Klasse. Props via {% props %}. Standard-Slot via { { content } }. Ideal für reine UI-Blöcke ohne Logik.

PHP-Backing-Klassen

#[AsTwigComponent] auf PHP-Klasse. Constructor DI für Services. #[ExposeInTemplate] macht Methoden als Twig-Variablen zugänglich — unit-testbar.

Slots

Standard-Slot via { { content } }. Benannte Slots für Header, Body, Footer. Aufruf: <twig:Modal><twig:block name="footer">…</twig:block></twig:Modal>.

Tailwind-Varianten

match-Expression gibt vollständige Klassen-Strings zurück — nie Strings zusammensetzen. Tailwind-Scanner findet vollständige Strings zur Build-Zeit.

11. FAQ: Symfony UX TwigComponent und wiederverwendbare Blöcke

1Was ist symfony/ux-twig-component?
Wiederverwendbare Twig-Komponenten mit Props, Slots und optionalen PHP-Backing-Klassen. HTML-ähnliche Syntax: <twig:Alert type="success">. Teil des Symfony UX-Ökosystems.
2Anonymous vs. Backing-Klasse?
Anonymous: reines Twig-Template ohne PHP, für UI-Blöcke ohne Logik. Backing-Klasse: PHP mit DI, Logik und Unit-Tests. Beide brauchen eine Twig-Template-Datei.
3Props definieren?
Anonymous: {% props title, variant = 'default' %}. Backing-Klasse: öffentliche PHP-Properties. Enums als Prop-Typen werden automatisch hydriert.
4Services injizieren?
Constructor Property Promotion in der Backing-Klasse. Symfony-DI injiziert automatisch — genau wie in Controllern. #[AsTwigComponent] auf der Klasse ist Pflicht.
5Was sind Slots?
Standard-Slot: { { content } } — Inhalt zwischen den Komponenten-Tags. Benannte Slots via <twig:block name="footer"> für mehrere anpassbare Bereiche.
6ExposeInTemplate-Attribut?
#[ExposeInTemplate] auf Methode macht sie im Twig als Variable zugänglich. getTypeClasses() wird zu { { typeClasses } } — kein expliziter Methodenaufruf im Template.
7Tailwind-Klassen richtig verwenden?
Nie Strings zusammensetzen — immer vollständige Klassen in match-Expressions. Tailwind-Scanner findet vollständige Strings. Dynamisch zusammengesetzte Klassen fehlen im CSS-Output.
8Backing-Klassen unit-testen?
Ja. Normale PHP-Klasse — ohne HTTP-Layer oder Twig testbar. Services mocken, Props setzen, Methoden aufrufen. Nicht möglich mit Twig-Include oder Macros.
9Design-System organisieren?
Unterordner nach Atomic Design: components/Atom/, components/Molecule/, components/Organism/. Komponentenname: <twig:Atom:Button>. PHP-Klassen in spiegelnder Struktur.
10TwigComponent oder Twig-Include?
Einfache Partials ohne Varianten: Include ist ausreichend. UI-Blöcke mit Props und Varianten: Anonymous Component. Logik und Services: Backing-Klasse. Nicht alles muss TwigComponent sein.