Symfony UX 3.0: A Component-First Frontend for PHP Developers
Symfony UX 3.0 raises the bar for PHP frontends: PHP 8.4 required, dead weight dropped, and Live Components sharper than ever.
When the Symfony team shipped UX 3.0 on April 13, 2026, they did something that major version bumps rarely do cleanly: they used the opportunity to remove things. Not just deprecated APIs, but entire packages that had quietly become dead weight over the 2.x cycle. The result is a leaner, more opinionated frontend toolkit that finally treats Live Components as the primary story rather than a bonus feature.
If you are building interactive PHP applications without reaching for a full JavaScript framework, Symfony UX 3.0 is worth a close look. Here is what actually changed and what it means for your day-to-day development.
What Is Symfony UX, and Why Does It Matter?
Symfony UX is the official JavaScript-and-PHP integration layer for the Symfony ecosystem. It covers everything from Stimulus-based controllers to full-stack reactive components. The Live Component and Twig Component packages are the flagship pieces — they let you write interactive UI components entirely in PHP and Twig, with state managed server-side and updates delivered over a lightweight AJAX/WebSocket bridge.
The philosophy is similar to Laravel Livewire: keep your application logic in PHP, avoid context-switching to JavaScript for every interactive piece, and trust the framework to handle the DOM diffing and network communication. UX 3.0 sharpens that philosophy by removing the parts that blurred the line.
The Minimum Requirements Jumped
UX 3.0 requires PHP 8.4 and Symfony 7.4. If you are not there yet, this is your nudge. PHP 8.4 brought property hooks, asymmetric visibility, and a significantly improved JIT — all of which the UX team intends to use going forward. Symfony 7.4 is the current LTS-eligible release, and the UX team wants a stable foundation before the 8.x line begins.
For existing applications on PHP 8.2 or 8.3, the 2.x line continues to receive maintenance releases, so there is no forced migration cliff. But the new features are going into 3.x only.
Four Packages Were Cut Entirely
This is the most consequential change for existing Symfony UX users. Four packages were removed because they offered thin PHP wrappers around JavaScript libraries that had minimal real integration with the Symfony stack:
- ux-swup — page transition library wrapper
- ux-lazy-image — lazy loading with BlurHash placeholder generation
- ux-typed — the Typed.js wrapper for animated typing effects
- ux-toggle-password — show/hide password toggle
The rationale is honest and worth understanding: these packages required ongoing maintenance to track upstream JavaScript library changes, but they did not add meaningful Symfony-specific value beyond a Stimulus controller and a Twig helper. The functionality each provides can be reproduced in a few lines of modern application code without a dedicated package.
If your application uses any of these, check the UPGRADE-3.0.md file for recommended migration approaches. Most of them reduce to adding a Stimulus controller to your own codebase and removing the Composer dependency.
Live Components: Tighter Security by Default
The biggest behavioral change in Live Components is that the csrf argument on the #[AsLiveComponent] attribute has been removed. CSRF protection was previously opt-in via that argument. In UX 3.0, same-origin and CORS protection is the default mechanism for all live component requests.
// UX 2.x — explicit csrf opt-in
#[AsLiveComponent(csrf: true)]
class ProductSearch extends AbstractController
{
use DefaultActionTrait;
public string $query = '';
#[LiveProp(writable: true)]
public string $query = '';
}
// UX 3.0 — just the component, CSRF argument removed
#[AsLiveComponent]
class ProductSearch extends AbstractController
{
use DefaultActionTrait;
#[LiveProp(writable: true)]
public string $query = '';
}
The practical effect: live component endpoints no longer require CSRF tokens in the traditional sense, but the framework enforces same-origin request validation at the HTTP level. For most applications this is a net improvement — no token to pass in JavaScript, no token expiry edge cases, and the security posture is equivalent or better for typical deployment scenarios.
Twig Components: cva() Function Replaced
The cva() Twig function (Class Variance Authority — a pattern for conditional CSS class generation) has been replaced by html_cva(), sourced from the twig/html-extra package at version 3.12 or higher.
{# UX 2.x #}
{% set buttonClass = cva(
'inline-flex items-center rounded font-medium',
{
variants: {
intent: { primary: 'bg-blue-600 text-white', ghost: 'border border-gray-300' },
size: { sm: 'px-3 py-1.5 text-sm', lg: 'px-6 py-3 text-base' }
}
}
) %}
{# UX 3.0 #}
{% set buttonClass = html_cva(
'inline-flex items-center rounded font-medium',
{
variants: {
intent: { primary: 'bg-blue-600 text-white', ghost: 'border border-gray-300' },
size: { sm: 'px-3 py-1.5 text-sm', lg: 'px-6 py-3 text-base' }
}
}
) %}
The function signature is identical — this is a rename, not an API change. The motivation is that html_cva now lives in twig/html-extra as a first-class Twig extension, making it available to non-Symfony projects and reducing the coupling between UX and this specific utility.
Maps Component: render_map() Is Now ux_map()
If you use the UX Maps component, the Twig function has been renamed:
{# UX 2.x #}
{{ render_map(map) }}
{# UX 3.0 #}
{{ ux_map(map) }}
This follows the broader naming convention in UX 3.0 where all first-party Twig functions are prefixed with ux_ for clarity. The old function is removed entirely, so this is a required change rather than a deprecation.
The Configuration Requirement Changed
twig_component.defaults is now mandatory configuration rather than optional. If your config/packages/twig_component.yaml does not already define defaults, you will need to add them before upgrading:
# config/packages/twig_component.yaml
twig_component:
defaults:
App\Twig\Components\: 'components/'
This enforces an explicit component namespace rather than relying on convention detection, which caused subtle discovery bugs in applications with non-standard directory structures.
Upgrading in Practice
If your Symfony UX 2.x application has been keeping up with deprecation warnings, the upgrade is mechanical rather than complex. The recommended approach:
- Ensure you are on PHP 8.4 and Symfony 7.4.
- Run your test suite with
SYMFONY_DEPRECATIONS_HELPER=weakto surface any remaining 2.x deprecations. - Update
symfony/ux-*packages to^3.0incomposer.json. - Remove any dependency on the four dropped packages.
- Rename
cva()calls tohtml_cva()and addtwig/html-extra:^3.12if not already present. - Rename
render_map()calls toux_map(). - Add
csrf: falseremoval cleanup (the attribute argument no longer exists).
The UX team’s UPGRADE-3.0.md covers each breaking change with code examples, and it is the authoritative reference for anything not listed here.
The Direction: Fewer Abstractions, Stronger Fundamentals
Symfony UX 3.0 is not a flashy release. There is no new killer feature, no headline-grabbing addition. What it delivers is a codebase that has cleaned house — removed packages that were accumulating maintenance debt, tightened the security model, aligned the naming conventions, and staked a clear position on the PHP and Symfony version floor.
For PHP developers who want reactive frontends without a full JavaScript rewrite, that stability and clarity matters more than novelty. Live Components and Twig Components are mature, well-tested tools. UX 3.0 makes them easier to reason about and harder to misconfigure.
If you have been on the fence about investing in the UX component stack, the 3.0 release is a good inflection point. The API surface is stable, the upgrade path from 2.x is well-documented, and the Symfony team has signaled long-term commitment by bumping requirements rather than carrying legacy weight indefinitely.