---
name: twig-component
description: Symfony UX TwigComponent for reusable UI building blocks -- server-rendered components with PHP classes and Twig templates. Use when creating buttons, cards, alerts, badges, navbars, or any reusable UI element with props, blocks/slots, computed properties, or anonymous (template-only) components. Code triggers: AsTwigComponent, #[AsTwigComponent], ExposeInTemplate, PreMount, PostMount, , , component(), computed properties, anonymous component, HTML syntax. Also trigger when the user asks "how to create a reusable component", "how to make a component library", "how to pass props to a component", "how to use slots/blocks in a component", "how to build a design system in Symfony", "what is the HTML syntax for components", "how to create a component without a PHP class". Do NOT trigger for components that re-render dynamically on user input (use live-component), for JS behavior (use stimulus), or for page navigation (use turbo).
license: MIT
metadata:
author: Simon Andre
email: smn.andre@gmail.com
url: https://smnandre.dev
version: "1.0"
---
# TwigComponent
Reusable UI components with PHP classes + Twig templates. Think React/Vue components, but server-rendered with zero JavaScript.
Two flavors exist: **class components** (PHP class + Twig template) for components that need logic, services, or computed properties, and **anonymous components** (Twig-only, no PHP class) for simple presentational elements.
## When to Use TwigComponent
Use TwigComponent when you need reusable markup with props but no server re-rendering after the initial render. If the component needs to react to user input (re-render via AJAX, data binding, actions), use LiveComponent instead.
Good candidates: buttons, alerts, cards, badges, icons, form widgets, layout sections, navigation items, table rows, modals (structure only).
## Installation
```bash
composer require symfony/ux-twig-component
```
## Class Component
A PHP class annotated with `#[AsTwigComponent]` paired with a Twig template.
```php
// src/Twig/Components/Alert.php
namespace App\Twig\Components;
use Symfony\UX\TwigComponent\Attribute\AsTwigComponent;
#[AsTwigComponent]
final class Alert
{
public string $type = 'info';
public string $message;
public bool $dismissible = false;
}
```
```twig
{# templates/components/Alert.html.twig #}
{{ message }}
{% if dismissible %}
{% endif %}
```
```twig
{# Usage #}
{# With block content instead of message prop #}
Warning: Check your input
```
## Anonymous Component (Twig Only)
No PHP class needed. Props are declared with `{% props %}` directly in the template. Use for simple presentational components with no logic.
```twig
{# templates/components/Button.html.twig #}
{% props variant = 'primary', size = 'md', disabled = false %}
```
```twig
Delete
```
## Props
### Public Properties (Class Components)
Public properties become props. Required props have no default value.
```php
#[AsTwigComponent]
final class Card
{
public string $title; // Required
public ?string $subtitle = null; // Optional
public bool $shadow = true; // Optional with default
}
```
### mount() for Derived State
Use `mount()` to compute values from incoming props. The method runs once during component initialization.
```php
#[AsTwigComponent]
final class UserCard
{
public User $user;
public string $displayName;
public function mount(User $user): void
{
$this->user = $user;
$this->displayName = $user->getFullName();
}
}
```
```twig
```
### Dynamic Props (Colon Prefix)
Prefix a prop with `:` to pass a Twig expression instead of a string literal.
```twig
{# Pass a variable #}
{# Pass an expression #}
```
## Blocks (Slots)
Blocks let parent templates inject content into specific areas of a component.
### Default Block
Content between component tags goes to `{% block content %}`:
```twig
{# Component template #}
{% block content %}{% endblock %}
{# Usage #}
This is the card content
```
### Named Blocks
```twig
{# templates/components/Modal.html.twig #}
```
```twig
Confirm Action
Are you sure?
```
## Computed Properties
Methods prefixed with `get` become accessible as `this.xxx` in templates. They are computed on each access (not cached across re-renders -- for caching, see LiveComponent's `computed`).
```php
#[AsTwigComponent]
final class ProductCard
{
public Product $product;
public function getFormattedPrice(): string
{
return number_format($this->product->getPrice(), 2) . ' EUR';
}
public function isOnSale(): bool
{
return $this->product->getDiscount() > 0;
}
}
```
```twig
```
## Attributes
Extra HTML attributes passed to the component are available via `{{ attributes }}`. This is how you let consumers add custom classes, ids, data attributes, etc.
```twig
{# Usage #}
{# In component template -- renders class, id, data-controller #}
...
```
### Attributes Methods
```twig
{# Merge with defaults #}
{# Exclude specific #}
{# Only render specific #}
{# Check existence #}
{% if attributes.has('disabled') %}
```
## Components as Services
Components are Symfony services -- autowiring works naturally. Use the constructor for dependencies, public properties for props.
```php
#[AsTwigComponent]
final class FeaturedProducts
{
public function __construct(
private readonly ProductRepository $products,
) {}
public function getProducts(): array
{
return $this->products->findFeatured(limit: 6);
}
}
```
```twig
{# templates/components/FeaturedProducts.html.twig #}
{% for product in this.products %}
{% endfor %}
```
```twig
{# Usage -- no props needed, data comes from service #}
```
## Lifecycle Hooks
```php
use Symfony\UX\TwigComponent\Attribute\PreMount;
use Symfony\UX\TwigComponent\Attribute\PostMount;
#[AsTwigComponent]
final class DataTable
{
public array $data;
public string $sortBy = 'id';
#[PreMount]
public function preMount(array $data): array
{
// Modify/validate incoming data before property assignment
$data['sortBy'] ??= 'id';
return $data;
}
#[PostMount]
public function postMount(): void
{
// Runs after all props are set
$this->data = $this->sortData($this->data);
}
}
```
## Nested Components
Components compose naturally -- nest them like HTML elements:
```twig
Featured
```
## Configuration
```yaml
# config/packages/twig_component.yaml
twig_component:
anonymous_template_directory: 'components/'
defaults:
App\Twig\Components\: 'components/'
```
## HTML vs Twig Syntax
```twig
{# HTML syntax (recommended -- better IDE support, more readable) #}
{# Twig syntax (alternative -- useful in edge cases) #}
{% component 'Alert' with {type: 'success', message: 'Done!'} %}
{% endcomponent %}
```
Prefer HTML syntax (``) in all cases. The Twig syntax (`{% component %}`) is legacy and less readable.
## References
- **Full API** (attribute options, hooks, configuration, all methods): [references/api.md](references/api.md)
- **Patterns** (forms, tables, layouts, composition, real-world examples): [references/patterns.md](references/patterns.md)
- **Gotchas** (attributes, naming, nested components, common pitfalls): [references/gotchas.md](references/gotchas.md)
## See Also
- **UX Icons** integrates naturally in TwigComponent templates: `` inside your component markup.
- **UX Map** can be rendered inside a TwigComponent template via `{{ ux_map(map, {style: 'height: 400px;'}) }}`.