# Symfony Live Components and UX 3.0: Reactive Apps Without JavaScript in 2026
> Build reactive, real-time interfaces with Symfony Live Components and UX 3.0 — no JavaScript framework required. Complete tutorial with LiveProp, LiveAction, form handling, deferred loading, and URL binding.
- Published: 2026-05-10
- Updated: 2026-05-10
- Author: SharpSkill
- Tags: symfony, live components, symfony ux, php, twig, reactive ui
- Reading time: 10 min
---
Symfony Live Components deliver reactive, real-time user interfaces entirely in PHP and Twig. Released in April 2026, [Symfony UX 3.0](https://symfony.com/blog/symfony-ux-3-0-0-released) removes all 2.x deprecations, raises the minimum to PHP 8.4 and Symfony 7.4, and trims four obsolete packages — producing the leanest UX toolkit to date.
> **What Are Live Components?**
>
> Live Components turn any Twig component into a stateful, interactive element that re-renders server-side on user interaction — without writing JavaScript. Data binding, actions, validation, and form handling all happen in PHP.
## Setting Up Symfony UX 3.0 with Live Components
The installation requires Symfony 7.4+, PHP 8.4+, and either AssetMapper or Webpack Encore for the frontend assets.
```bash
# terminal
composer require symfony/ux-live-component
# With AssetMapper (recommended for Symfony 7.4+)
php bin/console importmap:require @symfony/ux-live-component
# Verify installation
php bin/console debug:twig --filter=component
```
AssetMapper handles JavaScript delivery without Node.js, Webpack, or any build step. The Stimulus controller that powers Live Components loads automatically.
## Creating a Reactive Search Component with LiveProp
A search bar that filters results as the user types — zero JavaScript. The `#[LiveProp]` attribute marks properties as stateful, persisted across re-renders through an encrypted URL-safe token.
```php
query) < 2) {
return [];
}
return $this->productRepository->search(
query: $this->query,
category: $this->category === 'all' ? null : $this->category,
limit: 20,
);
}
}
```
The corresponding Twig template binds inputs to properties with `data-model`.
```twig
{# templates/components/ProductSearch.html.twig #}
{% for product in this.products %}
{{ product.name }}
{{ product.price|format_currency('EUR') }}
{% else %}
{% if query|length >= 2 %}
No results for "{{ query }}".
{% endif %}
{% endfor %}
```
The `debounce(300)` modifier waits 300ms after the last keystroke before sending the request. The `key` attribute on list items enables efficient DOM morphing — only changed elements are updated.
> **URL Binding with LiveProp**
>
> Setting `url: true` on a LiveProp syncs the property value with the browser URL as a query parameter. Bookmarkable, shareable search results — with one attribute.
## Handling User Actions with LiveAction
Beyond data binding, `#[LiveAction]` exposes PHP methods as callable actions from the frontend. Arguments pass through `#[LiveArg]`.
```php
cartService->add($productId, $quantity);
$this->items = $this->cartService->getItems();
// Notify other components on the page
$this->emit('cart:updated', [
'count' => count($this->items),
]);
}
#[LiveAction]
public function removeItem(#[LiveArg] int $itemId): void
{
$this->cartService->remove($itemId);
$this->items = $this->cartService->getItems();
$this->emit('cart:updated', ['count' => count($this->items)]);
}
public function getTotal(): float
{
return array_sum(array_map(
fn(CartItem $item) => $item->getPrice() * $item->getQuantity(),
$this->items,
));
}
}
```
The template triggers actions with the `live_action()` helper or `data-action` attributes.
```twig
{# templates/components/ShoppingCart.html.twig #}
```
The `emit()` method broadcasts events to other Live Components on the page — a cart badge component, for instance, can listen with `#[LiveListener('cart:updated')]` and update its count without a full page reload.
## Real-Time Form Validation Without JavaScript
Live Components integrate directly with [Symfony Forms](https://symfony.com/doc/current/forms.html). The `ComponentWithFormTrait` connects form state, validation errors, and submission to the component lifecycle.
```php
createForm(RegistrationType::class, $this->initialFormData ?? new User());
}
#[LiveAction]
public function save(EntityManagerInterface $em): mixed
{
// Submits + validates — re-renders with errors if invalid
$this->submitForm();
$user = $this->getForm()->getData();
$em->persist($user);
$em->flush();
$this->addFlash('success', 'Account created.');
return $this->redirectToRoute('app_login');
}
}
```
```twig
{# templates/components/RegistrationForm.html.twig #}
```
Validation errors appear inline as the user moves between fields — the `on(change)` modifier on `data-model` triggers re-render on blur, displaying Symfony Validator constraint messages instantly.
## Deferred and Lazy Loading for Performance
Heavy components — dashboards, analytics charts, long lists — benefit from deferred rendering. Instead of blocking the initial page load, the component renders a placeholder and fetches content via AJAX after the page is ready.
```php
analytics->getMetrics($this->period);
}
}
```
```twig
{# Parent page: load the dashboard after the page renders #}
{# Or load when scrolled into view #}
```
The placeholder macro inside the component template defines what users see while the real content loads.
```twig
{# templates/components/AnalyticsDashboard.html.twig #}
{% endmacro %}
```
The `loading="defer"` mode fires an AJAX request immediately on page load. The `loading="lazy"` mode uses IntersectionObserver — the request fires only when the component scrolls into the viewport. Both approaches keep the initial page response fast.
> **CSRF Removed in UX 3.0**
>
> Symfony UX 3.0 replaces CSRF tokens with same-origin/CORS protection for Live Components. The `csrf` argument on `#[AsLiveComponent]` no longer exists. Ensure the server enforces same-origin policies.
## Component Communication with Events
Live Components communicate through an event system. A child component emits an event; a parent (or sibling) listens and reacts.
```php
count = $cartService->getItemCount();
}
// Automatically re-renders when ShoppingCart emits 'cart:updated'
#[LiveListener('cart:updated')]
public function onCartUpdated(#[LiveArg] int $count): void
{
$this->count = $count;
}
}
```
This pattern keeps components decoupled. The ShoppingCart component does not reference CartBadge directly — the event bus handles the connection.
## What Changed in Symfony UX 3.0
UX 3.0 is a cleanup release. Applications running without deprecation warnings on 2.x upgrade with minimal friction.
| Change | Before (2.x) | After (3.0) |
|--------|-------------|-------------|
| CSRF protection | `csrf: true` on `#[AsLiveComponent]` | Same-origin/CORS (automatic) |
| Twig CVA function | `cva()` | `html_cva()` from twig/html-extra 3.12+ |
| Component defaults config | Optional | `twig_component.defaults` mandatory |
| Removed packages | Swup, LazyImage, Typed, TogglePassword | Use native APIs or UX Toolkit |
| PHP requirement | 8.1+ | 8.4+ |
| Symfony requirement | 6.4+ | 7.4+ |
The [UX Toolkit](https://symfony.com/blog/symfonyux-2-32-0-released), introduced during the 2.x cycle, provides pre-built UI components (Button, Dialog, Card, Table, Pagination) styled with Shadcn UI or Flowbite 4.0 — covering the gap left by removed packages.
Preparing [Symfony interview questions](/technologies/symfony/interview-questions/events-subscribers)? Understanding Live Components and the event system is increasingly relevant for senior-level positions. The [Symfony interview questions guide](/blog/symfony/symfony-interview-questions) covers foundational topics that pair well with this hands-on knowledge.
## Conclusion
- Live Components eliminate the need for a JavaScript framework in most CRUD-heavy Symfony applications. Data binding, actions, validation, and form handling stay in PHP and Twig
- `#[LiveProp(writable: true, url: true)]` creates bookmarkable, shareable stateful interfaces with one attribute
- Deferred and lazy loading (`loading="defer"` / `loading="lazy"`) keep initial page loads fast while heavy components render asynchronously
- UX 3.0 drops CSRF tokens in favor of same-origin/CORS protection — simpler security with fewer moving parts
- The event system (`emit` / `#[LiveListener]`) enables decoupled component communication without global state management
- For APIs, complement Live Components with [API Platform on Symfony 7](/blog/symfony/symfony-7-api-platform-best-practices) for backend-heavy architectures
---
Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack.
HTML version of this page: https://sharpskill.dev/en/blog/symfony/symfony-live-components-ux-3-reactive-apps-without-javascript