本文へ移動
cccskills
無料GitHub で公開

live-component

Symfony UX LiveComponent for reactive server-rendered UI -- components that re-render via AJAX on user interaction, zero JavaScript required. Use when building live search, real-time filtering, dynamic forms, inline validation, dependent selects, auto-save, polling, deferred/lazy rendering, or any UI that updates itself based on user input. Code triggers: AsLiveComponent, LiveProp, LiveAction, data-model, data-loading, data-live-action-url, ComponentWithFormTrait, LiveListener, emit, defer, lazy, polling. Also trigger when the user asks "how to build a search that filters as I type", "how to validate a form in real-time", "how to make a reactive component in PHP", "how to build dependent selects", "how to defer component rendering", "how to communicate between components via emit", "how to bind a form to a LiveComponent". Do NOT trigger for static reusable UI without reactivity (use twig-component), pure client-side JS behavior (use stimulus), or page-level navigation (use turbo).

インストール方法を見る

含まれるファイル(4)

  • SKILL.md13.0 KB
  • references/api.md10.1 KB
  • references/gotchas.md7.9 KB
  • references/patterns.md15.3 KB

SKILL.md(原文)

インストールする前に、エージェントに与えられる指示の中身を確認できます。

LiveComponent

TwigComponents that re-render dynamically via AJAX. Build reactive UIs in PHP + Twig with zero JavaScript. Every user interaction triggers a server round-trip that re-renders the component and morphs the DOM.

When to Use LiveComponent

Use LiveComponent when a component's output depends on user interaction -- search results that update as you type, forms with real-time validation, filters that refine a list, anything where the UI needs to change based on user input and that change requires server-side data or logic.

If the component never re-renders after initial load, use TwigComponent instead (less overhead, no AJAX). If the interaction is purely client-side (toggle, animation), use Stimulus instead.

Installation

composer require symfony/ux-live-component

Quick Reference

#[AsLiveComponent]           Make component live (re-renderable via AJAX)
#[LiveProp]                  State that persists across re-renders
#[LiveProp(writable: true)]  State that the frontend can modify
#[LiveAction]                Server method callable from frontend
data-model="prop"            Two-way bind input to LiveProp
data-action="live#action"    Call LiveAction on event
data-loading="..."           Show/hide/style elements during AJAX
{{ attributes }}             REQUIRED on root element (wires the Stimulus controller)

Basic Example

// src/Twig/Components/Counter.php
namespace App\Twig\Components;

use Symfony\UX\LiveComponent\Attribute\AsLiveComponent;
use Symfony\UX\LiveComponent\Attribute\LiveProp;
use Symfony\UX\LiveComponent\Attribute\LiveAction;
use Symfony\UX\LiveComponent\DefaultActionTrait;

#[AsLiveComponent]
final class Counter
{
    use DefaultActionTrait;

    #[LiveProp]
    public int $count = 0;

    #[LiveAction]
    public function increment(): void
    {
        $this->count++;
    }

    #[LiveAction]
    public function decrement(): void
    {
        $this->count--;
    }
}
{# templates/components/Counter.html.twig #}
<div {{ attributes }}>
    <button data-action="live#action" data-live-action-param="decrement">-</button>
    <span>{{ count }}</span>
    <button data-action="live#action" data-live-action-param="increment">+</button>
</div>

Critical: The root element must render {{ attributes }}. This injects the Stimulus data-controller="live" attribute that makes the whole system work. Without it, nothing re-renders.

LiveProp

State that persists between AJAX re-renders. Props are serialized to the frontend and sent back on every request.

Basic Props

#[LiveProp]
public string $query = '';

#[LiveProp]
public int $page = 1;

#[LiveProp]
public ?User $user = null;  // Entities auto-hydrate by ID

Writable Props (Two-way Binding)

Only writable props can be modified from the frontend via data-model:

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

// Writable with specific fields for objects
#[LiveProp(writable: ['email', 'name'])]
public User $user;

URL Binding

Sync a prop to a URL query parameter -- enables bookmarkable/shareable state:

#[LiveProp(writable: true, url: true)]
public string $query = '';
// URL becomes: ?query=search+term

// Custom parameter name
use Symfony\UX\LiveComponent\Metadata\UrlMapping;

#[LiveProp(writable: true, url: new UrlMapping(as: 'q'))]
public string $query = '';
// URL becomes: ?q=search+term

Hydration

Doctrine entities auto-hydrate by ID. For custom types:

#[LiveProp(hydrateWith: 'hydrateStatus', dehydrateWith: 'dehydrateStatus')]
public Status $status;

public function hydrateStatus(string $value): Status
{
    return Status::from($value);
}

public function dehydrateStatus(Status $status): string
{
    return $status->value;
}

Data Binding (data-model)

Bind inputs to writable LiveProps. When the input changes, the component re-renders with the new value.

{# Re-render on change (default) #}
<input type="text" data-model="search">

{# Debounced -- wait 300ms after last keystroke #}
<input type="text" data-model="debounce(300)|search">

{# Only update on blur #}
<input type="text" data-model="on(blur)|search">

{# Update model but don't re-render yet #}
<input type="text" data-model="norender|search">

{# Checkbox, radio, select #}
<input type="checkbox" data-model="enabled">
<select data-model="category">
    <option value="1">Category 1</option>
</select>

Validation Modifiers

{# Only re-render when input meets criteria #}
<input data-model="minlength(3)|search">
<input data-model="maxlength(100)|bio">
<input data-model="min(0)|quantity">
<input data-model="max(999)|price">

LiveAction

Server methods callable from the frontend:

#[LiveAction]
public function save(): void
{
    // Called via data-action="live#action" data-live-action-param="save"
}

#[LiveAction]
public function delete(#[LiveArg] int $id): void
{
    // With typed argument via data-live-id-param="123"
}

Calling Actions from Twig

{# Button click #}
<button data-action="live#action" data-live-action-param="save">Save</button>

{# With arguments #}
<button
    data-action="live#action"
    data-live-action-param="delete"
    data-live-id-param="{{ item.id }}"
>Delete</button>

{# Form submit (prevent default) #}
<form data-action="live#action:prevent" data-live-action-param="submit">

Search Example (Complete)

#[AsLiveComponent]
final class ProductSearch
{
    use DefaultActionTrait;

    #[LiveProp(writable: true, url: true)]
    public string $query = '';

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

    public function __construct(
        private readonly ProductRepository $products,
    ) {}

    public function getProducts(): array
    {
        return $this->products->search($this->query, $this->category);
    }
}
<div {{ attributes }}>
    <input type="search" data-model="debounce(300)|query" placeholder="Search...">

    <select data-model="category">
        <option value="">All Categories</option>
        {% for cat in categories %}
            <option value="{{ cat.id }}">{{ cat.name }}</option>
        {% endfor %}
    </select>

    <div data-loading="addClass(opacity-50)">
        {% for product in this.products %}
            <div>{{ product.name }}</div>
        {% endfor %}
    </div>
</div>

Loading States

Show visual feedback during AJAX re-renders:

{# Add/remove class while loading #}
<div data-loading="addClass(opacity-50)">
<div data-loading="removeClass(hidden)">

{# Show/hide element while loading #}
<span data-loading="show">Loading...</span>
<div data-loading="hide">Content</div>

{# Disable button while loading #}
<button data-loading="attr(disabled)">Submit</button>

{# Scoped to specific action or model #}
<span data-loading="action(save)|show">Saving...</span>
<span data-loading="model(query)|show">Searching...</span>

{# Delay before showing (avoid flicker on fast responses) #}
<span data-loading="delay(300)|show">Loading...</span>

Form Integration

use Symfony\Component\Form\FormInterface;
use Symfony\UX\LiveComponent\ComponentWithFormTrait;

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

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

    protected function instantiateForm(): FormInterface
    {
        return $this->createForm(UserType::class, $this->initialFormData);
    }

    #[LiveAction]
    public function save(EntityManagerInterface $em): Response
    {
        $this->submitForm();
        $user = $this->getForm()->getData();
        $em->persist($user);
        $em->flush();

        return $this->redirectToRoute('app_success');
    }
}
<div {{ attributes }}>
    {{ form_start(form, {
        attr: {
            'data-action': 'live#action:prevent',
            'data-live-action-param': 'save'
        }
    }) }}

    {{ form_row(form.email) }}
    {{ form_row(form.password) }}

    <button type="submit" data-loading="attr(disabled)">Register</button>

    {{ form_end(form) }}
</div>

Real-time Validation

{{ form_row(form.email, {
    attr: {'data-model': 'on(blur)|validatedFields'}
}) }}

Component Communication

Emit Events (Child to Parent)

use Symfony\UX\LiveComponent\ComponentToolsTrait;

#[AsLiveComponent]
final class ChildComponent
{
    use DefaultActionTrait;
    use ComponentToolsTrait;

    #[LiveAction]
    public function save(): void
    {
        // ... save logic
        $this->emit('itemSaved', ['id' => $this->item->getId()]);
    }
}

Listen to Events (Parent)

use Symfony\UX\LiveComponent\Attribute\LiveListener;

#[AsLiveComponent]
final class ParentComponent
{
    use DefaultActionTrait;

    #[LiveListener('itemSaved')]
    public function onItemSaved(#[LiveArg] int $id): void
    {
        // Component re-renders automatically after this method
    }
}

Browser Events (LiveComponent to Stimulus)

$this->dispatchBrowserEvent('modal:close');
<!-- Stimulus picks it up -->
<div data-action="modal:close@window->modal#close">

Polling

Auto-refresh a component on a timer:

{# Default: every 2 seconds #}
<div {{ attributes }} data-poll>

{# Custom interval #}
<div {{ attributes }} data-poll="delay(5000)">

{# Call specific action on each poll #}
<div {{ attributes }} data-poll="action(refresh)">

Lazy / Deferred Loading

{# Load component after page renders (deferred AJAX call) #}
<twig:HeavyComponent defer />

{# Load when element scrolls into viewport (IntersectionObserver) #}
<twig:HeavyComponent lazy />

{# Placeholder while loading #}
<twig:HeavyComponent lazy>
    <div>Loading...</div>
</twig:HeavyComponent>

Data Preservation

{# Prevent re-render from modifying this subtree #}
<div data-live-ignore>
    {# Third-party widget, contenteditable, etc. #}
</div>

{# Preserve specific attribute during DOM morph #}
<input data-live-preserve="value">

Computed Properties

Same as TwigComponent -- getXxx() methods are accessible as this.xxx. Use computed.xxx for caching within a single render cycle (avoids calling the method multiple times in a loop).

public function getFilteredItems(): array
{
    return array_filter($this->items, fn($i) => $i->isActive());
}
{# Uncached -- called each time #}
{% for item in this.filteredItems %}

{# Cached within this render #}
{% for item in computed.filteredItems %}

Key Principles

Every interaction is a server round-trip. LiveComponent is not a client-side framework. Each re-render sends the full component state to the server, re-executes PHP, and morphs the DOM. For high-frequency interactions (drag-and-drop, real-time drawing), use Stimulus instead.

Keep components small. Large components with many LiveProps and complex templates are slow to re-render. Split into smaller, focused components that communicate via emit/listen.

Use norender and on(blur) to reduce requests. Not every keystroke needs a server call. Debounce text inputs, defer binding to blur events for fields that don't need instant feedback.

{{ attributes }} on root element is non-negotiable. Without it, the live behavior Stimulus controller is never attached and nothing works.

References

See Also

  • UX Map provides ComponentWithMapTrait for reactive maps inside LiveComponents. The map automatically updates when LiveProps change.
  • UX Icons work inside LiveComponent templates with no special setup -- icons re-render on each server round-trip like any other Twig markup.

レビュー

まだレビューはありません。使ってみた感想をお寄せください。

同じリポジトリのスキル

概要と使いどころ

stimulus

無料

Stimulus JS framework for Symfony UX -- client-side behavior via HTML data attributes, zero server round-trips. Use when creating controllers for DOM manipulation, handling click/input/submit events, managing targets and values, wiring outlets between controllers, wrapping third-party JS libraries, or building toggles, dropdowns, modals, tabs, clipboard interactions. Code triggers: data-controller, data-action, data-target, data-*-value, data-*-class, data-*-outlet, stimulusFetch lazy, connect(), disconnect(), static targets, static values. Also trigger when the user asks "how do I add a click handler", "how to toggle a class", "how to build a dropdown/modal/tabs", "how to wrap a JS library in Symfony", "add keyboard shortcuts", "lazy-load a controller", "listen to global events", "communicate between controllers". Do NOT trigger for partial page updates without JS (use turbo), server-rendered reactivity (use live-component), or reusable Twig templates (use twig-component).

日本語の概要は準備中です。原文の説明を表示しています。

smnandre/symfony-ux-skills1802026年6月15日 更新

Symfony UX frontend stack -- decision tree and orchestrator for choosing between Stimulus, Turbo, TwigComponent, LiveComponent, UX Icons, and UX Map. Use when the user is unsure which tool fits, wants to combine multiple UX packages, or asks a general frontend architecture question in Symfony. Also trigger when the user asks "which UX package should I use", "how to make this interactive", "should I use Stimulus or LiveComponent", "how to structure my Symfony frontend", "what is the difference between Turbo and LiveComponent", "should this be a Frame or a LiveComponent", "how do these UX packages work together", "what is the Symfony way to do frontend". Do NOT trigger when the user clearly names a specific tool (stimulus, turbo, twig-component, live-component, ux-icons, ux-map) -- defer to the specialized skill instead.

日本語の概要は準備中です。原文の説明を表示しています。

smnandre/symfony-ux-skills1802026年6月15日 更新

turbo

無料

Hotwire Turbo for Symfony UX -- SPA-like speed with zero JavaScript. Covers Drive (navigation), Frames (partial page sections), and Streams (multi-target updates). Use when building ajax navigation, lazy-loaded sections, inline editing, pagination without reload, modals from the server, flash messages via streams, or real-time updates via Mercure/SSE. Code triggers: turbo-frame, turbo-stream, data-turbo-frame, data-turbo, data-turbo-action, turbo-stream-source, TurboStreamResponse, <twig:Turbo:Frame>, <twig:Turbo:Stream:Append>, <twig:Turbo:Stream:Replace>, turbo:before-fetch-request. Also trigger when the user asks "how to update part of the page without reload", "how to make navigation feel like SPA", "how to lazy-load a section", "how to do inline editing", "how to push real-time updates from server", "how to use Mercure with Turbo". Do NOT trigger for client-side JS behavior (use stimulus), server-rendered reactive components (use live-component), or reusable static UI (use twig-component).

日本語の概要は準備中です。原文の説明を表示しています。

smnandre/symfony-ux-skills1802026年6月15日 更新

Symfony UX TwigComponent for reusable UI building blocks -- server-rendered components with PHP classes and Twig templates. Use when creating buttons, cards, alerts, badges, navbars, or any reusable UI element with props, blocks/slots, computed properties, or anonymous (template-only) components. Code triggers: AsTwigComponent, #[AsTwigComponent], ExposeInTemplate, PreMount, PostMount, <twig:Alert />, <twig:Button>, component(), computed properties, anonymous component, HTML syntax. Also trigger when the user asks "how to create a reusable component", "how to make a component library", "how to pass props to a component", "how to use slots/blocks in a component", "how to build a design system in Symfony", "what is the HTML syntax for components", "how to create a component without a PHP class". Do NOT trigger for components that re-render dynamically on user input (use live-component), for JS behavior (use stimulus), or for page navigation (use turbo).

日本語の概要は準備中です。原文の説明を表示しています。

smnandre/symfony-ux-skills1802026年6月15日 更新

ux-icons

無料

Symfony UX Icons for rendering SVG icons in Twig templates. Supports 200,000+ Iconify icons (Lucide, Heroicons, Tabler, Material Design, etc.), local SVG files, and custom icon sets with aliases. Use when displaying icons, configuring icon defaults, importing or locking on-demand icons, creating icon aliases, or styling SVG icons with CSS. Code triggers: <twig:ux:icon />, ux_icon(), UX_ICONS_DEFAULT_ICON_ATTRIBUTES, icon.yaml, icons/, iconify:, lucide:, heroicons:, tabler:, mdi:, bin/console ux:icons:lock, bin/console ux:icons:import. Also trigger when the user asks "how to add an icon", "how to use Lucide/Heroicons/Tabler icons", "how to render an SVG icon in Twig", "how to lock icons for production", "how to create icon aliases", "how to style an icon", "icon not found", "icon not rendering". Do NOT trigger for interactive maps (use ux-map) or general Twig components (use twig-component).

日本語の概要は準備中です。原文の説明を表示しています。

smnandre/symfony-ux-skills1802026年6月15日 更新

ux-map

無料

Symfony UX Map for interactive maps with Leaflet or Google Maps in Symfony. Covers markers, polygons, polylines, circles, info windows, and LiveComponent integration. Use when displaying maps, placing markers, drawing shapes or routes, handling map events, building store locators, using custom tile layers, or making maps reactive with LiveComponent. Code triggers: <twig:ux:map />, Map(), Point(), Marker(), Polygon(), Polyline(), Circle(), InfoWindow(), MapOptionsInterface, ComponentWithMapTrait, fitBoundsToMarkers, ux:map:marker:before-create, ux:map:connect, SYMFONY_UX_MAP_DSN. Also trigger when the user asks "how to display a map", "how to add markers", "how to draw a polygon on a map", "how to handle map click events", "how to make a reactive map", "how to use Leaflet in Symfony", "how to use Google Maps in Symfony", "map not showing", "map has zero height". Do NOT trigger for SVG icons (use ux-icons) or general frontend interactivity (use stimulus).

日本語の概要は準備中です。原文の説明を表示しています。

smnandre/symfony-ux-skills1802026年6月15日 更新

smnandre のスキルをすべて見る

このスキルの問題を報告する