Symfony UX 3.3.0 is out. This release brings first-class support for Symfony Reprise in StimulusBundle, two new Shadcn components for the Toolkit (Sonner and Combobox), and a batch of quality-of-life improvements to the ux:install command and to how component documentation is parsed and linted.

StimulusBundle: Symfony Reprise support

Contributed by Hugo Alliaume in #3705

Symfony Reprise is the new Vite and Rsbuild integration for Symfony assets. It is brand new and still very much experimental (its first commit landed in September 2025 and the repository only just went public), so expect things to move fast and APIs to change. That said, StimulusBundle already supports it as a first-class asset system, alongside AssetMapper and Webpack Encore. Point the Reprise plugin at your controllers.json and start the app from @symfony/reprise/stimulus:

// vite.config.js (or rsbuild.config.js)
Symfony({
    stimulus: './assets/controllers.json',
})

As part of this change, @symfony/stimulus-bridge is no longer a default npm peer dependency: it is specific to Webpack Encore, and Reprise projects would pull it in for nothing. Only @hotwired/stimulus remains; Encore users now install the bridge manually.

StimulusBundle: preserved stimulusFetch: 'lazy' comments

Contributed by Indra Gunawan in #3702

The stimulusFetch: 'lazy' directive that marks a controller as lazy-loaded is now detected even when it uses a preserved comment marker (/*! stimulusFetch: 'lazy' */). Tools like tsc and esbuild emit that form to keep important comments from being stripped during compilation, so lazy controllers written in TypeScript now behave as expected.

Toolkit: two new Shadcn components

Contributed by Hamza Makraz in #3477 and #3482

The Shadcn kit gains two components:

  • Sonner is an opinionated toast notification component for showing transient feedback.
  • Combobox combines an input with a searchable, filterable list of options for autocomplete-style selection. Install either one with ux:install.
$ php bin/console ux:install sonner --kit=shadcn
$ php bin/console ux:install combobox --kit=shadcn

Toolkit: friendlier ux:install

Contributed by Hugo Alliaume in #3700

Recipe names are stored in lower-case, so ux:install Alert used to fail with a raw "does not exist in any official kits" error. Recipe lookup is now case-insensitive: Alert resolves to the alert recipe, both without --kit (the "which kit?" prompt) and with an explicit --kit=shadcn. When a recipe genuinely does not exist in any official kit, the error now suggests the closest alternatives found across all kits instead of leaving you guessing.

Toolkit: better @prop and @block documentation

Contributed by Hugo Alliaume in #3694

Component documentation comments now have a single, well-defined format. A new ComponentDocParser reads the {# @prop #} and {# @block #} comments at the top of a Twig component, and a companion ComponentDocChecker lints them for consistency (capitalized descriptions ending with a period, valid types, and no more hand-written "Defaults to ..." strings):

{# @prop defaultValue string Define the open Tabs at initial rendering. #}
{# @prop orientation 'horizontal'|'vertical' Define the visual orientation. #}
{# @block content The default block. #}
{%- props defaultValue = '', orientation = 'horizontal' -%}

Default values are now read directly from the {% props %} tag, so they are no longer duplicated in the description. The same parser can power both the UX website and the Toolkit linter, and it gives LLMs a clear structure to fix documentation issues against.

Shadcn tooltip fixes

Contributed by Hugo Alliaume in #3696

The Shadcn tooltip now dismisses itself on scroll and resize, and no longer intercepts pointer events while hidden, so it stops swallowing clicks on the elements underneath it.

Notable bug fixes

LiveComponent no longer returns a 500 error on malformed hydration payloads, handles nullable collection properties correctly, and stops the data-loading scanner from ignoring data-live-ignore subtrees. Autocomplete fixes a LIKE ESCAPE clause that broke search on PostgreSQL.

Full Changelog

  • #3705 [StimulusBundle] Add support for Symfony Reprise (@Kocal)
  • #3702 [StimulusBundle] Support preserved /*! stimulusFetch: 'lazy' */ syntax (@IndraGunawan)
  • #3703 [StimulusBundle] Sort custom controllers to keep the generated loader deterministic (@Amoifr)
  • #3676 [LiveComponent] Avoid 500 errors on malformed hydration payloads (@Amoifr)
  • #3688 [LiveComponent] Fix data-loading scanner ignoring data-live-ignore subtrees (@Amoifr)
  • #3690 [LiveComponent] Fix support for nullable collection properties on component (@rogierknoester)
  • #3700 [Toolkit] Resolve ux:install recipe name case-insensitively and suggest alternatives (@Kocal)
  • #3696 [Toolkit][Shadcn] Dismiss the tooltip on scroll and resize, and stop it from intercepting pointer events while hidden (@Kocal)
  • #3694 [Toolkit] Refactor (again) but improve @prop and @block comments format (@Kocal)
  • #3685 [Autocomplete] Fix LIKE ESCAPE clause breaking search on PostgreSQL (@Amoifr)
  • #3477 [Shadcn] Add Sonner (@makraz)
  • #3693 [Toolkit] Make component rendering snapshots independent from the libxml version (@Kocal)
  • #3482 [Toolkit][Shadcn] Add Combobox (@makraz)
Sponsor the Symfony project.