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
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
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
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
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
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
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:installrecipe name case-insensitively and suggest alternatives (@Kocal) - #3696 [Toolkit][Shadcn] Dismiss the
tooltipon scroll and resize, and stop it from intercepting pointer events while hidden (@Kocal) - #3694 [Toolkit] Refactor (again) but improve
@propand@blockcomments 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)