symfony-ux

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.

134 stars

Best use case

symfony-ux is best used when you need a repeatable AI agent workflow instead of a one-off prompt.

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.

Teams using symfony-ux should expect a more consistent output, faster repeated execution, less prompt rewriting.

When to use this skill

  • You want a reusable workflow that can be run more than once with consistent structure.

When not to use this skill

  • You only need a quick one-off answer and do not need a reusable workflow.
  • You cannot install or maintain the underlying files, dependencies, or repository context.

Installation

Claude Code / Cursor / Codex

$curl -o ~/.claude/skills/symfony-ux/SKILL.md --create-dirs "https://raw.githubusercontent.com/smnandre/symfony-ux-skills/main/skills/symfony-ux/SKILL.md"

Manual Installation

  1. Download SKILL.md from GitHub
  2. Place it in .claude/skills/symfony-ux/SKILL.md inside your project
  3. Restart your AI agent — it will auto-discover the skill

How symfony-ux Compares

Feature / Agentsymfony-uxStandard Approach
Platform SupportNot specifiedLimited / Varies
Context Awareness High Baseline
Installation ComplexityUnknownN/A

Frequently Asked Questions

What does this skill do?

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.

Where can I find the source code?

You can find the source code on GitHub using the link provided at the top of the page.

SKILL.md Source

# Symfony UX

Modern frontend stack for Symfony. Build reactive UIs with minimal JavaScript using server-rendered HTML.

Symfony UX follows a progressive enhancement philosophy: start with plain HTML, add interactivity only where needed, and prefer server-side rendering over client-side JavaScript. Each tool solves a specific problem -- pick the simplest one that fits.

## Decision Tree: Which Tool?

```
Need frontend interactivity?
|
+-- Pure JavaScript behavior (no server)?
|   -> Stimulus
|      (DOM manipulation, event handling, third-party libs)
|
+-- Navigation / partial page updates?
|   -> Turbo
|      +-- Full page AJAX        -> Turbo Drive (automatic, zero config)
|      +-- Single section update  -> Turbo Frame
|      +-- Multiple sections      -> Turbo Stream
|
+-- Reusable UI component?
|   |
|   +-- Static (no live updates)?
|   |   -> TwigComponent
|   |      (props, blocks, computed properties)
|   |
|   +-- Dynamic (re-renders on interaction)?
|       -> LiveComponent
|          (data binding, actions, forms, real-time validation)
|
+-- Need icons?
|   -> UX Icons
|      (inline SVG from 200+ Iconify sets or local files)
|
+-- Need an interactive map?
|   -> UX Map
|      (Leaflet or Google Maps, markers, polygons, circles)
|
+-- Real-time (WebSocket/SSE)?
    -> Turbo Stream + Mercure
```

The tools compose naturally. A typical page uses Turbo Drive for navigation, Turbo Frames for partial sections, TwigComponents for reusable UI elements, LiveComponents for reactive forms/search, and Stimulus for client-side behavior that doesn't need a server round-trip.

## Quick Comparison

| Feature | Stimulus | Turbo | TwigComponent | LiveComponent |
|---------|----------|-------|---------------|---------------|
| JavaScript required | Yes (minimal) | No | No | No |
| Server re-render | No | Yes (page/frame) | No | Yes (AJAX) |
| State management | JS only | URL/Server | Props (immutable) | LiveProp (mutable) |
| Two-way binding | Manual | No | No | data-model |
| Real-time capable | Manual | Yes (Streams+Mercure) | No | Yes (polling/emit) |
| Lazy loading | Yes (stimulusFetch) | Yes (lazy frames) | No | Yes (defer/lazy) |

**UX Icons** and **UX Map** are utility packages that complement the tools above. Icons provides inline SVG rendering (local files + 200,000+ Iconify icons). Map provides interactive maps (Leaflet or Google Maps) with PHP-first configuration. Both work inside TwigComponents, LiveComponents, and Turbo Frames. Map also has a dedicated `ComponentWithMapTrait` for reactive maps in LiveComponents.

## Installation

```bash
# All core packages
composer require symfony/ux-turbo symfony/stimulus-bundle \
    symfony/ux-twig-component symfony/ux-live-component

# Individual
composer require symfony/stimulus-bundle      # Stimulus
composer require symfony/ux-turbo             # Turbo
composer require symfony/ux-twig-component    # TwigComponent
composer require symfony/ux-live-component    # LiveComponent (includes TwigComponent)
composer require symfony/ux-icons             # UX Icons
composer require symfony/ux-map               # UX Map (then add a renderer below)
composer require symfony/ux-leaflet-map       # Leaflet renderer (free)
composer require symfony/ux-google-map        # Google Maps renderer (requires API key)
```

## Common Patterns

### Pattern 1: Static Component (TwigComponent)

Reusable UI with no interactivity. Use for buttons, cards, alerts, badges.

```php
#[AsTwigComponent]
final class Alert
{
    public string $type = 'info';
    public string $message;
}
```

```twig
{# templates/components/Alert.html.twig #}
<div class="alert alert-{{ type }}" {{ attributes }}>
    {{ message }}
</div>
```

```twig
<twig:Alert type="success" message="Saved!" />
```

### Pattern 2: Component + JS Behavior (TwigComponent + Stimulus)

Server-rendered component with client-side interactivity. Use when the interaction is purely cosmetic (toggling, animations, third-party JS libs) and doesn't need server data.

```php
#[AsTwigComponent]
final class Dropdown
{
    public string $label;
}
```

```twig
{# templates/components/Dropdown.html.twig #}
<div data-controller="dropdown" {{ attributes }}>
    <button data-action="click->dropdown#toggle">{{ label }}</button>
    <div data-dropdown-target="menu" hidden>
        {% block content %}{% endblock %}
    </div>
</div>
```

### Pattern 3: Server-Reactive Component (LiveComponent)

Component that re-renders via AJAX on user input. Use for search boxes, filters, forms with real-time validation, anything that needs server data on every interaction.

```php
#[AsLiveComponent]
final class SearchBox
{
    use DefaultActionTrait;

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

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

    public function getResults(): array
    {
        return $this->products->search($this->query);
    }
}
```

```twig
<div {{ attributes }}>
    <input data-model="debounce(300)|query" placeholder="Search...">
    <div data-loading="addClass(opacity-50)">
        {% for item in this.results %}
            <div>{{ item.name }}</div>
        {% endfor %}
    </div>
</div>
```

### Pattern 4: Frame-Based Navigation (Turbo Frame)

Partial page updates without full reload. Use for pagination, inline editing, tabbed content, modals loaded from server.

```twig
<turbo-frame id="product-list">
    {% for product in products %}
        <a href="{{ path('product_show', {id: product.id}) }}">
            {{ product.name }}
        </a>
    {% endfor %}
</turbo-frame>
```

### Pattern 5: Multi-Section Update (Turbo Stream)

Update multiple page areas from a single server response. Use after form submissions that affect several parts of the page.

```php
#[Route('/comments', methods: ['POST'])]
public function create(Request $request): Response
{
    // ... save comment

    $request->setRequestFormat(TurboBundle::STREAM_FORMAT);
    return $this->render('comment/create.stream.html.twig', [
        'comment' => $comment,
        'count' => $count,
    ]);
}
```

```twig
{# create.stream.html.twig #}
<turbo-stream action="append" target="comments">
    <template>{{ include('comment/_comment.html.twig') }}</template>
</turbo-stream>
<turbo-stream action="update" target="comment-count">
    <template>{{ count }}</template>
</turbo-stream>
```

You can also use the Twig component syntax:

```twig
<twig:Turbo:Stream:Append target="comments">
    {{ include('comment/_comment.html.twig') }}
</twig:Turbo:Stream:Append>
```

### Pattern 6: LiveComponent Inside Turbo Frame

Combine for complex UIs -- the frame scopes navigation, the LiveComponent handles reactivity within that scope.

```twig
<turbo-frame id="search-section">
    <twig:ProductSearch />
</turbo-frame>
```

### Pattern 7: Real-Time Updates (Mercure + Turbo Stream)

Broadcast server-side events to all connected browsers via SSE.

```php
use Symfony\UX\Turbo\Attribute\Broadcast;

#[Broadcast]
class Message
{
    // Entity changes broadcast automatically
}
```

```twig
<turbo-stream-source src="{{ mercure('chat')|escape('html_attr') }}">
</turbo-stream-source>
<div id="messages">...</div>
```

## When to Use What

**Stimulus** -- Adding JS behavior to existing HTML, integrating third-party libraries (charts, datepickers, maps), client-only interactions (toggles, tabs, clipboard), anything where you need full control over JavaScript execution.

**Turbo Drive** -- SPA-like navigation. Automatic, zero config. Just install and all links/forms become AJAX. Opt out selectively with `data-turbo="false"`.

**Turbo Frames** -- Loading or updating a single page section: inline editing, pagination within a section, modal content loading, lazy-loaded sidebar.

**Turbo Streams** -- Updating multiple page sections at once, real-time broadcasts (with Mercure), flash messages after form submit, delete confirmations that update a list and a counter.

**TwigComponent** -- Reusable UI elements (buttons, cards, alerts, form widgets), consistent styling and markup, no server interaction needed after initial render, component composition and nesting.

**LiveComponent** -- Forms with real-time validation, search with live results, data binding (like Vue/React but server-rendered), any component whose state changes based on user interaction, when you want to avoid writing JavaScript entirely.

**UX Icons** -- Rendering SVG icons in templates. Supports 200+ Iconify icon sets (Lucide, Tabler, Heroicons, MDI...) and local SVG files. Icons are inlined as `<svg>` -- no icon fonts, no runtime HTTP requests. Use `<twig:ux:icon name="lucide:check" />`.

**UX Map** -- Displaying interactive maps with markers, polygons, polylines, circles, and info windows. Build the map in PHP (`new Map()`), render in Twig (`ux_map(map)`). Supports Leaflet (free) and Google Maps. Works inside LiveComponents via `ComponentWithMapTrait` for reactive maps.

## Combining Tools

```
+-----------------------------------------------------+
|                     Page                            |
|  +------------------------------------------------+ |
|  | Turbo Drive (automatic full-page AJAX)         | |
|  |  +------------------------------------------+  | |
|  |  | Turbo Frame (partial section)            |  | |
|  |  |  +------------------------------------+  |  | |
|  |  |  | LiveComponent (reactive)           |  |  | |
|  |  |  |  +------------------------------+  |  |  | |
|  |  |  |  | TwigComponent (static)       |  |  |  | |
|  |  |  |  |  + Stimulus (JS behavior)    |  |  |  | |
|  |  |  |  +------------------------------+  |  |  | |
|  |  |  +------------------------------------+  |  | |
|  |  +------------------------------------------+  | |
|  +------------------------------------------------+ |
+-----------------------------------------------------+
```

## File Structure

```
src/
  Twig/
    Components/
      Alert.php              # TwigComponent
      Button.php             # TwigComponent
      SearchBox.php          # LiveComponent
      ProductForm.php        # LiveComponent

templates/
  components/
    Alert.html.twig
    Button.html.twig
    SearchBox.html.twig
    ProductForm.html.twig

assets/
  controllers/
    dropdown_controller.js   # Stimulus
    modal_controller.js      # Stimulus
    chart_controller.js      # Stimulus
  icons/
    close.svg                # UX Icons (local)
    header/
      logo.svg               # UX Icons (namespaced: header:logo)
```

## Anti-Patterns to Avoid

**Don't use LiveComponent for static content.** If a component never re-renders after initial load, use TwigComponent instead -- LiveComponent adds unnecessary overhead (AJAX requests, state serialization).

**Don't use Turbo Streams when a Frame is enough.** If you're only updating one section of the page, a Turbo Frame is simpler and requires no special response format.

**Don't reach for Stimulus when Turbo handles it.** Before writing a Stimulus controller for a link or form interaction, check if Turbo Drive/Frames already handle it.

**Don't fight Turbo Drive.** If a link or form behaves oddly with Turbo, the fix is usually to ensure the server returns a proper full HTML page, not to disable Turbo.

## Anti-Patterns for Icons and Map

**Don't use icon fonts when UX Icons is available.** Inline SVG is more accessible, stylable, and doesn't require extra HTTP requests.

**Don't hardcode map center/zoom when you have markers.** Use `fitBoundsToMarkers()` to auto-fit the viewport.

**Don't forget explicit height on map containers.** Without it, the `<div>` collapses to 0px and the map is invisible.

**Don't deploy with on-demand icons enabled.** Run `php bin/console ux:icons:lock` before deploying to avoid runtime HTTP requests to the Iconify API.

## Related Skills

For detailed documentation on each tool, read the dedicated skill:
- **Stimulus**: Controllers, targets, values, actions, outlets, lazy loading
- **Turbo**: Drive, Frames, Streams, Mercure integration, `<twig:Turbo:Stream:*>` components
- **TwigComponent**: Props, blocks, computed properties, anonymous components, attributes
- **LiveComponent**: LiveProp, LiveAction, data-model, forms, emit/listen, polling, defer/lazy
- **UX Icons**: Iconify on-demand, local SVG, icon sets, aliases, `ux:icons:lock` CLI
- **UX Map**: Leaflet/Google Maps, markers, polygons, polylines, circles, `ComponentWithMapTrait`

Related Skills

ux-map

134
from smnandre/symfony-ux-skills

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).

ux-icons

134
from smnandre/symfony-ux-skills

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).

twig-component

134
from smnandre/symfony-ux-skills

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).

turbo

134
from smnandre/symfony-ux-skills

Hotwire Turbo for Symfony UX -- SPA-like speed with zero JavaScript. Covers Drive (full-page navigation), Frames (partial page sections), and Streams (multi-target updates). Use when building ajax navigation, lazy-loaded page sections, inline editing, pagination without reload, modals loaded from the server, flash messages via streams, real-time updates via Mercure/SSE, or multi-section page updates. 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).

stimulus

134
from smnandre/symfony-ux-skills

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).

live-component

134
from smnandre/symfony-ux-skills

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, #[AsLiveComponent], LiveProp, #[LiveProp], LiveAction, #[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), for pure client-side JS behavior (use stimulus), or for page-level navigation (use turbo).

symfony-knowledge

59
from dykyi-roman/awesome-claude-code

Symfony framework knowledge base. Provides architecture, DDD integration, persistence, DI, security, messenger, workflow, events, infrastructure components, testing, and antipatterns for Symfony PHP projects.

symfony

26
from TerminalSkills/skills

You are an expert in Symfony, the enterprise PHP framework for building web applications and APIs. You help developers build production systems with Symfony's component architecture, Doctrine ORM, dependency injection, event system, security component, API Platform for REST/GraphQL, and Messenger for async processing — the backbone of enterprise PHP used by companies processing billions of requests.

Symfony — Enterprise PHP Framework

25
from ComeOnOliver/skillshub

You are an expert in Symfony, the enterprise PHP framework for building web applications and APIs. You help developers build production systems with Symfony's component architecture, Doctrine ORM, dependency injection, event system, security component, API Platform for REST/GraphQL, and Messenger for async processing — the backbone of enterprise PHP used by companies processing billions of requests.

symfony-semaphore

16
from diegosouzapw/awesome-omni-skill

Manage semaphores to allow multiple concurrent processes to access shared resources with configurable limits. Use semaphores for rate limiting, resource pooling, and coordinating concurrent access across multiple processes on local or remote systems.

php-symfony

16
from diegosouzapw/awesome-omni-skill

Symfony development standards aligned with official Symfony Best Practices Triggers on: **/*.php, **/*.yaml, **/*.yml, **/*.xml, **/*.twig

symfony:api-platform-versioning

16
from diegosouzapw/awesome-omni-skill

Use when symfony api platform versioning