# Symfony UX Live Components
## Introduction
Symfony UX Live Components is a powerful PHP library that enables automatic frontend updates for Twig components as users interact with them, inspired by Laravel Livewire and Phoenix LiveView. The bundle integrates seamlessly with Symfony's TwigComponent library to create reactive, stateful components that update without full page refreshes. Components maintain their state through LiveProps, which can be serialized and sent to the frontend, then hydrated back on the server during AJAX re-renders.
The library provides a sophisticated system for building interactive UIs with minimal JavaScript, handling form submissions, validation, event broadcasting between components, and deferred/lazy loading. It includes TypeScript frontend controllers using Stimulus, automatic model binding for form inputs, lifecycle hooks for component hydration/dehydration, and seamless integration with Symfony forms and Doctrine entities. The architecture supports parent-child component relationships, URL-bound state, and polling for real-time updates.
## Core APIs and Functions
### Creating a Basic Live Component
Define a live component using the `#[AsLiveComponent]` attribute with reactive properties marked by `#[LiveProp]`.
```php
items = ['Buy groceries', 'Write code', 'Deploy app'];
}
}
```
### Live Component Template with Model Binding
Create a Twig template that automatically updates on user interaction using `data-model` attributes.
```twig
{% for item in items %}
{% if searchTerm == '' or item|lower contains searchTerm|lower %}
{{ item }}
{% endif %}
{% endfor %}
```
### Component with Actions
Define interactive actions using `#[LiveAction]` attribute for user-triggered operations.
```php
count++;
}
#[LiveAction]
public function decrement(): void
{
$this->count--;
}
#[LiveAction]
public function reset(): void
{
$this->count = 0;
}
}
```
```twig
Count: {{ count }}
```
### Form Integration with ComponentWithFormTrait
Integrate Symfony forms into live components with automatic validation and submission handling.
```php
createForm(UserFormType::class, $this->user);
}
#[LiveAction]
public function save(): void
{
$this->submitForm();
$form = $this->getForm();
if ($form->isValid()) {
$entityManager = $this->getDoctrine()->getManager();
$entityManager->persist($this->user);
$entityManager->flush();
$this->addFlash('success', 'User saved successfully!');
}
}
}
```
```twig
```
### Validation with ValidatableComponentTrait
Add real-time validation to components using Symfony's validator constraints.
```php
validate();
// Send email or save to database
// ...
}
}
```
```twig
```
### LiveProp Configuration Options
Configure LiveProp behavior with writable paths, custom hydration, and URL binding.
```php
['min' => 0, 'max' => 1000],
'inStock' => true,
];
// Custom hydration/dehydration
#[LiveProp(hydrateWith: 'hydrateProduct', dehydrateWith: 'dehydrateProduct')]
public ?Product $selectedProduct = null;
// DateTime with specific format
#[LiveProp(writable: true, format: 'Y-m-d')]
public ?\DateTimeInterface $startDate = null;
// Hook called after property update
#[LiveProp(writable: true, onUpdated: 'onQueryChanged')]
public string $searchQuery = '';
public function hydrateProduct(int $id): ?Product
{
return $this->productRepository->find($id);
}
public function dehydrateProduct(?Product $product): ?int
{
return $product?->getId();
}
public function onQueryChanged(string $oldValue): void
{
$this->page = 1; // Reset to first page on new search
}
}
```
### Event Broadcasting Between Components
Emit and listen to events for inter-component communication.
```php
items[$productId])) {
$this->items[$productId]['quantity'] += $quantity;
} else {
$this->items[$productId] = [
'id' => $productId,
'quantity' => $quantity,
];
}
}
#[LiveListener('cart:clear')]
public function clearCart(): void
{
$this->items = [];
}
}
#[AsLiveComponent('product_item')]
final class ProductItemComponent
{
use DefaultActionTrait;
#[LiveProp]
public int $productId;
#[LiveAction]
public function addToCart(): void
{
$this->emit('product:added', [
'productId' => $this->productId,
'quantity' => 1,
]);
}
}
```
```twig
{# Product item template #}
{# Or emit directly from template #}
```
### Lifecycle Hooks
Use lifecycle hooks to execute code at specific points in the component lifecycle.
```php
userData['lastLogin'] = $this->userRepository->getLastLogin();
}
/**
* Called before component state is serialized to frontend
*/
#[PreDehydrate]
public function preDehydrate(): void
{
// Remove sensitive data before sending to frontend
unset($this->userData['sensitiveField']);
}
/**
* Called before re-rendering (only on AJAX requests)
*/
#[PreReRender]
public function preReRender(): void
{
// Fetch fresh data before each re-render
if ($this->filter) {
$this->cachedResults = $this->searchService->search($this->filter);
}
}
public function getFilteredResults(): array
{
return $this->cachedResults;
}
}
```
### Frontend JavaScript Integration
Access component functionality from JavaScript using the Stimulus controller.
```javascript
// Import the controller
import { Controller } from '@hotwired/stimulus';
import { getComponent } from '@symfony/ux-live-component';
export default class extends Controller {
connect() {
// Get the component instance
const component = getComponent(this.element);
// Listen to component events
this.element.addEventListener('live:connect', (event) => {
console.log('Component connected', event.detail.component);
});
this.element.addEventListener('live:render:finished', (event) => {
console.log('Component re-rendered');
});
// Update model programmatically
component.set('searchTerm', 'New value', true); // true = should render
// Call an action
component.action('save', { arg1: 'value' });
// Emit events
component.emit('custom:event', { data: 'value' });
component.emitUp('bubble:event', { data: 'value' });
component.emitSelf('self:event', { data: 'value' });
}
}
```
### Model Directive Modifiers
Control model binding behavior with directive modifiers for debouncing and event targeting.
```twig
{# Update on input with default debounce #}
{# Update only on change event #}
{# Update on blur event #}
{# Custom debounce (milliseconds) #}
{# No debounce #}
{# Update without re-rendering #}
{# Minimum/maximum length before triggering #}
{# Numeric constraints #}
{# Wildcard model for forms - binds all fields automatically #}
```
### Lazy and Deferred Loading
Load components lazily or defer rendering for better performance.
```twig
{# Lazy load - renders when scrolled into view #}
{# Deferred loading - shows placeholder, then loads #}
Loading...
{# Deferred with custom placeholder #}
{{ component('heavy_chart', {
data: chartData
}, {
loading: 'defer',
loading_template: 'components/_chart_loading.html.twig'
}) }}
{# Polling - refresh every N milliseconds #}
Last updated: {{ "now"|date('H:i:s') }}
```
### Loading States and Indicators
Add loading states to actions and model updates for better UX.
```twig
```
### Component Configuration and Routing
Configure component behavior and customize routing.
```php
add('ux_live_component', '/_components/{_live_component}/{_live_action}')
->defaults([
'_live_action' => 'get',
]);
// Custom route for specific component
$routes->add('app_custom_live_route', '/api/live/{_live_component}/{_live_action}')
->defaults([
'_live_action' => 'get',
]);
};
```
## Summary and Use Cases
Symfony UX Live Components excels in scenarios requiring interactive user interfaces without the complexity of a full JavaScript framework. Common use cases include real-time search interfaces with instant filtering, multi-step forms with progressive validation, shopping carts that update without page refreshes, live dashboards with polling updates, and dynamic form collections that expand or contract based on user input. The library shines when building admin panels, wizard-style interfaces, inline editing functionality, and components that need to communicate through events like notification systems or collaborative features.
Integration patterns typically involve server-side PHP components that extend Symfony controllers, leveraging traits like `ComponentWithFormTrait` for form handling and `ValidatableComponentTrait` for validation. Frontend integration uses Stimulus controllers with data attributes for model binding (`data-model`), action triggering (`data-action="live#action"`), and event emission (`data-action="live#emit"`). The architecture supports nested child components, URL state synchronization for bookmarkable interfaces, custom hydration strategies for complex entities, and seamless Doctrine ORM integration. Advanced patterns include implementing optimistic UI updates, handling file uploads, managing loading states with granular control, and building real-time collaborative features using event broadcasting between multiple component instances.