Symfony Live Components та UX 3.0: Реактивні Застосунки Без JavaScript у 2026

Symfony Live Components дозволяють створювати реактивні інтерфейси на PHP та Twig — без JavaScript. Практичний посібник з LiveProp, LiveAction, формами та відкладеним завантаженням.

Ілюстрація архітектури Symfony Live Components зі схемою реактивного потоку даних між PHP-компонентами та інтерфейсом користувача

Сучасна веб-розробка часто вимагає від PHP-розробників опановувати складні JavaScript-фреймворки для побудови інтерактивних інтерфейсів. Symfony Live Components докорінно змінюють цю парадигму, надаючи можливість створювати реактивні інтерфейси користувача виключно засобами PHP та Twig. З випуском Symfony UX 3.0 на початку 2026 року ця server-first архітектура досягла повної зрілості та перетворилася на повноцінну альтернативу класичним Single Page Applications.

Традиційний підхід до реактивності передбачає використання React, Vue або іншого JavaScript-фреймворку на клієнтській стороні, а також окремого API на сервері. Live Components пропонують принципово іншу модель: вся логіка залишається на сервері у вигляді звичайних PHP-класів, а бібліотека автоматично синхронізує стан через невеликі AJAX-запити. Розробник пише лише PHP та Twig -- решту бере на себе фреймворк.

У цьому матеріалі розглядаються Live Components через призму практичних прикладів продакшн-рівня, аналізуються ключові нововведення UX 3.0 та демонструється, як ця технологія трансформує архітектуру веб-застосунків PHP у 2026 році.

Ключовий принцип

Live Components побудовані на механізмі автоматичної синхронізації: кожна зміна властивості, позначеної #[LiveProp(writable: true)], ініціює AJAX-запит до сервера. Symfony перераховує стан компонента та повертає лише змінені фрагменти HTML, забезпечуючи плавну реактивність без повного перезавантаження сторінки.

Встановлення та налаштування UX 3.0

Версія 3.0 бандлу UX суттєво спрощує конфігурацію завдяки використанню AssetMapper як основного менеджера залежностей. Такий підхід повністю усуває складнощі Webpack або Vite, зберігаючи при цьому повну функціональність сучасної системи збирання ресурсів.

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

Команда debug:twig дозволяє переконатися, що всі компоненти коректно зареєстровані у системі. AssetMapper автоматично керує JavaScript-залежностями бандлу без додаткового налаштування -- значне покращення порівняно з попередніми версіями, які потребували ручної конфігурації Webpack Encore або іншого збирача фронтенду. Після успішного встановлення Live Components готові до використання без жодних додаткових кроків конфігурації.

Архітектура UX 3.0 також вимагає PHP 8.4+ та Symfony 7.4+, що дозволяє використовувати останні можливості мови: readonly-властивості, покращені enum-типи та оптимізації JIT-компілятора, які помітно прискорюють виконання компонентів.

Пошук у реальному часі із синхронізацією URL

Миттєвий пошук -- це класичний приклад застосування Live Components. Наступна реалізація демонструє роботу з реактивністю, debouncing та синхронізацією з адресним рядком для можливості поширення відфільтрованих результатів.

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,
        );
    }
}

Атрибут #[AsLiveComponent] реєструє клас як реактивний компонент. Публічні властивості з анотацією #[LiveProp(writable: true)] стають доступними для модифікації з боку шаблону через HTML-атрибут data-model. Опція url: true вмикає двосторонню синхронізацію з адресним рядком браузера -- функціональність, критично важлива для SEO та для можливості поширення посилань із застосованими фільтрами.

Відповідний Twig-шаблон ілюструє декларативний синтаксис прив'язки даних:

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>

Модифікатор debounce(300) обмежує кількість запитів до сервера, очікуючи 300 мс після останнього натискання клавіші. Ця техніка оптимізації має вирішальне значення для запобігання перевантаження бекенду та регулярно зустрічається у питаннях для співбесіди з Symfony, що стосуються продуктивності.

Синхронізація з URL

Використання url: true у #[LiveProp] автоматично оновлює параметри GET-запиту в адресному рядку. Це дозволяє користувачам копіювати URL із застосованими фільтрами та ділитися ним, а також забезпечує коректну роботу кнопок "Назад" та "Вперед" у браузері.

Використання this.products у шаблоні автоматично викликає метод getProducts() PHP-компонента, демонструючи прозору інтеграцію між серверною логікою та рендерингом. Атрибут key на кожному елементі списку дозволяє Symfony ефективно відстежувати зміни у DOM та оновлювати лише ті елементи, що дійсно змінилися, мінімізуючи кількість маніпуляцій з DOM-деревом.

Компонент кошика з діями та подіями

Крім реактивних властивостей, Live Components надають можливість оголошувати серверні методи через атрибут #[LiveAction]. Наступний компонент кошика покупок демонструє, як організувати кілька дій користувача та комунікацію між компонентами, розташованими у різних частинах сторінки.

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,
        ));
    }
}

Трейт ComponentToolsTrait надає метод emit(), який транслює глобальні події на сторінці. Така роз'єднана архітектура дозволяє іншим компонентам реагувати на зміни кошика без створення прямих залежностей -- фундаментальний принцип побудови підтримуваних застосунків.

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>

Функція live_action() у Twig автоматично генерує необхідні data-action атрибути для виклику PHP-методів, позначених #[LiveAction]. Цей декларативний підхід повністю усуває потребу у написанні JavaScript-обробників подій вручну. Атрибут #[LiveArg] на параметрах методу дозволяє передавати аргументи безпосередньо з шаблону, що робить взаємодію між Twig та PHP максимально зрозумілою та типобезпечною.

Готовий до співбесід з Symfony?

Практикуйся з нашими інтерактивними симуляторами, flashcards та технічними тестами.

Інтеграція з Symfony Forms та валідація в реальному часі

Одна з найпотужніших можливостей Live Components -- нативна інтеграція з системою форм Symfony. Трейт ComponentWithFormTrait автоматично координує відправку, валідацію та рендеринг помилок, забезпечуючи серверну валідацію в реальному часі без перезавантаження сторінки.

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');
    }
}

Метод instantiateForm() створює стандартний FormType Symfony. Трейт перехоплює зміни полів і автоматично запускає серверну валідацію, відображаючи помилки в реальному часі саме так, як вони визначені у правилах валідації Symfony. Метод submitForm() виконує повну валідацію та, у разі невдачі, повторно рендерить компонент з повідомленнями про помилки.

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>

Атрибут data-loading керує умовним відображенням елементів під час AJAX-запитів. Ця функціональність покращує UX, надаючи миттєвий візуальний зворотний зв'язок при асинхронних операціях: кнопка деактивується та відображає індикатор завантаження, доки сервер обробляє запит.

Зміна CSRF-захисту в UX 3.0

У версії 3.0 явний CSRF-захист через csrf: true на #[AsLiveComponent] замінено автоматичною перевіркою Same-Origin/CORS. Це спрощує конфігурацію, проте слід переконатися, що серверне середовище коректно налаштоване для перевірки заголовків Origin. Під час міграції з 2.x необхідно видалити явні параметри CSRF з атрибутів компонентів.

Відкладене завантаження та оптимізація продуктивності

Компоненти, що виконують ресурсомісткі запити до бази даних або зовнішніх API, можуть суттєво уповільнити початкове завантаження сторінки. UX 3.0 впроваджує розвинуті стратегії відкладеного завантаження (lazy loading), які значно покращують суб'єктивну швидкість роботи застосунку.

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);
    }
}

Параметр loading визначає момент завантаження компонента:

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" />

Опція defer відкладає завантаження до повної побудови DOM-дерева сторінки. Опція lazy використовує Intersection Observer API та завантажує компонент лише тоді, коли він потрапляє у видиму область вікна браузера -- техніка, особливо ефективна для дашбордів з великою кількістю віджетів.

Плейсхолдер, що відображається під час завантаження, визначається через макрос Twig:

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

Цей підхід дозволяє миттєво відображати структуру сторінки, відкладаючи виконання важких обчислень на потім. Це безпосередньо впливає на метрики Core Web Vitals, які мають критичне значення для SEO-просування. Скелетон-анімація з класом animate-pulse створює візуальне очікування, що суттєво покращує суб'єктивне сприйняття швидкості порівняно з порожнім екраном або статичним спінером.

Комунікація між компонентами через LiveListener

Складні архітектури часто потребують координації між компонентами, розташованими у різних частинах сторінки. Система подій Live Components реалізує елегантний механізм pub/sub на основі атрибута #[LiveListener].

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

Коли компонент ShoppingCart змінює свій вміст, він емітує подію cart:updated. Бейдж кошика, який зазвичай розміщується в хедері сторінки, автоматично прослуховує цю подію та оновлює лічильник без жодного прямого зв'язку між двома компонентами.

Така подієва архітектура спрощує підтримку коду та стимулює повторне використання компонентів -- принципи, які детально розглядаються у посібнику з питань для співбесіди з Symfony.

Міграція на UX 3.0: ключові зміни

Версія 3.0 впроваджує суттєві структурні зміни, що покращують безпеку та сумісність із сучасними веб-стандартами. Наступна таблиця підсумовує основні відмінності:

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

Захист CSRF став неявним завдяки автоматичним політикам Same-Origin, що значно спрощує конфігурацію компонентів. Функція html_cva() замінює застарілу cva() та потребує встановлення пакета twig/html-extra версії 3.12 або вище.

Кілька пакетів, які раніше входили до складу UX 2.x, було видалено на користь нативних API сучасних браузерів або перенесено до UX Toolkit. Це рішення зменшило розмір JavaScript-бандлу та покращило сумісність з інструментами збирання, зокрема AssetMapper.

Підвищення мінімальних вимог до PHP 8.4+ та Symfony 7.4+ дозволяє UX 3.0 повноцінно використовувати можливості сучасної мови та фреймворку. Для проектів на старших версіях рекомендується поетапна міграція з обов'язковим тестуванням кожного кроку.

Просунуті патерни та інтеграція з екосистемою Symfony

Окрім базових прикладів, Live Components підтримують складні архітектурні патерни, адаптовані для enterprise-застосунків. Поєднання кількох #[LiveProp], умовної валідації та інтеграції із зовнішніми сервісами дозволяє будувати багатофункціональні інтерфейси без надмірного ускладнення кодової бази.

Поширений патерн -- створення компонентів автозаповнення, які звертаються до зовнішніх API у міру введення тексту, реалізують розумне кешування для мінімізації дублювання запитів та відображають контекстні підказки на основі історії користувача. Такі компоненти наочно демонструють гнучкість системи через поєднання реактивного стану, асинхронних дій та мемоізації.

Багатокрокові форми (wizard) становлять ще один просунутий випадок використання. Кожен крок -- це незалежний Live Component, що валідує власні бізнес-правила, водночас розділяючи глобальний стан через сесійний сервіс. Ця архітектура зберігає принцип єдиної відповідальності кожного компонента, не жертвуючи плавністю UX.

Live Components також органічно інтегруються з API Platform на Symfony 7, надаючи можливість створювати реактивні адміністративні панелі, що споживають REST або GraphQL API без важких JavaScript-фреймворків.

Для застосунків, що потребують повної аудиторності, Live Components природно інтегруються з event subscribers Symfony для автоматичного логування усіх дій користувача, часових міток модифікацій та попередніх станів властивостей. Ця інтеграція ілюструє, як UX 3.0 гармонійно вписується у зрілий екосистем Symfony без введення несумісних абстракцій.

Ін'єкція залежностей працює ідентично до контролерів Symfony: конструктор компонента може приймати будь-який сервіс, зареєстрований у контейнері. Ця архітектурна послідовність знижує когнітивне навантаження на розробників, які вже мають досвід роботи з Symfony. Знання патернів dependency injection та service container, набуті під час роботи з контролерами, безпосередньо переносяться на Live Components без додаткового навчання.

Висновок: майбутнє реактивних інтерфейсів на PHP

Symfony Live Components втілюють фундаментальну тенденцію у веб-розробці: повернення до server-first архітектур, які балансують реактивність фронтенду та простоту бекенду. У 2026 році цей підхід утвердився як зріла альтернатива JavaScript SPA для широкого спектра застосунків.

Випуск UX 3.0 ознаменував повну зрілість цієї концепції з суттєвими покращеннями безпеки, продуктивності та досвіду розробника. Нативна інтеграція з Symfony Forms, розвинутий lazy loading та подієва архітектура надають повний інструментарій для побудови складних, але підтримуваних застосунків.

Основні переваги Live Components у продакшн-середовищі:

  • Спрощена підтримка -- єдина мова (PHP) для логіки фронтенду та бекенду
  • Контрольована продуктивність -- оптимізований серверний рендеринг з частковим оновленням DOM
  • Нативне SEO -- контент генерується на сервері без додаткового налаштування
  • Посилена безпека -- валідація та бізнес-логіка захищені на стороні сервера
  • Пологий поріг входження -- наявний досвід Symfony безпосередньо використовується
  • Зрілий екосистем -- прозора інтеграція з усіма компонентами Symfony

Майбутнє веб-розробки на PHP спрямоване на гібридні рішення, що поєднують потужність server-side rendering з реактивністю, яку очікують сучасні користувачі. Symfony Live Components та UX 3.0 позиціонують PHP-екосистему як провідну силу цієї трансформації, надаючи розробникам інструменти для конкуренції з JavaScript-фреймворками за умови збереження переваг серверної архітектури.

Починай практикувати!

Перевір свої знання з нашими симуляторами співбесід та технічними тестами.

Теги

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

Поділитися

Пов'язані статті