<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0"
    xmlns:content="http://purl.org/rss/1.0/modules/content/"
    xmlns:dc="http://purl.org/dc/elements/1.1/"
    xmlns:atom="http://www.w3.org/2005/Atom"
>
    <channel>
        <title>Symfony Blog</title>
        <atom:link href="https://feeds.feedburner.com/symfony/blog" rel="self" type="application/rss+xml" />
        <link>https://symfony.com/blog/</link>
        <description>Most recent posts published on the Symfony project blog</description>
        <pubDate>Sat, 08 Aug 2026 10:07:31 +0200</pubDate>
        <lastBuildDate>Fri, 07 Aug 2026 20:08:16 +0200</lastBuildDate>
        <language>en</language>
                        <item>
            <title><![CDATA[Symfony 8.1.4 released]]></title>
            <link>https://symfony.com/blog/symfony-8-1-4-released?utm_source=Symfony%20Blog%20Feed&amp;utm_medium=feed</link>
            <description>Symfony 8.1.4 has just been released.
Read the Symfony upgrade guide to learn more about upgrading Symfony
and use the SymfonyInsight upgrade reports to detect the code you will
need to change in your project.

    
                    
                Tip…</description>
            <content:encoded><![CDATA[
                                <p><a href="#Symfony%208.1.4" class="reference internal">Symfony 8.1.4</a> has just been released.</p>
<p>Read the <a href="https://symfony.com/doc/current/setup/upgrade_major.html" class="reference external">Symfony upgrade guide</a> to learn more about upgrading Symfony
and use the <a href="https://insight.symfony.com/" class="reference external">SymfonyInsight upgrade reports</a> to detect the code you will
need to change in your project.</p>
<div class="admonition admonition-tip ">
    <p class="admonition-title">
                    <svg xmlns="http://www.w3.org/2000/svg" fill="none" width="24" height="24" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9.663 17h4.673M12 3v1m6.364 1.636l-.707.707M21 12h-1M4 12H3m3.343-5.657l-.707-.707m2.828 9.9a5 5 0 117.072 0l-.548.547A3.374 3.374 0 0014 18.469V19a2 2 0 11-4 0v-.531c0-.895-.356-1.754-.988-2.386l-.548-.547z"></path></svg>
                <span>Tip</span>
    </p><p>Want to be notified whenever a new Symfony release is published? Or when a
version is not maintained anymore? Or only when a security issue is fixed?
Consider <a href="https://symfony.com/account/notifications" class="reference external">subscribing to the Symfony Roadmap Notifications</a>.</p>
</div>
<div class="section">
<h2 id="changelog-since-symfony-8-1-3"><a class="headerlink" href="#changelog-since-symfony-8-1-3" title="Permalink to this headline">Changelog Since Symfony 8.1.3</a></h2>
<ul>
    <li>bug <a href="https://github.com/symfony/symfony/pull/65193" class="reference external" rel="external noopener noreferrer" target="_blank">#65193</a> [RateLimiter] Cap the burst size and the duration computed from it (@cs278)</li>
<li>bug <a href="https://github.com/symfony/symfony/pull/65183" class="reference external" rel="external noopener noreferrer" target="_blank">#65183</a> [Intl] Honor the expiry date of currencies that have no start date (@sadiqk2)</li>
<li>bug <a href="https://github.com/symfony/symfony/pull/65184" class="reference external" rel="external noopener noreferrer" target="_blank">#65184</a> [Form] Distinguish active_at from not_active_at in the CurrencyType choice list cache (@sadiqk2)</li>
<li>bug <a href="https://github.com/symfony/symfony/pull/65191" class="reference external" rel="external noopener noreferrer" target="_blank">#65191</a> [PhpUnitBridge] Don't return a non-callable from getPhpUnitErrorHandler() (@sadiqk2)</li>
<li>bug <a href="https://github.com/symfony/symfony/pull/65187" class="reference external" rel="external noopener noreferrer" target="_blank">#65187</a> [HttpFoundation] Treat a UriSigner expiration of 0 as a date, not as no expiry (@sadiqk2)</li>
<li>bug <a href="https://github.com/symfony/symfony/pull/65180" class="reference external" rel="external noopener noreferrer" target="_blank">#65180</a> [HttpKernel] Fix failing reset methods preventing later services from resetting (@javiereguiluz)</li>
<li>bug <a href="https://github.com/symfony/symfony/pull/64982" class="reference external" rel="external noopener noreferrer" target="_blank">#64982</a> [PropertyInfo] Do not report a non-public mutator as writable (@nicolas-grekas)</li>
<li>bug <a href="https://github.com/symfony/symfony/pull/65174" class="reference external" rel="external noopener noreferrer" target="_blank">#65174</a> [Tui] Keep ANSI codes ordered when slicing across a color change (@smnandre)</li>
<li>bug <a href="https://github.com/symfony/symfony/pull/65177" class="reference external" rel="external noopener noreferrer" target="_blank">#65177</a> [HttpClient] Fix GuzzleHttpHandler crashing with guzzlehttp/guzzle ^8.0 (@peter17)</li>
<li>bug <a href="https://github.com/symfony/symfony/pull/65128" class="reference external" rel="external noopener noreferrer" target="_blank">#65128</a> [Mime] Escape backslashes in Address::getEncodedName() (@munzzyy)</li>
<li>bug <a href="https://github.com/symfony/symfony/pull/65154" class="reference external" rel="external noopener noreferrer" target="_blank">#65154</a> [ObjectMapper] Fix target property mappings dropped when the source carries metadata (@ousamabenyounes)</li>
<li>bug <a href="https://github.com/symfony/symfony/pull/65167" class="reference external" rel="external noopener noreferrer" target="_blank">#65167</a> [Serializer] Align union member order between the legacy and TypeInfo paths (@ousamabenyounes)</li>
<li>bug <a href="https://github.com/symfony/symfony/pull/65172" class="reference external" rel="external noopener noreferrer" target="_blank">#65172</a> [DependencyInjection] Don't keep partially-configured shared services when their setup fails (@javiereguiluz)</li>
<li>bug <a href="https://github.com/symfony/symfony/pull/65169" class="reference external" rel="external noopener noreferrer" target="_blank">#65169</a> [DoctrineBridge] fix compatibility with DBAL &lt; 4.5 (@xabbuh)</li>
<li>minor <a href="https://github.com/symfony/symfony/pull/65165" class="reference external" rel="external noopener noreferrer" target="_blank">#65165</a> [Form][Validator] Added missing Slovak translations (@dtokos)</li>
<li>bug <a href="https://github.com/symfony/symfony/pull/65150" class="reference external" rel="external noopener noreferrer" target="_blank">#65150</a> [Tui] Render raw HTML in MarkdownWidget instead of dropping it (@lazerg)</li>
<li>bug <a href="https://github.com/symfony/symfony/pull/65141" class="reference external" rel="external noopener noreferrer" target="_blank">#65141</a> [ObjectMapper] Skip nested mapping when no target fits the destination property type (@lazerg)</li>
<li>bug <a href="https://github.com/symfony/symfony/pull/65131" class="reference external" rel="external noopener noreferrer" target="_blank">#65131</a> [FrameworkBundle] Fix "<a href="cache:clear" class="reference internal">cache:clear</a>" failing when the cache dir is rebuilt concurrently (@ousamabenyounes)</li>
<li>bug <a href="https://github.com/symfony/symfony/pull/65142" class="reference external" rel="external noopener noreferrer" target="_blank">#65142</a> [ObjectMapper] Fix the class-level mapping of another target being applied (@lazerg)</li>
<li>bug <a href="https://github.com/symfony/symfony/pull/65149" class="reference external" rel="external noopener noreferrer" target="_blank">#65149</a> [DependencyInjection] Resolve factories on "service_container" against the concrete container class (@lazerg)</li>
<li>bug <a href="https://github.com/symfony/symfony/pull/65116" class="reference external" rel="external noopener noreferrer" target="_blank">#65116</a> [Lock] Fix a released read lock letting a new read lock through while a write lock is held (@Zombaya)</li>
<li>bug <a href="https://github.com/symfony/symfony/pull/65133" class="reference external" rel="external noopener noreferrer" target="_blank">#65133</a> [DependencyInjection] Fix ParameterBag::clear() leaving stale deprecations and resolution state (@javiereguiluz)</li>
<li>bug <a href="https://github.com/symfony/symfony/pull/65122" class="reference external" rel="external noopener noreferrer" target="_blank">#65122</a> [Messenger] Re-send original payload when retrying undecodable messages (@SystematicCZ)</li>
<li>bug <a href="https://github.com/symfony/symfony/pull/65125" class="reference external" rel="external noopener noreferrer" target="_blank">#65125</a> [Serializer] Fix FILTER_BOOL breaking union type denormalization (@eliot488995568)</li>
<li>bug <a href="https://github.com/symfony/symfony/pull/65120" class="reference external" rel="external noopener noreferrer" target="_blank">#65120</a> [DependencyInjection] Fix #[Autoconfigure] being processed twice for PSR-4-discovered abstract types (@javiereguiluz)</li>
<li>bug <a href="https://github.com/symfony/symfony/pull/65113" class="reference external" rel="external noopener noreferrer" target="_blank">#65113</a> [DependencyInjection] Fix instantiating a lazy proxy for an inline definition (@nicolas-grekas)</li>
<li>bug <a href="https://github.com/symfony/symfony/pull/65112" class="reference external" rel="external noopener noreferrer" target="_blank">#65112</a> [DependencyInjection] Fix misleading error messages for invalid "proxy" tags (@nicolas-grekas)</li>
<li>bug <a href="https://github.com/symfony/symfony/pull/65111" class="reference external" rel="external noopener noreferrer" target="_blank">#65111</a> [DependencyInjection] Fix generating one proxy class per set of "proxy" tags (@nicolas-grekas)</li>
<li>bug <a href="https://github.com/symfony/symfony/pull/65109" class="reference external" rel="external noopener noreferrer" target="_blank">#65109</a> [DependencyInjection] Honor the interface passed to #[Lazy] (@nicolas-grekas)</li>
<li>bug <a href="https://github.com/symfony/symfony/pull/65105" class="reference external" rel="external noopener noreferrer" target="_blank">#65105</a> [Uid] Fix the weak-secret guard in Uuid47Transformer for secrets longer than 16 bytes (@semx)</li>
<li>bug <a href="https://github.com/symfony/symfony/pull/65091" class="reference external" rel="external noopener noreferrer" target="_blank">#65091</a> [DoctrineBridge] Close all expired connections in the idle listener (@RobLogtenberg-Pay)</li>
<li>bug <a href="https://github.com/symfony/symfony/pull/65085" class="reference external" rel="external noopener noreferrer" target="_blank">#65085</a> [Messenger] Stop the PostgreSQL transport from blocking the worker loop (@bzajacc)</li>
<li>bug <a href="https://github.com/symfony/symfony/pull/65095" class="reference external" rel="external noopener noreferrer" target="_blank">#65095</a> [WebProfilerBundle] Avoid a flash of unstyled content in the debug toolbar (@wayne530)</li>
<li>bug <a href="https://github.com/symfony/symfony/pull/65100" class="reference external" rel="external noopener noreferrer" target="_blank">#65100</a> [Translation][Lokalise] Send filter_filenames as a JSON array (@nicolas-grekas)</li>
<li>bug <a href="https://github.com/symfony/symfony/pull/65099" class="reference external" rel="external noopener noreferrer" target="_blank">#65099</a> [Mailer][Sendgrid] Map the processed event to RECEIVED (@nicolas-grekas)</li>
<li>bug <a href="https://github.com/symfony/symfony/pull/65032" class="reference external" rel="external noopener noreferrer" target="_blank">#65032</a> [JsonStreamer] Fix escaping, float precision and error reporting when writing JSON (@ousamabenyounes)</li>
<li>minor <a href="https://github.com/symfony/symfony/pull/65097" class="reference external" rel="external noopener noreferrer" target="_blank">#65097</a> [Form][Validator] Review Lithuanian (lt) translations (@MariusVarna)</li>
<li>bug <a href="https://github.com/symfony/symfony/pull/65093" class="reference external" rel="external noopener noreferrer" target="_blank">#65093</a> [Cache][Messenger] Restore compatibility with ext-redis 5 (@nicolas-grekas)</li>
<li>minor <a href="https://github.com/symfony/symfony/pull/65070" class="reference external" rel="external noopener noreferrer" target="_blank">#65070</a> [Notifier][Telegram] Document how to build a multi-row inline keyboard (@nicolas-grekas)</li>
<li>bug <a href="https://github.com/symfony/symfony/pull/65034" class="reference external" rel="external noopener noreferrer" target="_blank">#65034</a> [Mailer][Resend] Handle the email.failed and email.suppressed webhook events (@alexisLefebvre)</li>
<li>bug <a href="https://github.com/symfony/symfony/pull/65065" class="reference external" rel="external noopener noreferrer" target="_blank">#65065</a> [Console] Fix column widths when a row spans columns (@wazum)</li>
<li>bug <a href="https://github.com/symfony/symfony/pull/65062" class="reference external" rel="external noopener noreferrer" target="_blank">#65062</a> [Mime] Fix corrupted CID references when one is a prefix of another (@MarcinOrlowski)</li>
<li>bug <a href="https://github.com/symfony/symfony/pull/65066" class="reference external" rel="external noopener noreferrer" target="_blank">#65066</a> [Mailer] Fix rewriting cid references of inline parts for Mailgun (@nicolas-grekas)</li>
<li>bug <a href="https://github.com/symfony/symfony/pull/64696" class="reference external" rel="external noopener noreferrer" target="_blank">#64696</a> [Config] Generate unsealed array shapes for any normalizers (@ousamabenyounes)</li>
<li>bug <a href="https://github.com/symfony/symfony/pull/65060" class="reference external" rel="external noopener noreferrer" target="_blank">#65060</a> [TwigBridge][WebProfilerBundle] Fix compatibility with Twig 4 (@nicolas-grekas)</li>
<li>bug <a href="https://github.com/symfony/symfony/pull/65058" class="reference external" rel="external noopener noreferrer" target="_blank">#65058</a> [PropertyAccess] Fix caching of null read info (@javiereguiluz)</li>
<li>bug <a href="https://github.com/symfony/symfony/pull/65059" class="reference external" rel="external noopener noreferrer" target="_blank">#65059</a> [Translation] Fix fuzzy translations and multi-line msgctxt in PO files (@javiereguiluz)</li>
<li>minor <a href="https://github.com/symfony/symfony/pull/65055" class="reference external" rel="external noopener noreferrer" target="_blank">#65055</a> [Validator] Review Serbian (sr_Latn) translations (@milanqtx)</li>
<li>minor <a href="https://github.com/symfony/symfony/pull/65054" class="reference external" rel="external noopener noreferrer" target="_blank">#65054</a> [Validator] Review Serbian (sr_Cyrl) translations (@milanqtx)</li>
<li>bug <a href="https://github.com/symfony/symfony/pull/65041" class="reference external" rel="external noopener noreferrer" target="_blank">#65041</a> [Validator] Fix File constraint skipping the mime type check when extensions and mimeTypes have no common value (@nicolas-grekas)</li>
<li>minor <a href="https://github.com/symfony/symfony/pull/65048" class="reference external" rel="external noopener noreferrer" target="_blank">#65048</a> [VarExporter] cache result of hasOctalControlChars (@alexisLefebvre)</li>
<li>bug <a href="https://github.com/symfony/symfony/pull/65049" class="reference external" rel="external noopener noreferrer" target="_blank">#65049</a> [Translation] Do not split ICU messages on pipes in PoFileDumper (@nicolas-grekas)</li>
<li>bug <a href="https://github.com/symfony/symfony/pull/65052" class="reference external" rel="external noopener noreferrer" target="_blank">#65052</a> [PropertyInfo] Revert "bug #64990  Do not trigger legacy Type deprecat… (@nicolas-grekas)</li>
</ul>
<span id="Symfony 8.1.4"></span>
</div>
                <hr style="margin-bottom: 5px" />
                <div style="font-size: 90%">
                    <a href="https://symfony.com/sponsor">Sponsor</a> the Symfony project.
                </div>
            ]]></content:encoded>
            <guid isPermaLink="false">https://symfony.com/blog/symfony-8-1-4-released?utm_source=Symfony%20Blog%20Feed&amp;utm_medium=feed</guid>
            <dc:creator><![CDATA[ Fabien Potencier ]]></dc:creator>
            <pubDate>Fri, 07 Aug 2026 20:08:16 +0200</pubDate>
            <comments>https://symfony.com/blog/symfony-8-1-4-released?utm_source=Symfony%20Blog%20Feed&amp;utm_medium=feed#comments-list</comments>
        </item>
                        <item>
            <title><![CDATA[Symfony 7.4.16 released]]></title>
            <link>https://symfony.com/blog/symfony-7-4-16-released?utm_source=Symfony%20Blog%20Feed&amp;utm_medium=feed</link>
            <description>Symfony 7.4.16 has just been released.
Read the Symfony upgrade guide to learn more about upgrading Symfony
and use the SymfonyInsight upgrade reports to detect the code you will
need to change in your project.

    
                    
                Tip…</description>
            <content:encoded><![CDATA[
                                <p><a href="#Symfony%207.4.16" class="reference internal">Symfony 7.4.16</a> has just been released.</p>
<p>Read the <a href="https://symfony.com/doc/current/setup/upgrade_major.html" class="reference external">Symfony upgrade guide</a> to learn more about upgrading Symfony
and use the <a href="https://insight.symfony.com/" class="reference external">SymfonyInsight upgrade reports</a> to detect the code you will
need to change in your project.</p>
<div class="admonition admonition-tip ">
    <p class="admonition-title">
                    <svg xmlns="http://www.w3.org/2000/svg" fill="none" width="24" height="24" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9.663 17h4.673M12 3v1m6.364 1.636l-.707.707M21 12h-1M4 12H3m3.343-5.657l-.707-.707m2.828 9.9a5 5 0 117.072 0l-.548.547A3.374 3.374 0 0014 18.469V19a2 2 0 11-4 0v-.531c0-.895-.356-1.754-.988-2.386l-.548-.547z"></path></svg>
                <span>Tip</span>
    </p><p>Want to be notified whenever a new Symfony release is published? Or when a
version is not maintained anymore? Or only when a security issue is fixed?
Consider <a href="https://symfony.com/account/notifications" class="reference external">subscribing to the Symfony Roadmap Notifications</a>.</p>
</div>
<div class="section">
<h2 id="changelog-since-symfony-7-4-15"><a class="headerlink" href="#changelog-since-symfony-7-4-15" title="Permalink to this headline">Changelog Since Symfony 7.4.15</a></h2>
<ul>
    <li>bug <a href="https://github.com/symfony/symfony/pull/65193" class="reference external" rel="external noopener noreferrer" target="_blank">#65193</a> [RateLimiter] Cap the burst size and the duration computed from it (@cs278)</li>
<li>bug <a href="https://github.com/symfony/symfony/pull/65183" class="reference external" rel="external noopener noreferrer" target="_blank">#65183</a> [Intl] Honor the expiry date of currencies that have no start date (@sadiqk2)</li>
<li>bug <a href="https://github.com/symfony/symfony/pull/65184" class="reference external" rel="external noopener noreferrer" target="_blank">#65184</a> [Form] Distinguish active_at from not_active_at in the CurrencyType choice list cache (@sadiqk2)</li>
<li>bug <a href="https://github.com/symfony/symfony/pull/65191" class="reference external" rel="external noopener noreferrer" target="_blank">#65191</a> [PhpUnitBridge] Don't return a non-callable from getPhpUnitErrorHandler() (@sadiqk2)</li>
<li>bug <a href="https://github.com/symfony/symfony/pull/65187" class="reference external" rel="external noopener noreferrer" target="_blank">#65187</a> [HttpFoundation] Treat a UriSigner expiration of 0 as a date, not as no expiry (@sadiqk2)</li>
<li>bug <a href="https://github.com/symfony/symfony/pull/65180" class="reference external" rel="external noopener noreferrer" target="_blank">#65180</a> [HttpKernel] Fix failing reset methods preventing later services from resetting (@javiereguiluz)</li>
<li>bug <a href="https://github.com/symfony/symfony/pull/64982" class="reference external" rel="external noopener noreferrer" target="_blank">#64982</a> [PropertyInfo] Do not report a non-public mutator as writable (@nicolas-grekas)</li>
<li>bug <a href="https://github.com/symfony/symfony/pull/65128" class="reference external" rel="external noopener noreferrer" target="_blank">#65128</a> [Mime] Escape backslashes in Address::getEncodedName() (@munzzyy)</li>
<li>bug <a href="https://github.com/symfony/symfony/pull/65154" class="reference external" rel="external noopener noreferrer" target="_blank">#65154</a> [ObjectMapper] Fix target property mappings dropped when the source carries metadata (@ousamabenyounes)</li>
<li>bug <a href="https://github.com/symfony/symfony/pull/65167" class="reference external" rel="external noopener noreferrer" target="_blank">#65167</a> [Serializer] Align union member order between the legacy and TypeInfo paths (@ousamabenyounes)</li>
<li>bug <a href="https://github.com/symfony/symfony/pull/65172" class="reference external" rel="external noopener noreferrer" target="_blank">#65172</a> [DependencyInjection] Don't keep partially-configured shared services when their setup fails (@javiereguiluz)</li>
<li>bug <a href="https://github.com/symfony/symfony/pull/65169" class="reference external" rel="external noopener noreferrer" target="_blank">#65169</a> [DoctrineBridge] fix compatibility with DBAL &lt; 4.5 (@xabbuh)</li>
<li>minor <a href="https://github.com/symfony/symfony/pull/65165" class="reference external" rel="external noopener noreferrer" target="_blank">#65165</a> [Form][Validator] Added missing Slovak translations (@dtokos)</li>
<li>bug <a href="https://github.com/symfony/symfony/pull/65141" class="reference external" rel="external noopener noreferrer" target="_blank">#65141</a> [ObjectMapper] Skip nested mapping when no target fits the destination property type (@lazerg)</li>
<li>bug <a href="https://github.com/symfony/symfony/pull/65131" class="reference external" rel="external noopener noreferrer" target="_blank">#65131</a> [FrameworkBundle] Fix "<a href="cache:clear" class="reference internal">cache:clear</a>" failing when the cache dir is rebuilt concurrently (@ousamabenyounes)</li>
<li>bug <a href="https://github.com/symfony/symfony/pull/65142" class="reference external" rel="external noopener noreferrer" target="_blank">#65142</a> [ObjectMapper] Fix the class-level mapping of another target being applied (@lazerg)</li>
<li>bug <a href="https://github.com/symfony/symfony/pull/65116" class="reference external" rel="external noopener noreferrer" target="_blank">#65116</a> [Lock] Fix a released read lock letting a new read lock through while a write lock is held (@Zombaya)</li>
<li>bug <a href="https://github.com/symfony/symfony/pull/65133" class="reference external" rel="external noopener noreferrer" target="_blank">#65133</a> [DependencyInjection] Fix ParameterBag::clear() leaving stale deprecations and resolution state (@javiereguiluz)</li>
<li>bug <a href="https://github.com/symfony/symfony/pull/65125" class="reference external" rel="external noopener noreferrer" target="_blank">#65125</a> [Serializer] Fix FILTER_BOOL breaking union type denormalization (@eliot488995568)</li>
<li>bug <a href="https://github.com/symfony/symfony/pull/65120" class="reference external" rel="external noopener noreferrer" target="_blank">#65120</a> [DependencyInjection] Fix #[Autoconfigure] being processed twice for PSR-4-discovered abstract types (@javiereguiluz)</li>
<li>bug <a href="https://github.com/symfony/symfony/pull/65113" class="reference external" rel="external noopener noreferrer" target="_blank">#65113</a> [DependencyInjection] Fix instantiating a lazy proxy for an inline definition (@nicolas-grekas)</li>
<li>bug <a href="https://github.com/symfony/symfony/pull/65112" class="reference external" rel="external noopener noreferrer" target="_blank">#65112</a> [DependencyInjection] Fix misleading error messages for invalid "proxy" tags (@nicolas-grekas)</li>
<li>bug <a href="https://github.com/symfony/symfony/pull/65111" class="reference external" rel="external noopener noreferrer" target="_blank">#65111</a> [DependencyInjection] Fix generating one proxy class per set of "proxy" tags (@nicolas-grekas)</li>
<li>bug <a href="https://github.com/symfony/symfony/pull/65109" class="reference external" rel="external noopener noreferrer" target="_blank">#65109</a> [DependencyInjection] Honor the interface passed to #[Lazy] (@nicolas-grekas)</li>
<li>bug <a href="https://github.com/symfony/symfony/pull/65091" class="reference external" rel="external noopener noreferrer" target="_blank">#65091</a> [DoctrineBridge] Close all expired connections in the idle listener (@RobLogtenberg-Pay)</li>
<li>bug <a href="https://github.com/symfony/symfony/pull/65095" class="reference external" rel="external noopener noreferrer" target="_blank">#65095</a> [WebProfilerBundle] Avoid a flash of unstyled content in the debug toolbar (@wayne530)</li>
<li>bug <a href="https://github.com/symfony/symfony/pull/65100" class="reference external" rel="external noopener noreferrer" target="_blank">#65100</a> [Translation][Lokalise] Send filter_filenames as a JSON array (@nicolas-grekas)</li>
<li>bug <a href="https://github.com/symfony/symfony/pull/65099" class="reference external" rel="external noopener noreferrer" target="_blank">#65099</a> [Mailer][Sendgrid] Map the processed event to RECEIVED (@nicolas-grekas)</li>
<li>bug <a href="https://github.com/symfony/symfony/pull/65032" class="reference external" rel="external noopener noreferrer" target="_blank">#65032</a> [JsonStreamer] Fix escaping, float precision and error reporting when writing JSON (@ousamabenyounes)</li>
<li>minor <a href="https://github.com/symfony/symfony/pull/65097" class="reference external" rel="external noopener noreferrer" target="_blank">#65097</a> [Form][Validator] Review Lithuanian (lt) translations (@MariusVarna)</li>
<li>bug <a href="https://github.com/symfony/symfony/pull/65093" class="reference external" rel="external noopener noreferrer" target="_blank">#65093</a> [Cache][Messenger] Restore compatibility with ext-redis 5 (@nicolas-grekas)</li>
<li>minor <a href="https://github.com/symfony/symfony/pull/65070" class="reference external" rel="external noopener noreferrer" target="_blank">#65070</a> [Notifier][Telegram] Document how to build a multi-row inline keyboard (@nicolas-grekas)</li>
<li>bug <a href="https://github.com/symfony/symfony/pull/65034" class="reference external" rel="external noopener noreferrer" target="_blank">#65034</a> [Mailer][Resend] Handle the email.failed and email.suppressed webhook events (@alexisLefebvre)</li>
<li>bug <a href="https://github.com/symfony/symfony/pull/65065" class="reference external" rel="external noopener noreferrer" target="_blank">#65065</a> [Console] Fix column widths when a row spans columns (@wazum)</li>
<li>bug <a href="https://github.com/symfony/symfony/pull/65062" class="reference external" rel="external noopener noreferrer" target="_blank">#65062</a> [Mime] Fix corrupted CID references when one is a prefix of another (@MarcinOrlowski)</li>
<li>bug <a href="https://github.com/symfony/symfony/pull/65066" class="reference external" rel="external noopener noreferrer" target="_blank">#65066</a> [Mailer] Fix rewriting cid references of inline parts for Mailgun (@nicolas-grekas)</li>
<li>bug <a href="https://github.com/symfony/symfony/pull/64696" class="reference external" rel="external noopener noreferrer" target="_blank">#64696</a> [Config] Generate unsealed array shapes for any normalizers (@ousamabenyounes)</li>
<li>bug <a href="https://github.com/symfony/symfony/pull/65060" class="reference external" rel="external noopener noreferrer" target="_blank">#65060</a> [TwigBridge][WebProfilerBundle] Fix compatibility with Twig 4 (@nicolas-grekas)</li>
<li>bug <a href="https://github.com/symfony/symfony/pull/65058" class="reference external" rel="external noopener noreferrer" target="_blank">#65058</a> [PropertyAccess] Fix caching of null read info (@javiereguiluz)</li>
<li>bug <a href="https://github.com/symfony/symfony/pull/65059" class="reference external" rel="external noopener noreferrer" target="_blank">#65059</a> [Translation] Fix fuzzy translations and multi-line msgctxt in PO files (@javiereguiluz)</li>
<li>minor <a href="https://github.com/symfony/symfony/pull/65055" class="reference external" rel="external noopener noreferrer" target="_blank">#65055</a> [Validator] Review Serbian (sr_Latn) translations (@milanqtx)</li>
<li>minor <a href="https://github.com/symfony/symfony/pull/65054" class="reference external" rel="external noopener noreferrer" target="_blank">#65054</a> [Validator] Review Serbian (sr_Cyrl) translations (@milanqtx)</li>
<li>bug <a href="https://github.com/symfony/symfony/pull/65041" class="reference external" rel="external noopener noreferrer" target="_blank">#65041</a> [Validator] Fix File constraint skipping the mime type check when extensions and mimeTypes have no common value (@nicolas-grekas)</li>
<li>minor <a href="https://github.com/symfony/symfony/pull/65048" class="reference external" rel="external noopener noreferrer" target="_blank">#65048</a> [VarExporter] cache result of hasOctalControlChars (@alexisLefebvre)</li>
<li>bug <a href="https://github.com/symfony/symfony/pull/65049" class="reference external" rel="external noopener noreferrer" target="_blank">#65049</a> [Translation] Do not split ICU messages on pipes in PoFileDumper (@nicolas-grekas)</li>
<li>bug <a href="https://github.com/symfony/symfony/pull/65052" class="reference external" rel="external noopener noreferrer" target="_blank">#65052</a> [PropertyInfo] Revert "bug #64990  Do not trigger legacy Type deprecat… (@nicolas-grekas)</li>
<li>minor <a href="https://github.com/symfony/symfony/pull/65046" class="reference external" rel="external noopener noreferrer" target="_blank">#65046</a> [VarExporter] Detect the null byte rendering instead of pinning a PHP version (@nicolas-grekas)</li>
<li>bug <a href="https://github.com/symfony/symfony/pull/65036" class="reference external" rel="external noopener noreferrer" target="_blank">#65036</a> [HttpClient] Revert " Strip Proxy-Authorization on cross-authority redirects" (@GrahamCampbell)</li>
</ul>
<span id="Symfony 7.4.16"></span>
</div>
                <hr style="margin-bottom: 5px" />
                <div style="font-size: 90%">
                    <a href="https://symfony.com/sponsor">Sponsor</a> the Symfony project.
                </div>
            ]]></content:encoded>
            <guid isPermaLink="false">https://symfony.com/blog/symfony-7-4-16-released?utm_source=Symfony%20Blog%20Feed&amp;utm_medium=feed</guid>
            <dc:creator><![CDATA[ Fabien Potencier ]]></dc:creator>
            <pubDate>Fri, 07 Aug 2026 20:03:31 +0200</pubDate>
            <comments>https://symfony.com/blog/symfony-7-4-16-released?utm_source=Symfony%20Blog%20Feed&amp;utm_medium=feed#comments-list</comments>
        </item>
                        <item>
            <title><![CDATA[SymfonyCon Warsaw 2026: Developing the developer: Journaling with AI]]></title>
            <link>https://symfony.com/blog/symfonycon-warsaw-2026-developing-the-developer-journaling-with-ai?utm_source=Symfony%20Blog%20Feed&amp;utm_medium=feed</link>
            <description>
    

SymfonyCon Warsaw 2026 is officially set for November 26 and 27, 2026, in Warsaw (Poland)! Join developers from across the globe for our flagship international event.



🎤 Speaker announcements

We are happy to host Timo Bakx, Webdeveloper, Linku for…</description>
            <content:encoded><![CDATA[
                                <p><a class="block text-center" href="https://live.symfony.com/2026-warsaw-con/">
    <img src="https://symfony.com/uploads/assets/blog/NL-BLOG-Banner-38.png" alt="Nl Blog Banner 38">
</a>
<strong><a href="https://live.symfony.com/2026-warsaw-con/">SymfonyCon Warsaw 2026</a></strong> is officially set for November 26 and 27, 2026, in Warsaw (Poland)! Join developers from across the globe for our flagship international event.</p>

<hr />

<h3>🎤 Speaker announcements</h3>

<p>We are happy to host <strong><a href="https://connect.symfony.com/profile/timobakx">Timo Bakx</a></strong>, Webdeveloper, Linku for his talk <strong><a href="https://live.symfony.com/2026-warsaw-con/schedule/developing-the-developer-journaling-with-ai">"Developing the developer: Journaling with AI"</a></strong>:</p>

<p>"In order to be able to show growth and improve myself as a developer and coworker, I need a system. Previous attempts at journaling failed, but with the rise of LLM and AI, there is a new option.</p>

<p>In this talk, I explain my journaling system, how it works for me, and how you can use the open sourced template to make it your own.</p>

<p>This is for developers: it is not an app, nor a SaaS. It's something you can use within your own IDE, with your own workflow, and your own modifications.</p>

<p>I've been using this system for a year now. It has grown, like I have. I will share some of the pitfalls and improvements I've went through so far, which includes better text writing, use of MCPs to pull data from other systems, and more."</p>

<p>Click here to see more <a href="https://live.symfony.com/2026-warsaw-con/schedule">talks</a> — the full schedule will be unveiled step-by-step over the coming weeks. Stay tuned!</p>

<h3>Ready to join us?</h3>

<h3>🏨 Accommodation</h3>

<p>The official conference hotel offers special preferential rates and flexible conditions for our attendees. Make sure to use our <strong><a href="https://www.hilton.com/en/book/reservation/rooms/?ctyhocn=WAWHIHI&amp;arrivalDate=2026-11-23&amp;departureDate=2026-11-29&amp;groupCode=GSYMDP&amp;room1NumAdults=1">dedicated link</a></strong> to book your room and stay right where the action happens!</p>

<h3>🥳 Save the date for the Community Party!</h3>

<p>Because SymfonyCon isn't just about code, we are happy to confirm that our traditional Community Party will take place on the evening of <strong>Thursday, November 26</strong>. Get ready for a great night of networking, fun, and connecting with peers!</p>

<h3>💻 Hackday</h3>

<p>Ready to contribute? The Hackday on <strong>Saturday, November 28</strong> (free and open to all!) will take place at "The Brain Embassy - Czackiego" thanks to the support of Baksla.sh. Bring your laptop and your good mood!</p>

<h3>🎟️ Get your tickets</h3>

<p>Regular tickets are available now, but don't wait too long. <strong><a href="https://live.symfony.com/2026-warsaw-con/registration">secure your ticket today</a></strong></p>

<hr />

<h3>Join us online!</h3>

<p>💡Follow the "conference" blog posts to not miss anything!</p>

<p>Want the latest Symfony updates? Follow us and tune in from wherever you are 🌎</p>

<p><a class="block text-center" href="https://linktr.ee/symfony">
   <img src="https://symfony.com/uploads/assets/blog/Banner-BLOG.png" alt="Banner Blog">
</a></p>

                <hr style="margin-bottom: 5px" />
                <div style="font-size: 90%">
                    <a href="https://symfony.com/sponsor">Sponsor</a> the Symfony project.
                </div>
            ]]></content:encoded>
            <guid isPermaLink="false">https://symfony.com/blog/symfonycon-warsaw-2026-developing-the-developer-journaling-with-ai?utm_source=Symfony%20Blog%20Feed&amp;utm_medium=feed</guid>
            <dc:creator><![CDATA[ Eloïse Charrier ]]></dc:creator>
            <pubDate>Thu, 06 Aug 2026 16:00:00 +0200</pubDate>
            <comments>https://symfony.com/blog/symfonycon-warsaw-2026-developing-the-developer-journaling-with-ai?utm_source=Symfony%20Blog%20Feed&amp;utm_medium=feed#comments-list</comments>
        </item>
                        <item>
            <title><![CDATA[New in Twig 4.0: A New Macro System]]></title>
            <link>https://symfony.com/blog/new-in-twig-4-0-a-new-macro-system?utm_source=Symfony%20Blog%20Feed&amp;utm_medium=feed</link>
            <description>
    
                    
                
            
            
    
        Contributed by
                    Fabien Potencier
                                

Macros are as old as Twig itself. They look like functions, you call
them like functions,…</description>
            <content:encoded><![CDATA[
                                <div class="blog-post-contributor-info">
    <div class="blog-post-contributor-avatar">
                    <a target="_blank" href="https://github.com/fabpot">
                <img src="https://github.com/fabpot.png" alt="Fabien Potencier">
            </a>
            </div>
    <div class="blog-post-contributor-contents">
        <span>Contributed by</span>
                    <a target="_blank" class="blog-post-contributor-name" href="https://github.com/fabpot">Fabien Potencier</a>
                                </div>
</div>
<p>Macros are as old as Twig itself. They look like functions, you call
them like functions, and for more than fifteen years they have quietly
refused to behave like functions: every argument is optional, extra
arguments vanish into a magic variable and a typo in a named argument
is silently ignored. Twig 4.0 ends the special treatment: macros become
full members of the callable family, with the same argument handling as
functions and filters. Let me show you what changes for your templates.</p>
<div class="section">
<h2 id="the-problem-macros-are-too-forgiving"><a class="headerlink" href="#the-problem-macros-are-too-forgiving" title="Permalink to this headline">The Problem: Macros Are Too Forgiving</a></h2>
<p>Take the form helper that almost every project has a variant of:</p>
<div translate="no" data-loc="4" class="notranslate codeblock codeblock-length-sm codeblock-html+twig codeblock-twig">
        <div class="codeblock-scroll">
        
        <pre class="codeblock-code"><code><span class="hljs-template-tag">{% <span class="hljs-name"><span class="hljs-keyword">macro</span></span> input(name, value, type, size) %}</span><span class="xml">
    <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"</span></span></span><span class="hljs-template-variable">{{ type|<span class="hljs-keyword">default</span>(<span class="hljs-string">'text'</span>) }}</span><span class="xml"><span class="hljs-tag"><span class="hljs-string">"</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"</span></span></span><span class="hljs-template-variable">{{ name }}</span><span class="xml"><span class="hljs-tag"><span class="hljs-string">"</span>
           <span class="hljs-attr">value</span>=<span class="hljs-string">"</span></span></span><span class="hljs-template-variable">{{ value|e }}</span><span class="xml"><span class="hljs-tag"><span class="hljs-string">"</span> <span class="hljs-attr">size</span>=<span class="hljs-string">"</span></span></span><span class="hljs-template-variable">{{ size|<span class="hljs-keyword">default</span>(20) }}</span><span class="xml"><span class="hljs-tag"><span class="hljs-string">"</span>/&gt;</span>
</span><span class="hljs-template-tag">{% <span class="hljs-name"><span class="hljs-keyword">endmacro</span></span> %}</span></code></pre>
    </div>
</div>
<p>Now, be honest: how many of these calls have you shipped at some point?</p>
<div translate="no" data-loc="10" class="notranslate codeblock codeblock-length-md codeblock-html+twig codeblock-twig">
        <div class="codeblock-scroll">
        
        <pre class="codeblock-code"><code><span class="hljs-template-tag">{% <span class="hljs-name"><span class="hljs-keyword">import</span></span> <span class="hljs-string">'forms.html.twig'</span> as forms %}</span><span class="xml">

</span><span class="hljs-comment">{# "name" is missing: it silently becomes null #}</span><span class="xml">
</span><span class="hljs-template-variable">{{ forms.input() }}</span><span class="xml">

</span><span class="hljs-comment">{# "sise" is a typo: it is silently swallowed and the size is lost #}</span><span class="xml">
</span><span class="hljs-template-variable">{{ forms.input(<span class="hljs-string">'username'</span>, sise: 30) }}</span><span class="xml">

</span><span class="hljs-comment">{# one argument too many: it silently lands in a magic "varargs" variable #}</span><span class="xml">
</span><span class="hljs-template-variable">{{ forms.input(<span class="hljs-string">'username'</span>, <span class="hljs-string">''</span>, <span class="hljs-string">'text'</span>, 20, 42) }}</span></code></pre>
    </div>
</div>
<p>None of them raises an error. All of them render broken HTML that you
have to spot by eye, often in production. I am partly to blame: macros
have been lenient since the very first version of Twig, and the
leniency even had a certain charm: a macro never complained, it always
rendered <em>something</em>. But <em>something</em> is rarely what you want from a
form helper, and the price shows in every macro out there: bodies
sprinkled with <code translate="no" class="notranslate">|default()</code> filters because no argument can be
trusted to be there.</p>
<p>Functions and filters have always been strict about their arguments;
macros were the only Twig callables that accepted anything. Twig 4.0
closes that gap.</p>
</div>
<div class="section">
<h2 id="required-arguments"><a class="headerlink" href="#required-arguments" title="Permalink to this headline">Required Arguments</a></h2>
<p>In Twig 4.0, a macro argument works like a PHP function argument: it is
required unless it declares a default value.</p>
<div translate="no" data-loc="7" class="notranslate codeblock codeblock-length-sm codeblock-html+twig codeblock-twig">
        <div class="codeblock-scroll">
        
        <pre class="codeblock-code"><code><span class="hljs-template-tag">{% <span class="hljs-name"><span class="hljs-keyword">macro</span></span> input(name, value = <span class="hljs-string">''</span>, type = <span class="hljs-string">'text'</span>, size = 20) %}</span><span class="xml">
    <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"</span></span></span><span class="hljs-template-variable">{{ type }}</span><span class="xml"><span class="hljs-tag"><span class="hljs-string">"</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"</span></span></span><span class="hljs-template-variable">{{ name }}</span><span class="xml"><span class="hljs-tag"><span class="hljs-string">"</span>
           <span class="hljs-attr">value</span>=<span class="hljs-string">"</span></span></span><span class="hljs-template-variable">{{ value|e }}</span><span class="xml"><span class="hljs-tag"><span class="hljs-string">"</span> <span class="hljs-attr">size</span>=<span class="hljs-string">"</span></span></span><span class="hljs-template-variable">{{ size }}</span><span class="xml"><span class="hljs-tag"><span class="hljs-string">"</span>/&gt;</span>
</span><span class="hljs-template-tag">{% <span class="hljs-name"><span class="hljs-keyword">endmacro</span></span> %}</span><span class="xml">

</span><span class="hljs-comment">{# Error: Value for argument "name" is required for macro "input". #}</span><span class="xml">
</span><span class="hljs-template-variable">{{ forms.input() }}</span></code></pre>
    </div>
</div>
<p>To keep an argument optional, give it an explicit default value (<code translate="no" class="notranslate">=
null</code> reproduces the old behavior). The signature now tells the whole
story: reading the definition is enough to know which arguments you
must pass and which ones you may omit. And because defaults live in the
signature, the body no longer needs defensive <code translate="no" class="notranslate">|default()</code> calls.</p>
</div>
<div class="section">
<h2 id="explicit-variadic-arguments"><a class="headerlink" href="#explicit-variadic-arguments" title="Permalink to this headline">Explicit Variadic Arguments</a></h2>
<p>Today, every macro is implicitly variadic: extra arguments are
collected into a reserved <code translate="no" class="notranslate">varargs</code> variable that no signature
mentions; you either know it exists or you don't. Twig 4.0 replaces
this magic with an explicit opt-in, using the same <code translate="no" class="notranslate">...</code> notation as
PHP:</p>
<div translate="no" data-loc="8" class="notranslate codeblock codeblock-length-sm codeblock-html+twig codeblock-twig">
        <div class="codeblock-scroll">
        
        <pre class="codeblock-code"><code><span class="hljs-template-tag">{% <span class="hljs-name"><span class="hljs-keyword">macro</span></span> tag(element, ...attrs) %}</span><span class="xml">
    <span class="hljs-tag">&lt;</span></span><span class="hljs-template-variable">{{ element }}</span><span class="xml"><span class="hljs-tag">
    </span></span><span class="hljs-template-tag">{%- <span class="hljs-name"><span class="hljs-keyword">for</span></span> name, value in attrs %}</span><span class="xml"><span class="hljs-tag"> </span></span><span class="hljs-template-variable">{{ name }}</span><span class="xml"><span class="hljs-tag">=<span class="hljs-string">"</span></span></span><span class="hljs-template-variable">{{ value }}</span><span class="xml"><span class="hljs-tag"><span class="hljs-string">"</span></span></span><span class="hljs-template-tag">{% <span class="hljs-name"><span class="hljs-keyword">endfor</span></span> -%}</span><span class="xml"><span class="hljs-tag">
    &gt;</span>
</span><span class="hljs-template-tag">{% <span class="hljs-name"><span class="hljs-keyword">endmacro</span></span> %}</span><span class="xml">

</span><span class="hljs-comment">{# renders &lt;input type="text" name="username"&gt; #}</span><span class="xml">
</span><span class="hljs-template-variable">{{ html.tag(<span class="hljs-string">'input'</span>, type: <span class="hljs-string">'text'</span>, name: <span class="hljs-string">'username'</span>) }}</span></code></pre>
    </div>
</div>
<p>The variadic argument collects both extra positional and extra named
arguments, which makes patterns like the HTML attributes helper above
natural to write. It must be the last argument, and you pick its name,
so the signature documents that the macro accepts an open-ended
argument list. A macro that does not declare a variadic argument
rejects extra arguments, like a function does.</p>
<p>The explicit syntax already works in Twig 3.29, so you can adopt it
today; a macro that relies on the implicit <code translate="no" class="notranslate">varargs</code> variable keeps
working as-is once it declares <code translate="no" class="notranslate">...varargs</code>.</p>
</div>
<div class="section">
<h2 id="calls-look-like-calls"><a class="headerlink" href="#calls-look-like-calls" title="Permalink to this headline">Calls Look Like Calls</a></h2>
<p>Referencing a macro and calling it have always been blurred together:
Twig quietly calls a macro referenced without parentheses, and accepts
parentheses in places where nothing is called. Twig 4.0 separates the
two usages. Calling a macro requires parentheses:</p>
<div translate="no" data-loc="4" class="notranslate codeblock codeblock-length-sm codeblock-html+twig codeblock-twig">
        <div class="codeblock-scroll">
        
        <pre class="codeblock-code"><code><span class="hljs-comment">{# Twig 3.x renders the macro as if you had written forms.input() #}</span><span class="xml">
</span><span class="hljs-comment">{# Twig 4.0: Omitting parentheses when calling a macro is not
   allowed; add parentheses after the macro name. #}</span><span class="xml">
</span><span class="hljs-template-variable">{{ forms.input }}</span></code></pre>
    </div>
</div>
<p>And testing whether a macro exists rejects them, because the <code translate="no" class="notranslate">defined</code>
test inspects the macro itself, it does not call it:</p>
<div translate="no" data-loc="6" class="notranslate codeblock codeblock-length-sm codeblock-html+twig codeblock-twig">
        <div class="codeblock-scroll">
        
        <pre class="codeblock-code"><code><span class="hljs-comment">{# Twig 4.0 #}</span><span class="xml">
</span><span class="hljs-template-tag">{% <span class="hljs-name"><span class="hljs-keyword">if</span></span> forms.input is defined %}</span><span class="xml">...</span><span class="hljs-template-tag">{% <span class="hljs-name"><span class="hljs-keyword">endif</span></span> %}</span><span class="xml">

</span><span class="hljs-comment">{# deprecated in 3.29: this reads as "is the result of the call
   defined?", which is not what the test checks #}</span><span class="xml">
</span><span class="hljs-template-tag">{% <span class="hljs-name"><span class="hljs-keyword">if</span></span> forms.input() is defined %}</span><span class="xml">...</span><span class="hljs-template-tag">{% <span class="hljs-name"><span class="hljs-keyword">endif</span></span> %}</span></code></pre>
    </div>
</div>
</div>
<div class="section">
<h2 id="clear-errors-instead-of-broken-output"><a class="headerlink" href="#clear-errors-instead-of-broken-output" title="Permalink to this headline">Clear Errors Instead of Broken Output</a></h2>
<p>With strict resolution, every calling mistake that used to corrupt your
output now fails with a precise message:</p>
<ul>
    <li><code translate="no" class="notranslate">Value for argument "name" is required for macro "input".</code></li>
<li><code translate="no" class="notranslate">Unknown argument "sise" for macro "input".</code></li>
<li><code translate="no" class="notranslate">Too many arguments for macro "input".</code></li>
<li><code translate="no" class="notranslate">Argument "name" is defined twice for macro "input".</code></li>
</ul>
<p>This is the same experience you already get when miscalling a function
or a filter; a typo now shows up during development instead of shipping
as an empty attribute. Definitions get the same cleanup: macro names
become case-sensitive in 4.0, so calling <code translate="no" class="notranslate">forms.Input()</code> no longer
resolves the <code translate="no" class="notranslate">input</code> macro; defining the same macro twice in one
template, which today silently keeps the last definition, becomes a
syntax error; and a <code translate="no" class="notranslate">macro</code> tag must now sit at the root of a
template, not nested inside an <code translate="no" class="notranslate">if</code> or another tag.</p>
</div>
<div class="section">
<h2 id="a-more-capable-macro-toolbox"><a class="headerlink" href="#a-more-capable-macro-toolbox" title="Permalink to this headline">A More Capable Macro Toolbox</a></h2>
<p>The overhaul does not come alone.</p>
<p><strong>Dynamic macro names</strong>, added in Twig 3.28: the macro to call can now
be computed at runtime by wrapping an expression in parentheses after
the dot operator:</p>
<div translate="no" data-loc="2" class="notranslate codeblock codeblock-length-sm codeblock-html+twig codeblock-twig">
        <div class="codeblock-scroll">
        
        <pre class="codeblock-code"><code><span class="hljs-template-tag">{% <span class="hljs-name"><span class="hljs-keyword">set</span></span> field = widget.multiline ? <span class="hljs-string">'textarea'</span> : <span class="hljs-string">'input'</span> %}</span><span class="xml">
</span><span class="hljs-template-variable">{{ forms.(field)(<span class="hljs-string">'description'</span>) }}</span></code></pre>
    </div>
</div>
<p><strong>Deprecating a macro</strong>: this one is not new, but it completes the
toolbox. Shared macro libraries can evolve their API the same way PHP
libraries do, by placing the <code translate="no" class="notranslate">deprecated</code> tag at the top of a macro
body; a deprecation notice is triggered whenever the macro is called:</p>
<div translate="no" data-loc="4" class="notranslate codeblock codeblock-length-sm codeblock-html+twig codeblock-twig">
        <div class="codeblock-scroll">
        
        <pre class="codeblock-code"><code><span class="hljs-template-tag">{% <span class="hljs-name"><span class="hljs-keyword">macro</span></span> input(name, value = <span class="hljs-string">''</span>) %}</span><span class="xml">
    </span><span class="hljs-template-tag">{% <span class="hljs-name"><span class="hljs-keyword">deprecated</span></span> <span class="hljs-string">'The "input" macro is deprecated, use "field" instead.'</span> %}</span><span class="xml">
    <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"</span></span></span><span class="hljs-template-variable">{{ name }}</span><span class="xml"><span class="hljs-tag"><span class="hljs-string">"</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"</span></span></span><span class="hljs-template-variable">{{ value|e }}</span><span class="xml"><span class="hljs-tag"><span class="hljs-string">"</span>/&gt;</span>
</span><span class="hljs-template-tag">{% <span class="hljs-name"><span class="hljs-keyword">endmacro</span></span> %}</span></code></pre>
    </div>
</div>
</div>
<div class="section">
<h2 id="the-upgrade-path"><a class="headerlink" href="#the-upgrade-path" title="Permalink to this headline">The Upgrade Path</a></h2>
<p>As usual, Twig 3.29 triggers a deprecation for every macro call and
definition that will break in 4.0. All the fixes work on 3.29, so an
application that runs deprecation-free is ready for Twig 4.0, where
macros finally behave like the functions they always looked like.</p>
</div>
                <hr style="margin-bottom: 5px" />
                <div style="font-size: 90%">
                    <a href="https://symfony.com/sponsor">Sponsor</a> the Symfony project.
                </div>
            ]]></content:encoded>
            <guid isPermaLink="false">https://symfony.com/blog/new-in-twig-4-0-a-new-macro-system?utm_source=Symfony%20Blog%20Feed&amp;utm_medium=feed</guid>
            <dc:creator><![CDATA[ Fabien Potencier ]]></dc:creator>
            <pubDate>Thu, 06 Aug 2026 14:12:00 +0200</pubDate>
            <comments>https://symfony.com/blog/new-in-twig-4-0-a-new-macro-system?utm_source=Symfony%20Blog%20Feed&amp;utm_medium=feed#comments-list</comments>
        </item>
                        <item>
            <title><![CDATA[New in Twig 4.0: A First-Class Sandbox]]></title>
            <link>https://symfony.com/blog/new-in-twig-4-0-a-first-class-sandbox?utm_source=Symfony%20Blog%20Feed&amp;utm_medium=feed</link>
            <description>
    
                    
                
            
            
    
        Contributed by
                    Fabien Potencier
                                

The sandbox has been part of Twig since 2009. If your users write newsletters,
CMS blocks…</description>
            <content:encoded><![CDATA[
                                <div class="blog-post-contributor-info">
    <div class="blog-post-contributor-avatar">
                    <a target="_blank" href="https://github.com/fabpot">
                <img src="https://github.com/fabpot.png" alt="Fabien Potencier">
            </a>
            </div>
    <div class="blog-post-contributor-contents">
        <span>Contributed by</span>
                    <a target="_blank" class="blog-post-contributor-name" href="https://github.com/fabpot">Fabien Potencier</a>
                                </div>
</div>
<p>The sandbox has been part of Twig since 2009. If your users write newsletters,
CMS blocks or email themes, the sandbox is the piece that keeps their
creativity away from your application internals. For seventeen years, it has
done that job as an extension bolted onto your application environment. Twig
4.0 promotes it to a first-class citizen: a dedicated <code translate="no" class="notranslate">Sandbox</code> class that
owns an environment built for untrusted templates, and for nothing else. Let
me show you how it works.</p>
<div class="section">
<h2 id="the-problem-one-environment-two-trust-levels"><a class="headerlink" href="#the-problem-one-environment-two-trust-levels" title="Permalink to this headline">The Problem: One Environment, Two Trust Levels</a></h2>
<p>The historical sandbox shares everything with your application. To render one
untrusted template, you enable sandbox mode on your main environment and
remember to disable it afterwards:</p>
<div translate="no" data-loc="10" class="notranslate codeblock codeblock-length-md codeblock-php">
        <div class="codeblock-scroll">
        
        <pre class="codeblock-code"><code><span class="hljs-keyword">use</span> <span class="hljs-title">Twig</span>\<span class="hljs-title">Extension</span>\<span class="hljs-title">SandboxExtension</span>;

<span class="hljs-variable"><span class="hljs-variable-other-marker">$</span>sandboxExtension</span> = <span class="hljs-variable"><span class="hljs-variable-other-marker">$</span>twig</span>-&gt;<span class="hljs-title invoke__">getExtension</span>(SandboxExtension::<span class="hljs-variable language_">class</span>);
<span class="hljs-variable"><span class="hljs-variable-other-marker">$</span>sandboxExtension</span>-&gt;<span class="hljs-title invoke__">enableSandbox</span>();

<span class="hljs-keyword">try</span> {
    <span class="hljs-keyword">echo</span> <span class="hljs-variable"><span class="hljs-variable-other-marker">$</span>twig</span>-&gt;<span class="hljs-title invoke__">render</span>(<span class="hljs-string">'newsletter.twig'</span>, <span class="hljs-variable"><span class="hljs-variable-other-marker">$</span>context</span>);
} <span class="hljs-keyword">finally</span> {
    <span class="hljs-variable"><span class="hljs-variable-other-marker">$</span>sandboxExtension</span>-&gt;<span class="hljs-title invoke__">disableSandbox</span>();
}</code></pre>
    </div>
</div>
<p>The documentation recommended a dedicated environment, and careful developers
created one by hand. But the API never asked for one. I wrote that design, and
for a long time I found it good enough: one environment, one switch.</p>
<p>Sharing, though, is precisely what you do not want at a security boundary. An
untrusted template could load any template known to the application loader
when loading templates was allowed by the policy. It saw every registered
global, including your <code translate="no" class="notranslate">app</code> variable, and inherited every extension,
filter, function and test used by your application. The security policy had to
compensate for an environment that was never designed for untrusted code.</p>
<p>Twig 4.0 gives sandboxed templates a dedicated environment, with no shared
state to toggle and no application configuration inherited by accident.</p>
</div>
<div class="section">
<h2 id="an-environment-of-their-own"><a class="headerlink" href="#an-environment-of-their-own" title="Permalink to this headline">An Environment of Their Own</a></h2>
<p>The new model fits in one sentence: you craft an environment for untrusted
templates, and a <code translate="no" class="notranslate">Sandbox</code> takes ownership of it:</p>
<div translate="no" data-loc="24" class="notranslate codeblock codeblock-length-md codeblock-php">
        <div class="codeblock-scroll">
        
        <pre class="codeblock-code"><code><span class="hljs-keyword">use</span> <span class="hljs-title">Twig</span>\<span class="hljs-title">Environment</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">Twig</span>\<span class="hljs-title">Loader</span>\<span class="hljs-title">ArrayLoader</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">Twig</span>\<span class="hljs-title">Sandbox</span>\<span class="hljs-title">Sandbox</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">Twig</span>\<span class="hljs-title">Sandbox</span>\<span class="hljs-title">SecurityPolicy</span>;

<span class="hljs-variable"><span class="hljs-variable-other-marker">$</span>sandboxEnvironment</span> = <span class="hljs-keyword">new</span> <span class="hljs-title invoke__">Environment</span>(
    <span class="hljs-keyword">new</span> <span class="hljs-title invoke__">ArrayLoader</span>(<span class="hljs-variable"><span class="hljs-variable-other-marker">$</span>newsletterTemplates</span>),
    [<span class="hljs-string">'cache'</span> =&gt; <span class="hljs-string">'/var/cache/newsletters'</span>],
);

<span class="hljs-variable"><span class="hljs-variable-other-marker">$</span>policy</span> = <span class="hljs-keyword">new</span> <span class="hljs-title invoke__">SecurityPolicy</span>(
    <span class="hljs-attr">allowedTags</span>: [<span class="hljs-string">'if'</span>, <span class="hljs-string">'for'</span>],
    <span class="hljs-attr">allowedFilters</span>: [<span class="hljs-string">'escape'</span>, <span class="hljs-string">'upper'</span>, <span class="hljs-string">'date'</span>],
);
<span class="hljs-variable"><span class="hljs-variable-other-marker">$</span>policy</span>-&gt;<span class="hljs-title invoke__">setStrict</span>(<span class="hljs-keyword">true</span>);

<span class="hljs-variable"><span class="hljs-variable-other-marker">$</span>sandbox</span> = <span class="hljs-keyword">new</span> <span class="hljs-title invoke__">Sandbox</span>(<span class="hljs-variable"><span class="hljs-variable-other-marker">$</span>sandboxEnvironment</span>, <span class="hljs-variable"><span class="hljs-variable-other-marker">$</span>policy</span>);

<span class="hljs-keyword">echo</span> <span class="hljs-variable"><span class="hljs-variable-other-marker">$</span>sandbox</span>-&gt;<span class="hljs-title invoke__">render</span>(<span class="hljs-string">'newsletter.twig'</span>, [<span class="hljs-string">'name'</span> =&gt; <span class="hljs-string">'Fabien'</span>]);

<span class="hljs-comment">// or render a template held as a string, straight from your database</span>
<span class="hljs-keyword">echo</span> <span class="hljs-variable"><span class="hljs-variable-other-marker">$</span>sandbox</span>-&gt;<span class="hljs-title invoke__">createTemplate</span>(<span class="hljs-variable"><span class="hljs-variable-other-marker">$</span>newsletter</span>-&gt;<span class="hljs-title invoke__">getBody</span>())-&gt;<span class="hljs-title invoke__">render</span>([
    <span class="hljs-string">'name'</span> =&gt; <span class="hljs-string">'Fabien'</span>,
]);</code></pre>
    </div>
</div>
<p>The environment must be fresh and dedicated to the sandbox. Passing one that
has already rendered a template or already has a <code translate="no" class="notranslate">SandboxExtension</code> throws a
<code translate="no" class="notranslate">LogicException</code> instead of creating a half-isolated setup. Never pass your
application environment: the sandbox takes ownership of it and renders all of
its templates in sandbox mode.</p>
<p><code translate="no" class="notranslate">Sandbox</code> implements <code translate="no" class="notranslate">SandboxInterface</code>. Type-hint the interface when you
inject a sandbox into an application service.</p>
<p>Each argument answers one question. The environment loader defines which
templates exist. Its built-in and registered extensions, filters, functions,
tests and globals define the available capabilities. The policy decides which
of those capabilities may execute, and which methods and properties may be
accessed on context objects.</p>
<p>There is no mode to toggle and no state to restore. Everything rendered
through a <code translate="no" class="notranslate">Sandbox</code> is sandboxed: <code translate="no" class="notranslate">render()</code>, <code translate="no" class="notranslate">display()</code> and
<code translate="no" class="notranslate">stream()</code>, their block counterparts <code translate="no" class="notranslate">renderBlock()</code>, <code translate="no" class="notranslate">displayBlock()</code>
and <code translate="no" class="notranslate">streamBlock()</code>, and the templates returned by <code translate="no" class="notranslate">createTemplate()</code>.
And because your application environment is not involved, a trusted render
happening in the middle of a sandboxed one is not sandboxed. Isolation works
in both directions.</p>
</div>
<div class="section">
<h2 id="strict-by-default"><a class="headerlink" href="#strict-by-default" title="Permalink to this headline">Strict by Default</a></h2>
<p>Twig's 4.0 <code translate="no" class="notranslate">SecurityPolicy</code> no longer has the historical exceptions that
implicitly allowed some tags, functions and tests. Anything that is not in an
allow-list is denied, except for built-ins that Twig marks as always safe in a
sandbox. The <code translate="no" class="notranslate">setStrict(true)</code> call opts into those rules on Twig 3.29; it is
a harmless no-op on Twig 4.0, so the same setup works on both versions.</p>
<p>What happens when a template author reaches beyond the policy or the dedicated
loader? Twig reports the exact capability or template involved:</p>
<ul>
    <li><code translate="no" class="notranslate">Filter "json_encode" is not allowed in "newsletter.twig" at line 1.</code></li>
<li><code translate="no" class="notranslate">Calling "delete" method on a "Customer" object is not allowed in
"profile.twig" at line 1.</code></li>
<li><code translate="no" class="notranslate">Template "admin/config.html.twig" is not defined in "page.twig" at
line 1.</code></li>
</ul>
<p>The last error comes from the loader, not the policy. Templates are not another
allow-list: <code translate="no" class="notranslate">include</code>, <code translate="no" class="notranslate">extends</code> and their friends can only resolve names
through the sandbox environment's loader.</p>
<p>One boundary remains worth spelling out. The sandbox restricts what template
source can express; PHP code invoked by an allowed filter, function or
extension still runs with full PHP capabilities. Only register callables that
are safe with attacker-chosen arguments, and enforce CPU and memory limits
outside Twig when resource exhaustion is a concern.</p>
</div>
<div class="section">
<h2 id="untrusted-fragments-in-trusted-pages"><a class="headerlink" href="#untrusted-fragments-in-trusted-pages" title="Permalink to this headline">Untrusted Fragments in Trusted Pages</a></h2>
<p>The classic CMS scenario is a trusted page embedding user-authored blocks. For
that, register the <code translate="no" class="notranslate">SandboxBridgeExtension</code> and inject the sandbox lazily into
its runtime:</p>
<div translate="no" data-loc="10" class="notranslate codeblock codeblock-length-md codeblock-php">
        <div class="codeblock-scroll">
        
        <pre class="codeblock-code"><code><span class="hljs-keyword">use</span> <span class="hljs-title">Twig</span>\<span class="hljs-title">Extension</span>\<span class="hljs-title">SandboxBridgeExtension</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">Twig</span>\<span class="hljs-title">Runtime</span>\<span class="hljs-title">SandboxBridgeRuntime</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">Twig</span>\<span class="hljs-title">RuntimeLoader</span>\<span class="hljs-title">FactoryRuntimeLoader</span>;

<span class="hljs-variable"><span class="hljs-variable-other-marker">$</span>twig</span>-&gt;<span class="hljs-title invoke__">addExtension</span>(<span class="hljs-keyword">new</span> <span class="hljs-title invoke__">SandboxBridgeExtension</span>());
<span class="hljs-variable"><span class="hljs-variable-other-marker">$</span>twig</span>-&gt;<span class="hljs-title invoke__">addRuntimeLoader</span>(<span class="hljs-keyword">new</span> <span class="hljs-title invoke__">FactoryRuntimeLoader</span>([
    SandboxBridgeRuntime::<span class="hljs-variable language_">class</span> =&gt; <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-params">()</span> =&gt;</span> <span class="hljs-keyword">new</span> <span class="hljs-title invoke__">SandboxBridgeRuntime</span>(
        <span class="hljs-variable"><span class="hljs-variable-other-marker">$</span>sandbox</span>,
    ),
]));</code></pre>
    </div>
</div>
<p>Your trusted template can then call <code translate="no" class="notranslate">render_sandboxed()</code>:</p>
<div translate="no" data-loc="8" class="notranslate codeblock codeblock-length-sm codeblock-html+twig codeblock-twig">
        <div class="codeblock-scroll">
        
        <pre class="codeblock-code"><code><span class="hljs-comment">{# page.html.twig, a trusted template rendered by your application #}</span><span class="xml">
<span class="hljs-tag">&lt;<span class="hljs-name">article</span>&gt;</span>
    </span><span class="hljs-template-variable">{{ render_sandboxed(
        <span class="hljs-string">'block-'</span> ~ <span class="hljs-name">block</span>.id,
        {title: page.title},
        <span class="hljs-string">'html'</span>,
    ) }}</span><span class="xml">
<span class="hljs-tag">&lt;/<span class="hljs-name">article</span>&gt;</span></span></code></pre>
    </div>
</div>
<p>The context mapping is the complete context passed to the sandboxed template;
variables from the trusted template are not copied implicitly. The third
argument declares the escaping strategy for which the result is safe. Unlike
<code translate="no" class="notranslate">|raw</code>, this keeps the result safe only in HTML and lets Twig escape it when
used in another context, such as JavaScript.</p>
<p>The strategy must be a non-empty literal string other than <code translate="no" class="notranslate">all</code>. It does not
sanitize the fragment: declaring <code translate="no" class="notranslate">html</code> means that HTML written by the
untrusted template author may reach the response, which should be an explicit
application decision.</p>
</div>
<div class="section">
<h2 id="the-upgrade-path"><a class="headerlink" href="#the-upgrade-path" title="Permalink to this headline">The Upgrade Path</a></h2>
<p>Twig 3.29 deprecates the <code translate="no" class="notranslate">sandboxed</code> argument of <code translate="no" class="notranslate">include()</code> and the
legacy <code translate="no" class="notranslate">SandboxExtension</code> methods, completing the path started by earlier
3.x deprecations. Their replacements, the <code translate="no" class="notranslate">Sandbox</code> class and
<code translate="no" class="notranslate">render_sandboxed()</code> function, work on 3.29, so a setup that no longer uses
legacy APIs is ready for Twig 4.0.</p>
</div>
                <hr style="margin-bottom: 5px" />
                <div style="font-size: 90%">
                    <a href="https://symfony.com/sponsor">Sponsor</a> the Symfony project.
                </div>
            ]]></content:encoded>
            <guid isPermaLink="false">https://symfony.com/blog/new-in-twig-4-0-a-first-class-sandbox?utm_source=Symfony%20Blog%20Feed&amp;utm_medium=feed</guid>
            <dc:creator><![CDATA[ Fabien Potencier ]]></dc:creator>
            <pubDate>Wed, 05 Aug 2026 14:09:00 +0200</pubDate>
            <comments>https://symfony.com/blog/new-in-twig-4-0-a-first-class-sandbox?utm_source=Symfony%20Blog%20Feed&amp;utm_medium=feed#comments-list</comments>
        </item>
                        <item>
            <title><![CDATA[SymfonyLive Germany 2027 heads to Cologne]]></title>
            <link>https://symfony.com/blog/symfonylive-germany-2027-heads-to-cologne?utm_source=Symfony%20Blog%20Feed&amp;utm_medium=feed</link>
            <description>Exciting news for the German Symfony community! After years of memorable editions in Berlin, our annual conference in Germany is starting a fresh chapter: SymfonyLive Germany is going on tour across the country!

For this first itinerant edition, we are thrilled…</description>
            <content:encoded><![CDATA[
                                <p>Exciting news for the German Symfony community! After years of memorable editions in Berlin, our annual conference in Germany is starting a fresh chapter: <strong><a href="https://live.symfony.com/2027-germany/">SymfonyLive Germany</a></strong> is going on tour across the country!</p>

<p>For this first itinerant edition, we are thrilled to bring the event to the vibrant city of <strong>Cologne</strong>.</p>

<h2>📅 Save the Date: May 18–21, 2027</h2>

<p>Join us at smartvillage Cologne: a bright, welcoming venue featuring a unique tropical vibe that creates the perfect setting for learning, networking, and connecting with fellow developers 🌴</p>

<p>Here is what the schedule looks like:</p>

<ul>
<li><strong>May 18–19, 2027</strong>: Hands-on pre-conference workshops</li>
<li><strong>May 20–21, 2027</strong>: Main conference featuring 15 talks delivered in English</li>
</ul>

<h2>🎤 The Call for Papers is open: we want to hear from you!</h2>

<p>Whether you are an experienced speaker or looking to share a project, the Call for Papers is officially open! We encourage you to submit your proposals for:</p>

<ul>
<li>A conference talk for the main event</li>
<li>A workshop to lead hands-on learning sessions</li>
</ul>

<p>👉 <strong><a href="https://live.symfony.com/2027-germany/cfp">Submit here</a></strong></p>

<p>What we provide:</p>

<ul>
<li>All speakers and trainers receive a free conference ticket. If you already bought a ticket, you'll be refunded.</li>
<li>Speakers who do not live in the conference city will also have their travel and accommodation expenses arranged and covered by the conference.</li>
</ul>

<p>We can’t wait to read your proposals and build an outstanding lineup together. Send us your ideas!</p>

<h2>🎟️ Early bird tickets are available</h2>

<p>Don't wait to secure your spot as the <strong><a href="https://live.symfony.com/2027-germany/registration/">early bird tickets are already available!</a></strong> Grab yours early to guarantee your seat for workshops, talks, and community networking:</p>

<ul>
<li>Workshops-only ticket: allows attending the selected workshops on May 18–19</li>
<li>Conference-only ticket: allows access to all conference talks on May 20–21</li>
<li>Conference + Workshops ticket: allows access to all talks and the selected workshops on May 18–21</li>
</ul>

<p>We can’t wait to see you all in Cologne and kick off this exciting new chapter together! 🫶</p>

                <hr style="margin-bottom: 5px" />
                <div style="font-size: 90%">
                    <a href="https://symfony.com/sponsor">Sponsor</a> the Symfony project.
                </div>
            ]]></content:encoded>
            <guid isPermaLink="false">https://symfony.com/blog/symfonylive-germany-2027-heads-to-cologne?utm_source=Symfony%20Blog%20Feed&amp;utm_medium=feed</guid>
            <dc:creator><![CDATA[ Eloïse Charrier ]]></dc:creator>
            <pubDate>Wed, 05 Aug 2026 09:00:00 +0200</pubDate>
            <comments>https://symfony.com/blog/symfonylive-germany-2027-heads-to-cologne?utm_source=Symfony%20Blog%20Feed&amp;utm_medium=feed#comments-list</comments>
        </item>
                        <item>
            <title><![CDATA[Symfony UX 3.4.0 released]]></title>
            <link>https://symfony.com/blog/symfony-ux-3-4-0-released?utm_source=Symfony%20Blog%20Feed&amp;utm_medium=feed</link>
            <description>Symfony UX 3.4 expands Toolkit with a Bootstrap 5.3 kit, reusable
design-system-agnostic behaviors and richer documentation for kit authors.
The release also adds dynamic Twig component names, safer on-demand icons,
modern React and Vue bundler support and…</description>
            <content:encoded><![CDATA[
                                <p>Symfony UX 3.4 expands Toolkit with a Bootstrap 5.3 kit, reusable
design-system-agnostic behaviors and richer documentation for kit authors.
The release also adds dynamic Twig component names, safer on-demand icons,
modern React and Vue bundler support and a stable Translator component.</p>
<div class="section">
<h2 id="bootstrap-5-3-kit"><a class="headerlink" href="#bootstrap-5-3-kit" title="Permalink to this headline">Bootstrap 5.3 Kit</a></h2>
<div class="blog-post-contributor-info">
    <div class="blog-post-contributor-avatar">
                    <a target="_blank" href="https://github.com/smnandre">
                <img src="https://github.com/smnandre.png" alt="Simon André">
            </a>
                    <a target="_blank" href="https://github.com/Kocal">
                <img src="https://github.com/Kocal.png" alt="Hugo Alliaume">
            </a>
            </div>
    <div class="blog-post-contributor-contents">
        <span>Contributed by</span>
                    <a target="_blank" class="blog-post-contributor-name" href="https://github.com/smnandre">Simon André</a>
             and                     <a target="_blank" class="blog-post-contributor-name" href="https://github.com/Kocal">Hugo Alliaume</a>
                                        <span class="blog-post-contributor-prs"> in
                                    <a target="_blank" href="https://github.com/symfony/ux/pull/3713">#3713</a>
                                                </span>
            </div>
</div>
<p>Bootstrap users previously had to turn the framework's markup into reusable
Twig components themselves. The new Bootstrap kit provides 25 ready-to-use
recipes based on Bootstrap 5.3, including accordions, alerts, cards, carousels,
dropdowns, modals, navigation bars, pagination, toasts and tooltips.</p>
<p>Install each recipe on demand with the Toolkit command:</p>
<div translate="no" data-loc="1" class="notranslate codeblock codeblock-length-sm codeblock-terminal codeblock-bash">
        <div class="codeblock-scroll">
        
        <pre class="codeblock-code"><code><span class="hljs-prompt">$ </span>php bin/console ux:install alert --kit bootstrap</code></pre>
    </div>
</div>
<p>The installed Twig components use the familiar Bootstrap options and classes:</p>
<div translate="no" data-loc="3" class="notranslate codeblock codeblock-length-sm codeblock-twig">
        <div class="codeblock-scroll">
        
        <pre class="codeblock-code"><code><span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">twig:Alert</span> <span class="hljs-attr">color</span>=<span class="hljs-string">"success"</span> <span class="hljs-attr">heading</span>=<span class="hljs-string">"Profile updated"</span> <span class="hljs-attr">dismissible</span>&gt;</span>
    Your account settings have been saved.
<span class="hljs-tag">&lt;/<span class="hljs-name">twig:Alert</span>&gt;</span></span></code></pre>
    </div>
</div>
<p>The components are copied into your application, where you can use them as-is
or customize their markup and behavior. Toolkit also includes the kit-level
Bootstrap dependency when installing any of these recipes.</p>
</div>
<div class="section">
<h2 id="design-system-agnostic-toolkit-recipes"><a class="headerlink" href="#design-system-agnostic-toolkit-recipes" title="Permalink to this headline">Design-System-Agnostic Toolkit Recipes</a></h2>
<div class="blog-post-contributor-info">
    <div class="blog-post-contributor-avatar">
                    <a target="_blank" href="https://github.com/kbond">
                <img src="https://github.com/kbond.png" alt="Kevin Bond">
            </a>
            </div>
    <div class="blog-post-contributor-contents">
        <span>Contributed by</span>
                    <a target="_blank" class="blog-post-contributor-name" href="https://github.com/kbond">Kevin Bond</a>
                                        <span class="blog-post-contributor-prs"> in
                                    <a target="_blank" href="https://github.com/symfony/ux/pull/3707">#3707</a>
                    ,                                     <a target="_blank" href="https://github.com/symfony/ux/pull/3725">#3725</a>
                     and                                     <a target="_blank" href="https://github.com/symfony/ux/pull/3740">#3740</a>
                                                </span>
            </div>
</div>
<p>Common interface behaviors are often rebuilt in every application because
they don't belong to a specific design system. The new <code translate="no" class="notranslate">common</code> Toolkit kit
provides five unstyled recipes that you can install and customize in your
project:</p>
<ul>
    <li><code translate="no" class="notranslate">post-link</code> submits a link as a form, with optional method spoofing, CSRF
protection and a confirmation prompt;</li>
<li><code translate="no" class="notranslate">logout-link</code> builds on <code translate="no" class="notranslate">post-link</code> to log users out securely with a
<code translate="no" class="notranslate">POST</code> request;</li>
<li><code translate="no" class="notranslate">closeable</code> removes an element immediately, after a delay or automatically;</li>
<li><code translate="no" class="notranslate">clipboard</code> copies a value or an element's content and supports several
kinds of success feedback;</li>
<li><code translate="no" class="notranslate">tooltip</code> provides accessible hover, focus, click and manual tooltips
positioned with Floating UI.</li>
</ul>
<p>Install recipes independently with the Toolkit command:</p>
<div translate="no" data-loc="1" class="notranslate codeblock codeblock-length-sm codeblock-terminal codeblock-bash">
        <div class="codeblock-scroll">
        
        <pre class="codeblock-code"><code><span class="hljs-prompt">$ </span>php bin/console ux:install clipboard --kit common</code></pre>
    </div>
</div>
<p>The installed controller can then be used directly in your templates:</p>
<div translate="no" data-loc="7" class="notranslate codeblock codeblock-length-sm codeblock-twig">
        <div class="codeblock-scroll">
        
        <pre class="codeblock-code"><code><span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">data-controller</span>=<span class="hljs-string">"clipboard"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">code</span> <span class="hljs-attr">data-clipboard-target</span>=<span class="hljs-string">"source"</span>&gt;</span>php bin/console cache:clear<span class="hljs-tag">&lt;/<span class="hljs-name">code</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"button"</span> <span class="hljs-attr">data-action</span>=<span class="hljs-string">"clipboard#copy"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">data-clipboard-target</span>=<span class="hljs-string">"idle"</span>&gt;</span>Copy<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">data-clipboard-target</span>=<span class="hljs-string">"success"</span> <span class="hljs-attr">hidden</span>&gt;</span>Copied!<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span></code></pre>
    </div>
</div>
<p>Because the recipes ship without presentation styles, they work with any CSS
framework or your own design system.</p>
</div>
<div class="section">
<h2 id="self-documenting-toolkit-recipes"><a class="headerlink" href="#self-documenting-toolkit-recipes" title="Permalink to this headline">Self-Documenting Toolkit Recipes</a></h2>
<div class="blog-post-contributor-info">
    <div class="blog-post-contributor-avatar">
                    <a target="_blank" href="https://github.com/Kocal">
                <img src="https://github.com/Kocal.png" alt="Hugo Alliaume">
            </a>
            </div>
    <div class="blog-post-contributor-contents">
        <span>Contributed by</span>
                    <a target="_blank" class="blog-post-contributor-name" href="https://github.com/Kocal">Hugo Alliaume</a>
                                        <span class="blog-post-contributor-prs"> in
                                    <a target="_blank" href="https://github.com/symfony/ux/pull/3718">#3718</a>
                                                </span>
            </div>
</div>
<p>Before Symfony UX 3.4, a recipe lived in the Symfony UX repository while its
documentation was a Twig template in the ux.symfony.com repository. Adding a
recipe or reorganizing its examples split the work almost equally between the
two repositories.</p>
<p>Recipes now ship a self-contained <code translate="no" class="notranslate">README.md</code> next to their implementation.
Toolkit renders it as HTML or portable Markdown, including installation
instructions, live examples and generated API references. In practice, about
99% of recipe work can now happen in the Symfony UX repository; the generic
rendering layer in ux.symfony.com accounts for the remaining 1%.</p>
</div>
<div class="section">
<h2 id="stimulus-controller-api-references"><a class="headerlink" href="#stimulus-controller-api-references" title="Permalink to this headline">Stimulus Controller API References</a></h2>
<div class="blog-post-contributor-info">
    <div class="blog-post-contributor-avatar">
                    <a target="_blank" href="https://github.com/kbond">
                <img src="https://github.com/kbond.png" alt="Kevin Bond">
            </a>
            </div>
    <div class="blog-post-contributor-contents">
        <span>Contributed by</span>
                    <a target="_blank" class="blog-post-contributor-name" href="https://github.com/kbond">Kevin Bond</a>
                                        <span class="blog-post-contributor-prs"> in
                                    <a target="_blank" href="https://github.com/symfony/ux/pull/3724">#3724</a>
                     and                                     <a target="_blank" href="https://github.com/symfony/ux/pull/3733">#3733</a>
                                                </span>
            </div>
</div>
<p>Recipes that only ship a Stimulus controller had no generated API reference.
Toolkit can now document the complete public API of a controller from
<code translate="no" class="notranslate">@value</code>, <code translate="no" class="notranslate">@target</code>, <code translate="no" class="notranslate">@css-class</code>, <code translate="no" class="notranslate">@outlet</code> and <code translate="no" class="notranslate">@action</code> tags in
its docblock:</p>
<div translate="no" data-loc="13" class="notranslate codeblock codeblock-length-md codeblock-javascript">
        <div class="codeblock-scroll">
        
        <pre class="codeblock-code"><code><span class="hljs-comment">/**
 * <span class="hljs-doctag">@value  </span>autoClose  Delay before the notification closes.
 * <span class="hljs-doctag">@target </span>timerbar   Element that displays the remaining time.
 * <span class="hljs-doctag">@action </span>close      Removes the notification.
 */</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Controller</span> </span>{
    <span class="hljs-keyword">static</span> values = { <span class="hljs-attr">autoClose</span>: <span class="hljs-built_in">Number</span> };
    <span class="hljs-keyword">static</span> targets = [<span class="hljs-string">'timerbar'</span>];

    close() {
        <span class="hljs-keyword">this</span>.element.remove();
    }
}</code></pre>
    </div>
</div>
<p>Toolkit generates the matching attributes, types and descriptions in the API
reference. Its linter also checks that the tags agree with the controller, so
the code remains the source of truth.</p>
</div>
<div class="section">
<h2 id="kit-level-dependencies"><a class="headerlink" href="#kit-level-dependencies" title="Permalink to this headline">Kit-Level Dependencies</a></h2>
<div class="blog-post-contributor-info">
    <div class="blog-post-contributor-avatar">
                    <a target="_blank" href="https://github.com/smnandre">
                <img src="https://github.com/smnandre.png" alt="Simon André">
            </a>
            </div>
    <div class="blog-post-contributor-contents">
        <span>Contributed by</span>
                    <a target="_blank" class="blog-post-contributor-name" href="https://github.com/smnandre">Simon André</a>
                                        <span class="blog-post-contributor-prs"> in
                                    <a target="_blank" href="https://github.com/symfony/ux/pull/3719">#3719</a>
                                                </span>
            </div>
</div>
<p>Dependencies shared by every recipe in a kit were declared in the kit
manifest but omitted when installing an individual recipe. The installer now
includes these Composer, npm and importmap dependencies automatically.</p>
<p>Kit authors can therefore declare a library once instead of repeating it in
every recipe manifest:</p>
<div translate="no" data-loc="10" class="notranslate codeblock codeblock-length-md codeblock-json">
        <div class="codeblock-scroll">
        
        <pre class="codeblock-code"><code>{
    <span class="hljs-attr">"name"</span>: <span class="hljs-string">"Bootstrap"</span>,
    <span class="hljs-attr">"dependencies"</span>: {
        <span class="hljs-attr">"npm"</span>: [<span class="hljs-string">"bootstrap@^5.3.0"</span>],
        <span class="hljs-attr">"importmap"</span>: [
            <span class="hljs-string">"bootstrap"</span>,
            <span class="hljs-string">"bootstrap/dist/css/bootstrap.min.css"</span>
        ]
    }
}</code></pre>
    </div>
</div>
</div>
<div class="section">
<h2 id="dynamic-twig-component-names"><a class="headerlink" href="#dynamic-twig-component-names" title="Permalink to this headline">Dynamic Twig Component Names</a></h2>
<div class="blog-post-contributor-info">
    <div class="blog-post-contributor-avatar">
                    <a target="_blank" href="https://github.com/andreybotanic">
                <img src="https://github.com/andreybotanic.png" alt="andreybotanic">
            </a>
            </div>
    <div class="blog-post-contributor-contents">
        <span>Contributed by</span>
                    <a target="_blank" class="blog-post-contributor-name" href="https://github.com/andreybotanic">andreybotanic</a>
                                        <span class="blog-post-contributor-prs"> in
                                    <a target="_blank" href="https://github.com/symfony/ux/pull/3395">#3395</a>
                                                </span>
            </div>
</div>
<p>Dynamic component selection previously required the <code translate="no" class="notranslate">component()</code> function,
which cannot define component blocks. The <code translate="no" class="notranslate">{% component %}</code> tag now accepts
a parenthesized expression, combining dynamic selection with props and blocks:</p>
<div translate="no" data-loc="7" class="notranslate codeblock codeblock-length-sm codeblock-twig">
        <div class="codeblock-scroll">
        
        <pre class="codeblock-code"><code><span class="hljs-template-tag">{% <span class="hljs-name"><span class="hljs-keyword">for</span></span> tab in dashboard.tabs %}</span><span class="xml">
    </span><span class="hljs-template-tag">{% <span class="hljs-name">component</span> (tab.componentName) with {loading: <span class="hljs-string">'lazy'</span>} %}</span><span class="xml">
        </span><span class="hljs-template-tag">{% <span class="hljs-name"><span class="hljs-keyword">block</span></span> loadingContent %}</span><span class="xml">
            <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>Loading </span><span class="hljs-template-variable">{{ tab.label }}</span><span class="xml">...<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
        </span><span class="hljs-template-tag">{% <span class="hljs-name"><span class="hljs-keyword">endblock</span></span> %}</span><span class="xml">
    </span><span class="hljs-template-tag">{% <span class="hljs-name">endcomponent</span> %}</span><span class="xml">
</span><span class="hljs-template-tag">{% <span class="hljs-name"><span class="hljs-keyword">endfor</span></span> %}</span></code></pre>
    </div>
</div>
<p>Parentheses distinguish an expression from a literal component name. For
example, <code translate="no" class="notranslate">{% component Alert %}</code> still renders the component named
<code translate="no" class="notranslate">Alert</code>, while <code translate="no" class="notranslate">{% component (componentName) %}</code> resolves the name from
the current context.</p>
</div>
<div class="section">
<h2 id="persistent-on-demand-icons"><a class="headerlink" href="#persistent-on-demand-icons" title="Permalink to this headline">Persistent On-Demand Icons</a></h2>
<div class="blog-post-contributor-info">
    <div class="blog-post-contributor-avatar">
                    <a target="_blank" href="https://github.com/Kocal">
                <img src="https://github.com/Kocal.png" alt="Hugo Alliaume">
            </a>
            </div>
    <div class="blog-post-contributor-contents">
        <span>Contributed by</span>
                    <a target="_blank" class="blog-post-contributor-name" href="https://github.com/Kocal">Hugo Alliaume</a>
                                        <span class="blog-post-contributor-prs"> in
                                    <a target="_blank" href="https://github.com/symfony/ux/pull/3741">#3741</a>
                                                </span>
            </div>
</div>
<p>The icon lock command cannot discover names that are built dynamically, which
can leave production dependent on the Iconify API. Enable the new
<code translate="no" class="notranslate">iconify.auto_lock</code> option in development to save every on-demand icon the
first time it is rendered:</p>
<div translate="no" data-loc="5" class="notranslate codeblock codeblock-length-sm codeblock-yaml">
        <div class="codeblock-scroll">
        
        <pre class="codeblock-code"><code><span class="hljs-comment"># config/packages/ux_icons.yaml</span>
<span class="hljs-string">when@dev:</span>
    <span class="hljs-attr">ux_icons:</span>
        <span class="hljs-attr">iconify:</span>
            <span class="hljs-attr">auto_lock:</span> <span class="hljs-literal">true</span></code></pre>
    </div>
</div>
<p>Browsing the application now writes those icons to <code translate="no" class="notranslate">assets/icons/</code>. Commit
the generated files so production can serve them locally without contacting
Iconify.</p>
</div>
<div class="section">
<h2 id="translator-is-now-stable"><a class="headerlink" href="#translator-is-now-stable" title="Permalink to this headline">Translator Is Now Stable</a></h2>
<div class="blog-post-contributor-info">
    <div class="blog-post-contributor-avatar">
                    <a target="_blank" href="https://github.com/Kocal">
                <img src="https://github.com/Kocal.png" alt="Hugo Alliaume">
            </a>
            </div>
    <div class="blog-post-contributor-contents">
        <span>Contributed by</span>
                    <a target="_blank" class="blog-post-contributor-name" href="https://github.com/Kocal">Hugo Alliaume</a>
                                        <span class="blog-post-contributor-prs"> in
                                    <a target="_blank" href="https://github.com/symfony/ux/pull/3716">#3716</a>
                                                </span>
            </div>
</div>
<p>Symfony UX Translator has been available for three years and its API has
stabilized. It is no longer marked as experimental, so applications can use
Symfony translations from JavaScript and TypeScript with the regular Symfony
backward compatibility expectations.</p>
</div>
<div class="section">
<h2 id="react-and-vue-support-for-modern-bundlers"><a class="headerlink" href="#react-and-vue-support-for-modern-bundlers" title="Permalink to this headline">React and Vue Support for Modern Bundlers</a></h2>
<div class="blog-post-contributor-info">
    <div class="blog-post-contributor-avatar">
                    <a target="_blank" href="https://github.com/Kocal">
                <img src="https://github.com/Kocal.png" alt="Hugo Alliaume">
            </a>
            </div>
    <div class="blog-post-contributor-contents">
        <span>Contributed by</span>
                    <a target="_blank" class="blog-post-contributor-name" href="https://github.com/Kocal">Hugo Alliaume</a>
                                        <span class="blog-post-contributor-prs"> in
                                    <a target="_blank" href="https://github.com/symfony/ux/pull/3714">#3714</a>
                                                </span>
            </div>
</div>
<p>React and Vue controller registration previously expected Webpack's
<code translate="no" class="notranslate">require.context()</code>. Both integrations now accept <code translate="no" class="notranslate">import.meta.glob()</code>,
which makes them work naturally with Vite and Rsbuild through Symfony Reprise:</p>
<div translate="no" data-loc="10" class="notranslate codeblock codeblock-length-md codeblock-javascript">
        <div class="codeblock-scroll">
        
        <pre class="codeblock-code"><code><span class="hljs-keyword">import</span> { registerReactControllerComponents } <span class="hljs-keyword">from</span> <span class="hljs-string">'@symfony/ux-react'</span>;
<span class="hljs-keyword">import</span> { registerVueControllerComponents } <span class="hljs-keyword">from</span> <span class="hljs-string">'@symfony/ux-vue'</span>;

registerReactControllerComponents(
    <span class="hljs-keyword">import</span>.meta.glob(<span class="hljs-string">'./react/controllers/**/*.{jsx,tsx}'</span>, { <span class="hljs-attr">eager</span>: <span class="hljs-literal">true</span> })
);

registerVueControllerComponents(
    <span class="hljs-keyword">import</span>.meta.glob(<span class="hljs-string">'./vue/controllers/**/*.vue'</span>)
);</code></pre>
    </div>
</div>
<p>React components must be imported eagerly. Vue supports both eager imports and
lazy loading. Existing Webpack Encore applications can continue using
<code translate="no" class="notranslate">require.context()</code> unchanged.</p>
</div>
<div class="section">
<h2 id="full-changelog"><a class="headerlink" href="#full-changelog" title="Permalink to this headline">Full Changelog</a></h2>
<ul>
    <li><a href="https://github.com/symfony/ux/pull/3740" class="reference external" rel="external noopener noreferrer" target="_blank">#3740</a> [Toolkit][Common] Add <code translate="no" class="notranslate">clipboard</code> and <code translate="no" class="notranslate">tooltip</code> recipes
(@kbond)</li>
<li><a href="https://github.com/symfony/ux/pull/3733" class="reference external" rel="external noopener noreferrer" target="_blank">#3733</a> [Toolkit] Document Stimulus controller CSS classes and outlets in
the API reference (@kbond)</li>
<li><a href="https://github.com/symfony/ux/pull/3741" class="reference external" rel="external noopener noreferrer" target="_blank">#3741</a> [Icons] Add <code translate="no" class="notranslate">auto_lock</code> to persist on-demand icons (@Kocal)</li>
<li><a href="https://github.com/symfony/ux/pull/3395" class="reference external" rel="external noopener noreferrer" target="_blank">#3395</a> [TwigComponent] Support component tag names from expressions
(@andreybotanic)</li>
<li><a href="https://github.com/symfony/ux/pull/3736" class="reference external" rel="external noopener noreferrer" target="_blank">#3736</a> [Toolkit] Rework and normalize usage of
<code translate="no" class="notranslate">attributes.defaults()</code> in kit components (@seb-jean, @Kocal)</li>
<li><a href="https://github.com/symfony/ux/pull/3735" class="reference external" rel="external noopener noreferrer" target="_blank">#3735</a> Add <code translate="no" class="notranslate">sync-packages.php</code> to normalize package <code translate="no" class="notranslate">.gitattributes</code>
files (@Kocal)</li>
<li><a href="https://github.com/symfony/ux/pull/3730" class="reference external" rel="external noopener noreferrer" target="_blank">#3730</a> Fix broken <code translate="no" class="notranslate">assets/tsconfig.json</code> reference affecting Webpack
5.109 and later (@zalesak)</li>
<li><a href="https://github.com/symfony/ux/pull/3734" class="reference external" rel="external noopener noreferrer" target="_blank">#3734</a> [Toolkit] Remove the <code translate="no" class="notranslate">height</code> preview option (@Kocal)</li>
<li><a href="https://github.com/symfony/ux/pull/3724" class="reference external" rel="external noopener noreferrer" target="_blank">#3724</a> [Toolkit] Document Stimulus controllers in the recipe API reference
(@kbond)</li>
<li><a href="https://github.com/symfony/ux/pull/3725" class="reference external" rel="external noopener noreferrer" target="_blank">#3725</a> [Toolkit][Common] Add the <code translate="no" class="notranslate">closeable</code> recipe (@kbond)</li>
<li><a href="https://github.com/symfony/ux/pull/3716" class="reference external" rel="external noopener noreferrer" target="_blank">#3716</a> [Translator] Make the component non-experimental (@Kocal)</li>
<li><a href="https://github.com/symfony/ux/pull/3713" class="reference external" rel="external noopener noreferrer" target="_blank">#3713</a> [Toolkit] Add the Bootstrap Toolkit kit (@smnandre, @Kocal)</li>
<li><a href="https://github.com/symfony/ux/pull/3720" class="reference external" rel="external noopener noreferrer" target="_blank">#3720</a> [Toolkit] Fix suggested frontend installation commands (@smnandre)</li>
<li><a href="https://github.com/symfony/ux/pull/3719" class="reference external" rel="external noopener noreferrer" target="_blank">#3719</a> [Toolkit] Support kit-level dependencies (@smnandre)</li>
<li><a href="https://github.com/symfony/ux/pull/3721" class="reference external" rel="external noopener noreferrer" target="_blank">#3721</a> [Toolkit] Require the correct <code translate="no" class="notranslate">twig/html-extra</code> version for
<code translate="no" class="notranslate">html_attr_*</code> filters (@Kocal)</li>
<li><a href="https://github.com/symfony/ux/pull/3718" class="reference external" rel="external noopener noreferrer" target="_blank">#3718</a> [Toolkit] Add a documentation rendering layer so kits can describe
themselves (@Kocal)</li>
<li><a href="https://github.com/symfony/ux/pull/3714" class="reference external" rel="external noopener noreferrer" target="_blank">#3714</a> [React][Vue] Add support for <code translate="no" class="notranslate">import.meta.glob()</code> through Symfony
Reprise (@Kocal)</li>
<li><a href="https://github.com/symfony/ux/pull/3707" class="reference external" rel="external noopener noreferrer" target="_blank">#3707</a> [Toolkit][Common] Add the <code translate="no" class="notranslate">common</code> kit with <code translate="no" class="notranslate">logout-link</code> and
<code translate="no" class="notranslate">post-link</code> recipes (@kbond)</li>
<li><a href="https://github.com/symfony/ux/pull/3710" class="reference external" rel="external noopener noreferrer" target="_blank">#3710</a> [Toolkit][Shadcn] Fix <code translate="no" class="notranslate">Table:Cell</code> and <code translate="no" class="notranslate">Table:Head</code> class
attribute merging (@stephen-lewis)</li>
<li><a href="https://github.com/symfony/ux/pull/3709" class="reference external" rel="external noopener noreferrer" target="_blank">#3709</a> [Toolkit] Add the <code translate="no" class="notranslate">ClassMergeSpacingChecker</code> to the linter
(@Kocal)</li>
</ul>
</div>
                <hr style="margin-bottom: 5px" />
                <div style="font-size: 90%">
                    <a href="https://symfony.com/sponsor">Sponsor</a> the Symfony project.
                </div>
            ]]></content:encoded>
            <guid isPermaLink="false">https://symfony.com/blog/symfony-ux-3-4-0-released?utm_source=Symfony%20Blog%20Feed&amp;utm_medium=feed</guid>
            <dc:creator><![CDATA[ Fabien Potencier ]]></dc:creator>
            <pubDate>Tue, 04 Aug 2026 15:53:00 +0200</pubDate>
            <comments>https://symfony.com/blog/symfony-ux-3-4-0-released?utm_source=Symfony%20Blog%20Feed&amp;utm_medium=feed#comments-list</comments>
        </item>
            </channel>
</rss>
