Symfony UX 3.2.0 and 2.36.1 are now available. Both releases fix two security issues, one in UX Icons and one in UX Toolkit, so every application using these packages should upgrade as soon as possible. On top of the security fixes, version 3.2.0 ships several new features for TwigComponent, Toolkit and Native.

Security

Two vulnerabilities were fixed in both maintained branches (2.36.1 and 3.2.0).

UX Icons rendered SVG content without sanitization, both for local icon files and for the on-demand Iconify responses (enabled by default). Because the ux_icon() Twig function outputs its SVG as safe HTML, a malicious icon set or a compromised Iconify endpoint could inject scripts and event handlers, leading to cross-site scripting. The renderer now sanitizes every icon, from any source, before it reaches the page (CVE-2026-55877).

UX Toolkit installed recipe files from a kit by copying the paths listed in its copy-files map, guarded only by a relative-path check. A crafted kit could use .. segments to escape the target directory and read or overwrite arbitrary files, up to code execution on a developer machine or CI runner. The installer now rejects any path that escapes the recipe directory (CVE-2026-55878).

Both issues were reported by Pascal Cescon and fixed by Hugo Alliaume.

Standalone components with any PSR-11 container

Guillaume Sainthillier
Contributed by Guillaume Sainthillier in #3602

TwigComponent's runtime no longer requires symfony/dependency-injection. ComponentFactory and ComponentRuntime only ever call get() and has() on their service locator, both defined by the PSR-11 Psr\Container\ContainerInterface, so they now typehint that interface instead of Symfony's concrete ServiceLocator.

This decouples the runtime from the DI component, so you can use TwigComponent standalone, with only Twig and any PSR-11 container:

1
2
3
4
5
6
7
8
9
10
11
12
13
use Psr\Container\ContainerInterface;
use Symfony\UX\TwigComponent\ComponentFactory;

// wire the runtime with any Psr\Container\ContainerInterface
$factory = new ComponentFactory(
    $templateFinder,
    $container,
    $propertyAccessor,
    $eventDispatcher,
    $config,
    $classMap,
    $twig,
);

Inside a full Symfony application, nothing changes: ServiceLocator still implements the interface and the bundle keeps wiring it for you.

A richer ComponentRendererInterface

Mathias H.
Contributed by Mathias H. in #3600

ComponentRendererInterface only declared createAndRender(), even though ComponentRuntime also relied on preCreateForRender(), startEmbeddedComponentRender() and finishEmbeddedComponentRender() from the concrete, final renderer. Intercepting the render pipeline therefore meant forking the runtime or reaching into private state through reflection.

These three methods are now part of the interface, and ComponentRuntime depends on the interface instead of the concrete class. You can decorate or replace the renderer with a clean, upgrade-safe contract:

1
2
3
4
5
6
7
8
9
10
11
12
use Symfony\UX\TwigComponent\ComponentRendererInterface;

final class ComponentPreloader implements ComponentRendererInterface
{
    public function __construct(
        private ComponentRendererInterface $inner,
    ) {
    }

    // preCreateForRender(), startEmbeddedComponentRender() and
    // finishEmbeddedComponentRender() are now part of the contract
}

Kit-global dependencies in UX Toolkit

Hugo Alliaume
Contributed by Hugo Alliaume in #3671

A Toolkit kit can install some libraries once for the whole kit (for example Flowbite), but the dependency checker only knew about per-recipe dependencies, so it flagged those kit-global imports as undeclared on every recipe.

A kit manifest can now declare its own dependencies, shared across all of its recipes:

1
2
3
4
5
6
7
{
    "name": "Flowbite v4",
    "dependencies": {
        "npm": ["flowbite"],
        "importmap": ["flowbite"]
    }
}

npm and importmap packages declared at the kit level are now treated as available for every recipe in the kit.

A linter for Toolkit kits

Hugo Alliaume
Contributed by Hugo Alliaume in #3655

Maintaining a kit means keeping recipes, templates, assets and dependencies in sync, and it is easy to ship a broken copy-files entry or a Stimulus controller without its JavaScript file. A new linter catches these regressions before they reach users:

1
$ ./vendor/bin/ux-toolkit-kit-lint path/to/your-kit

It runs structural checks (missing recipe manifests, broken copy-files entries, unknown recipe references) and coherence checks (Stimulus controllers used in Twig without a matching JavaScript file, undeclared JavaScript imports). The tool is meant for kit maintainers, so it is exposed as a standalone binary with no DI wiring, ready to run locally or from CI.

Renamed UX Native build command

Hugo Alliaume
Contributed by Hugo Alliaume in #3611

The UX Native command that produces the JSON configuration for the iOS and Android Hotwire Native shells was renamed to follow the ux:<...> convention and to drop the debug-flavored dump verb:

1
2
3
4
5
# before
$ php bin/console ux-native:dump

# after
$ php bin/console ux:native:build-configs

UX Native is still experimental, so this rename ships without a backward compatibility alias; update your scripts and CI accordingly.

Full Changelog

  • CVE-2026-55877 [Icons] Sanitize Iconify SVG output and unify icon creation (@Kocal)
  • CVE-2026-55878 [Toolkit] Harden recipe installer against path traversal (@Kocal)
  • #3682 [Toolkit][Shadcn] Fix position and phantom text-node in tooltip recipe (@Kocal)
  • #3602 [TwigComponent] Allow standalone usage with any PSR-11 container (@guillaume-sainthillier)
  • #3673 [Encore] Pin vue to <3.5.36 to workaround broken upstream publish (@Kocal)
  • #3672 [Toolkit][Flowbite4] Fix linting issues (@Kocal)
  • #3671 [Toolkit] Allow declaring kit-global dependencies in kit manifest (@Kocal)
  • #3659 [Toolkit] Consider recipe deps in StimulusControllerChecker (@Kocal)
  • #3655 [Toolkit] Add lint kit command and CI lint workflow (@Kocal)
  • #3652 [Toolkit][Shadcn] Fix manifest dependencies (@seb-jean)
  • #3600 [TwigComponent] Expose pre/embedded render methods on ComponentRendererInterface (@treztreiz)
  • #3614 [Turbo] Conflict with Mercure >=0.7.0 <0.7.2 (@Kocal)
  • #3611 [Native] Rename ux-native:dump command to ux:native:build-configs (@Kocal)
Published in #Releases #Symfony UX