# 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 #}

Cart ({{ items|length }})

{% for item in items %}
{{ item.name }} x{{ item.quantity }} {{ (item.price * item.quantity)|format_currency('EUR') }}
{% endfor %}
Total: {{ this.total|format_currency('EUR') }}
``` 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 #}
{{ form_start(form, { attr: { 'data-action': 'live#action:prevent', 'data-live-action-param': 'save' } }) }} {{ form_row(form.email) }} {{ form_row(form.plainPassword, { label: 'Password' }) }} {{ form_row(form.fullName) }} {{ form_end(form) }}
``` 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 #}

Analytics — {{ period }}

{% for metric in this.metrics %}
{{ metric.label }} {{ metric.value|number_format }}
{% endfor %}
{% macro placeholder(props) %}
{% 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