---
id: smnandre/symfony-ux-skills/twig-component
version: "5702798c"
license: MIT
install: manual
updated: 2026-06-14
---
# twig-component — TwigComponent enables you to compose reusable UI building blocks by pairing PHP classes with Twig templates, rendered server-side with zero JavaScript overhead. Define props, blocks, and computed properties to create everything from simple buttons to complex layout sections. Use class components for logic-heavy elements or anonymous Twig-only components for presentational markup.
Publisher: smnandre · Stars: 164 · Updated: 2026-06-14
Install (manual): `git clone https://github.com/smnandre/symfony-ux-skills`
## SKILL.md
# 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;'}) }}`.
[View on SkillFed](https://skillfed.io/smnandre/symfony-ux-skills/twig-component) · [View on GitHub](https://github.com/smnandre/symfony-ux-skills)