Symfony Live Components i UX 3.0: Reaktywne Aplikacje Bez JavaScript w 2026

Symfony Live Components pozwalaja budowac reaktywne interfejsy w PHP i Twig bez pisania JavaScript. Przewodnik z LiveProp, LiveAction, formularzami i odroczonym ladowaniem w UX 3.0.

Diagram architektury Symfony Live Components z przeplywem danych miedzy serwerem PHP a reaktywnym interfejsem uzytkownika

Budowanie reaktywnych interfejsow webowych tradycyjnie wymagalo znacznych nakladow pracy po stronie frontendu -- frameworki JavaScript, zarzadzanie stanem, synchronizacja danych miedzy klientem a serwerem. Symfony Live Components calkowicie odwracaja ten paradygmat, oferujac mozliwosc tworzenia dynamicznych komponentow z aktualizacjami w czasie rzeczywistym, walidacja formularzy i plynnym doswiadczeniem uzytkownika, a wszystko to bez opuszczania ekosystemu PHP i Twig. Wraz z premiera Symfony UX 3.0 w 2026 roku, biblioteka osiagnela pelna dojrzalosc produkcyjna i stala sie standardem w spolecznosci Symfony.

Niniejszy artykul przedstawia kluczowe koncepcje Live Components, demonstruje praktyczne przypadki uzycia -- od wyszukiwania produktow po koszyk zakupowy i formularze z walidacja w czasie rzeczywistym -- oraz omawia najwazniejsze zmiany wprowadzone w UX 3.0.

Jak dzialaja Live Components?

Live Components to komponenty Twig, ktore automatycznie aktualizuja sie w przegladarce po zmianie ich wlasciwosci. Wykorzystuja zapytania AJAX do komunikacji z serwerem bez przeladowywania strony. Symfony renderuje ponownie wylacznie te fragmenty szablonu, ktore ulegly zmianie, co tworzy plynne doswiadczenie uzytkownika porownywalne z aplikacjami SPA -- przy zachowaniu calej logiki biznesowej po stronie backendu.

Instalacja i konfiguracja Symfony UX 3.0

Pierwszym krokiem jest zainstalowanie pakietu symfony/ux-live-component. Wersja 3.0 wprowadzila pelna kompatybilnosc z PHP 8.4+ i Symfony 7.4+, a takze natywna integracje z AssetMapper, co eliminuje koniecznosc konfigurowania Webpack Encore lub Vite do zarzadzania zasobami frontendowymi.

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

Po instalacji polecenie debug:twig powinno wyswietlic liste wszystkich dostepnych komponentow Twig w projekcie. AssetMapper radykalnie upraszcza konfiguracje, poniewaz calkowicie eliminuje potrzebe stosowania kompilatorow JavaScript. Caly niezbedny kod JavaScript jest dolaczany automatycznie -- programista nie musi pisac ani jednej linii kodu frontendowego.

Reaktywne wyszukiwanie produktow z LiveProp

Jednym z najczesciej spotykanych przypadkow uzycia Live Components jest budowanie systemow wyszukiwania, ktore aktualizuja wyniki w czasie rzeczywistym w miare wpisywania zapytania przez uzytkownika. Ponizszy przyklad przedstawia komponent wyszukiwania produktow z dynamicznym filtrowaniem i synchronizacja z adresem URL.

php
<?php
// src/Twig/Components/ProductSearch.php

namespace App\Twig\Components;

use App\Repository\ProductRepository;
use Symfony\UX\LiveComponent\Attribute\AsLiveComponent;
use Symfony\UX\LiveComponent\Attribute\LiveProp;
use Symfony\UX\LiveComponent\DefaultActionTrait;

#[AsLiveComponent]
class ProductSearch
{
    use DefaultActionTrait;

    // writable: true allows the frontend to modify this property
    // url: true syncs the value with ?query= in the address bar
    #[LiveProp(writable: true, url: true)]
    public string $query = '';

    #[LiveProp(writable: true)]
    public string $category = 'all';

    public function __construct(
        private ProductRepository $productRepository,
    ) {
    }

    public function getProducts(): array
    {
        if (strlen($this->query) < 2) {
            return [];
        }

        return $this->productRepository->search(
            query: $this->query,
            category: $this->category === 'all' ? null : $this->category,
            limit: 20,
        );
    }
}

Atrybut #[AsLiveComponent] przeksztalca klase PHP w dynamiczny komponent. Wlasciwosci oznaczone #[LiveProp(writable: true)] moga byc modyfikowane przez frontend za pomoca atrybutow data-model w HTML. Opcja url: true dodaje funkcjonalnosc deep linkingu -- wartosc wlasciwosci jest automatycznie synchronizowana z parametrem ?query= w pasku adresu przegladarki, co pozwala uzytkownikom udostepniac linki z zastosowanymi filtrami.

Odpowiadajacy szablon Twig wykorzystuje dyrektywe data-model do powiazania pol formularza z wlasciwosciami komponentu:

twig
{# templates/components/ProductSearch.html.twig #}
<div {{ attributes }}>
    <input
        type="search"
        data-model="debounce(300)|query"
        placeholder="Search products..."
        class="form-input w-full"
    />

    <select data-model="category" class="form-select mt-2">
        <option value="all">All categories</option>
        <option value="electronics">Electronics</option>
        <option value="books">Books</option>
    </select>

    <div class="mt-4">
        {% for product in this.products %}
            <div key="{{ product.id }}" class="border-b py-2">
                <h3>{{ product.name }}</h3>
                <span>{{ product.price|format_currency('EUR') }}</span>
            </div>
        {% else %}
            {% if query|length >= 2 %}
                <p>No results for "{{ query }}".</p>
            {% endif %}
        {% endfor %}
    </div>
</div>

Modyfikator debounce(300) gwarantuje, ze zapytanie do serwera zostanie wyslane dopiero po 300 milisekundach nieaktywnosci uzytkownika, co zapobiega przeciazeniu backendu nadmierna liczba zapytan. Ta technika optymalizacji jest czesto poruszana podczas pytan na rozmowe kwalifikacyjna z Symfony dotyczacych wydajnosci aplikacji.

Synchronizacja z URL

Opcja url: true w #[LiveProp] automatycznie aktualizuje parametry adresu URL w przegladarce. Dzieki temu uzytkownik moze skopiowac link z aktualnymi filtrami i wyslac go innej osobie -- po otwarciu adresu komponent odtworzy dokladnie ten sam stan wyszukiwania. Ta funkcjonalnosc jest dostepna bez dodatkowej konfiguracji.

Interaktywny koszyk zakupowy z LiveAction

Podczas gdy LiveProp zarzadza reaktywnymi wlasciwosciami, atrybut #[LiveAction] umozliwia tworzenie metod odpowiadajacych na akcje uzytkownika, takie jak klikniecia przyciskow. Ponizszy komponent koszyka zakupowego demonstruje koordynacje wielu akcji oraz komunikacje miedzy komponentami.

php
<?php
// src/Twig/Components/ShoppingCart.php

namespace App\Twig\Components;

use App\Entity\CartItem;
use App\Service\CartService;
use Symfony\UX\LiveComponent\Attribute\AsLiveComponent;
use Symfony\UX\LiveComponent\Attribute\LiveAction;
use Symfony\UX\LiveComponent\Attribute\LiveArg;
use Symfony\UX\LiveComponent\Attribute\LiveProp;
use Symfony\UX\LiveComponent\ComponentToolsTrait;
use Symfony\UX\LiveComponent\DefaultActionTrait;

#[AsLiveComponent]
class ShoppingCart
{
    use DefaultActionTrait;
    use ComponentToolsTrait;

    #[LiveProp]
    public array $items = [];

    public function __construct(
        private CartService $cartService,
    ) {
    }

    #[LiveAction]
    public function addItem(#[LiveArg] int $productId, #[LiveArg] int $quantity = 1): void
    {
        $this->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,
        ));
    }
}

Metody oznaczone #[LiveAction] sa wywolywane bezposrednio z szablonu Twig. Argumenty sa przekazywane przez #[LiveArg] z automatyczna deserializacja i walidacja typow. Metoda emit() udostepniona przez ComponentToolsTrait pozwala komponentowi emitowac zdarzenia, ktore inne Live Components na tej samej stronie moga nasluchiwac -- tworzy to architekture pub/sub bez bezposredniego sprzegania komponentow.

twig
{# templates/components/ShoppingCart.html.twig #}
<div {{ attributes }}>
    <h2>Cart ({{ items|length }})</h2>

    {% for item in items %}
        <div key="{{ item.id }}" class="flex justify-between py-2">
            <span>{{ item.name }} x{{ item.quantity }}</span>
            <span>{{ (item.price * item.quantity)|format_currency('EUR') }}</span>
            <button {{ live_action('removeItem', { itemId: item.id }) }}
                    class="text-red-600">
                Remove
            </button>
        </div>
    {% endfor %}

    <div class="border-t pt-2 font-bold">
        Total: {{ this.total|format_currency('EUR') }}
    </div>
</div>

Funkcja Twig live_action() generuje niezbedne atrybuty HTML do powiazania klikniec przyciskow z metodami #[LiveAction] w komponencie PHP. Kazda interakcja uzytkownika wywoluje zapytanie AJAX, ktore uruchamia odpowiednia metode na serwerze, aktualizuje stan komponentu i ponownie renderuje wylacznie te fragmenty DOM, ktore ulegly zmianie.

Gotowy na rozmowy o Symfony?

Ćwicz z naszymi interaktywnymi symulatorami, flashcards i testami technicznymi.

Walidacja formularzy w czasie rzeczywistym

Jedna z najbardziej zaawansowanych funkcji Live Components jest natywna integracja z systemem formularzy Symfony. Trait ComponentWithFormTrait pozwala tworzyc formularze z walidacja w czasie rzeczywistym bez przeladowywania strony, utrzymujac cala logike walidacji scentralizowana w kodzie PHP.

php
<?php
// src/Twig/Components/RegistrationForm.php

namespace App\Twig\Components;

use App\Entity\User;
use App\Form\RegistrationType;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Form\FormInterface;
use Symfony\UX\LiveComponent\Attribute\AsLiveComponent;
use Symfony\UX\LiveComponent\Attribute\LiveAction;
use Symfony\UX\LiveComponent\Attribute\LiveProp;
use Symfony\UX\LiveComponent\ComponentWithFormTrait;
use Symfony\UX\LiveComponent\DefaultActionTrait;

#[AsLiveComponent]
class RegistrationForm extends AbstractController
{
    use DefaultActionTrait;
    use ComponentWithFormTrait;

    #[LiveProp]
    public ?User $initialFormData = null;

    protected function instantiateForm(): FormInterface
    {
        return $this->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');
    }
}

Metoda instantiateForm() jest automatycznie wywolywana przez trait i powinna zwracac instancje formularza Symfony. Wywolanie submitForm() uruchamia pelna walidacje zgodnie z ograniczeniami (constraints) zdefiniowanymi w formularzu. Jesli walidacja sie nie powiedzie, komponent jest ponownie renderowany z komunikatami bledow wyswietlanymi bezposrednio przy odpowiednich polach -- bez zadnego kodu JavaScript.

twig
{# templates/components/RegistrationForm.html.twig #}
<div {{ attributes }}>
    {{ 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) }}

        <button type="submit"
                class="btn-primary w-full"
                data-loading="addAttribute(disabled) addClass(opacity-50)">
            <span data-loading="hide">Create Account</span>
            <span data-loading="show" class="animate-spin">Loading...</span>
        </button>
    {{ form_end(form) }}
</div>

Atrybut data-loading kontroluje stany wizualne podczas zapytan AJAX -- przycisk zostaje zablokowany i zmienia wyglad, a tekst jest zastepowany animacja ladowania. Ten mechanizm zapewnia natychmiastowy feedback wizualny uzytkownikowi podczas operacji asynchronicznych, co znaczaco poprawia postrzegana jakosc interfejsu.

Odroczone ladowanie i lazy loading komponentow

Komponenty wykonujace ciezkie zapytania do bazy danych lub wywolania zewnetrznych API moga negatywnie wplywac na czas poczatkowego ladowania strony. UX 3.0 wprowadza zaawansowane strategie odroczonego ladowania, ktore znaczaco poprawiaja postrzegana wydajnosc aplikacji oraz wskazniki Core Web Vitals.

php
<?php
// src/Twig/Components/AnalyticsDashboard.php

namespace App\Twig\Components;

use App\Service\AnalyticsService;
use Symfony\UX\LiveComponent\Attribute\AsLiveComponent;
use Symfony\UX\LiveComponent\Attribute\LiveProp;
use Symfony\UX\LiveComponent\DefaultActionTrait;

#[AsLiveComponent]
class AnalyticsDashboard
{
    use DefaultActionTrait;

    #[LiveProp]
    public string $period = '30d';

    public function __construct(
        private AnalyticsService $analytics,
    ) {
    }

    public function getMetrics(): array
    {
        // Expensive query — runs only after page load
        return $this->analytics->getMetrics($this->period);
    }
}

Aby aktywowac odroczone ladowanie, nalezy uzyc atrybutu loading podczas osadzania komponentu w szablonie nadrzednym:

twig
{# Parent page: load the dashboard after the page renders #}
<twig:AnalyticsDashboard loading="defer" period="30d" />

{# Or load when scrolled into view #}
<twig:AnalyticsDashboard loading="lazy" period="30d" />

Tryb loading="defer" renderuje poczatkowo pusty placeholder, a nastepnie laduje wlasciwa zawartosc komponentu przez AJAX natychmiast po zaladowaniu DOM. Tryb loading="lazy" wykorzystuje Intersection Observer API do zaladowania komponentu dopiero wtedy, gdy wejdzie on w widoczny obszar przegladarki (viewport). Obie strategie utrzymuja szybki czas poczatkowej odpowiedzi serwera.

Aby dostosowac placeholder wyswietlany podczas ladowania, nalezy zdefiniowac makro placeholder w szablonie komponentu:

twig
{# templates/components/AnalyticsDashboard.html.twig #}
<div {{ attributes }}>
    <h2>Analytics — {{ period }}</h2>
    {% for metric in this.metrics %}
        <div class="stat-card">
            <span class="stat-label">{{ metric.label }}</span>
            <span class="stat-value">{{ metric.value|number_format }}</span>
        </div>
    {% endfor %}
</div>

{% macro placeholder(props) %}
    <div class="animate-pulse space-y-4">
        <div class="h-6 bg-gray-200 rounded w-1/3"></div>
        <div class="h-20 bg-gray-200 rounded"></div>
        <div class="h-20 bg-gray-200 rounded"></div>
    </div>
{% endmacro %}

Skeleton screens znaczaco poprawiaja postrzegana szybkosc aplikacji, pokazujac oczekiwana strukture zanim rzeczywiste dane zostana pobrane. Ta technika jest fundamentalna przy optymalizacji aplikacji z rozbudowanymi dashboardami i raportami -- temat czesto poruszany podczas pytan na rozmowe kwalifikacyjna z Symfony dotyczacych wydajnosci.

Usuniecie ochrony CSRF w UX 3.0

Symfony UX 3.0 zastepuje tokeny CSRF ochrona same-origin/CORS dla Live Components. Argument csrf w #[AsLiveComponent] nie istnieje juz w wersji 3.0. Serwer automatycznie stosuje polityki same-origin, co upraszcza konfiguracje, ale wymaga poprawnego ustawienia naglowkow CORS w srodowisku produkcyjnym.

Komunikacja miedzy komponentami z LiveListener

W zlozonych aplikacjach wiele komponentow musi ze soba wspolpracowac. System zdarzen Live Components umozliwia te komunikacje bez tworzenia bezposrednich zaleznosci. Atrybut #[LiveListener] definiuje metody, ktore sa automatycznie wywolywane w momencie emisji okreslonych zdarzen.

php
<?php
// src/Twig/Components/CartBadge.php

namespace App\Twig\Components;

use App\Service\CartService;
use Symfony\UX\LiveComponent\Attribute\AsLiveComponent;
use Symfony\UX\LiveComponent\Attribute\LiveListener;
use Symfony\UX\LiveComponent\Attribute\LiveProp;
use Symfony\UX\LiveComponent\DefaultActionTrait;

#[AsLiveComponent]
class CartBadge
{
    use DefaultActionTrait;

    #[LiveProp]
    public int $count = 0;

    public function __construct(CartService $cartService)
    {
        $this->count = $cartService->getItemCount();
    }

    // Automatically re-renders when ShoppingCart emits 'cart:updated'
    #[LiveListener('cart:updated')]
    public function onCartUpdated(#[LiveArg] int $count): void
    {
        $this->count = $count;
    }
}

Gdy komponent ShoppingCart dodaje lub usuwa pozycje z koszyka, emituje zdarzenie cart:updated z aktualna liczba elementow. Komponent CartBadge, ktory moze znajdowac sie w dowolnym miejscu na stronie (najczesciej w naglowku nawigacji), nasluchuje tego zdarzenia i automatycznie aktualizuje swoj licznik -- bez koniecznosci pisania dodatkowego kodu JavaScript i bez przeladowywania strony.

Ta architektura oparta na zdarzeniach promuje luzne wiazanie miedzy komponentami, ulatwia ich ponowne wykorzystanie i znaczaco upraszcza utrzymanie zlozonych interfejsow. To wzorzec, ktory jest fundamentalny w nowoczesnym rozwoju aplikacji Symfony.

Najwazniejsze zmiany w Symfony UX 3.0

Przejscie z UX 2.x na wersje 3.0 przynosi istotne usprawnienia w zakresie bezpieczenstwa, wydajnosci i doswiadczenia programisty, ale wymaga rowniez pewnych modyfikacji w istniejacym kodzie. Ponizsze zestawienie podsumowuje kluczowe roznice:

| 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+ |

Usuniecie pakietow takich jak Swup i LazyImage odzwierciedla dojrzalosc natywnych API wspolczesnych przegladarek. Programisci powinni migrowac do Intersection Observer API (dla lazy loadingu) oraz View Transitions API (dla plynnej nawigacji miedzy stronami). Funkcja html_cva() zastepuje dawna cva() i wymaga instalacji pakietu twig/html-extra w wersji 3.12 lub nowszej.

Podniesienie wymagan minimalnych do PHP 8.4+ i Symfony 7.4+ pozwala UX 3.0 wykorzystywac nowoczesne funkcje jezyka, takie jak property hooks, ulepszone enumeracje oraz poprawki wydajnosci kompilatora JIT. Dla projektow pozostajacych na wczesniejszych wersjach oficjalny przewodnik migracji szczegolowo opisuje proces stopniowej aktualizacji.

Kiedy stosowac Live Components, a kiedy JavaScript

Pomimo licznych zalet, Live Components nie zastepuja frameworkow JavaScript we wszystkich scenariuszach. Decyzja zalezy od charakteru tworzonej aplikacji.

Live Components sprawdzaja sie doskonale w aplikacjach ze zlozonej logika biznesowa wykonywanej po stronie serwera, w zespolach zorientowanych na backend, ktore nie chca utrzymywac dwoch oddzielnych baz kodu, w formularzach z zaawansowana walidacja wymagajaca dostepu do bazy danych oraz w panelach administracyjnych z operacjami CRUD. Z kolei pelny framework JavaScript pozostaje lepszym wyborem dla aplikacji wymagajacych trybu offline lub zaawansowanych funkcji PWA, interfejsow z rozbudowanymi animacjami oraz edytorow wizualnych dzialajacych w czasie rzeczywistym.

W wielu projektach optymalne okazuje sie polaczenie obu podejsc: Live Components dla wiekszosci funkcjonalnosci aplikacji, a JavaScript dla elementow wymagajacych maksymalnej interaktywnosci. Integracja z API Platform na Symfony 7 pozwala laczyc oba podejscia w spojnej architekturze.

Znajmosc Live Components staje sie tez coraz wazniejszym wyroznikiem w procesach rekrutacyjnych na stanowiska zwiazane z Symfony. Typowe pytania na rozmowe kwalifikacyjna z Symfony dotycza cyklu zycia komponentu, roznic miedzy #[LiveProp] z opcja writable: true i bez niej, strategii optymalizacji z lazy i defer oraz obslugi formularzy z ComponentWithFormTrait. Poglebiony przewodnik po pytaniach na rozmowe z Symfony stanowi uzupelnienie wiedzy praktycznej przedstawionej w tym artykule.

Podsumowanie

Symfony Live Components i UX 3.0 reprezentuja fundamentalna zmiane w podejsciu do budowania interfejsow webowych w PHP. W 2026 roku technologia ta osiagnela pelna dojrzalosc i pozycjonuje sie jako realna alternatywa dla zespolow poszukujacych produktywnosci bez zbednej zlozonosci.

  • Jeden jezyk: PHP do calej logiki frontendu i backendu, eliminujacy koniecznosc przelaczania kontekstu
  • Natywne SEO: Renderowanie po stronie serwera domyslnie, bez dodatkowej konfiguracji
  • Zintegrowane bezpieczenstwo: Walidacja i logika biznesowa chronione na serwerze
  • Zoptymalizowana wydajnosc: Czesciowe aktualizacje DOM z lazy loadingiem i odroczonym ladowaniem
  • Gleboka integracja: Bezposredni dostep do serwisow, ORM i calego ekosystemu Symfony
  • Zdarzenia bez sprzegania: System pub/sub (emit / #[LiveListener]) do komunikacji miedzy komponentami

Dla wiekszosci aplikacji webowych klasy enterprise -- dashboardow administracyjnych, portali tresci i systemow zarzadzania -- Live Components oferuja idealny balans miedzy nowoczesna interaktywnoscia a architektura backend-first. Pytanie nie brzmi juz, czy Live Components nadaja sie do srodowiska produkcyjnego, ale w jakich konkretnych przypadkach pelny framework JavaScript uzasadnia swoja dodatkowa zlozonosc.

Zacznij ćwiczyć!

Sprawdź swoją wiedzę z naszymi symulatorami rozmów i testami technicznymi.

Tagi

#symfony
#live components
#symfony ux
#php
#twig
#reactive ui

Udostępnij

Powiązane artykuły