<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Jozef Môstka</title>
    <description>The latest articles on DEV Community by Jozef Môstka (@tito10047).</description>
    <link>https://dev.to/tito10047</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3024997%2Fb87cea6a-ffde-4026-9f9c-1b3535b8d1fe.jpg</url>
      <title>DEV Community: Jozef Môstka</title>
      <link>https://dev.to/tito10047</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/tito10047"/>
    <language>en</language>
    <item>
      <title>Symfony UX: Single Directory Components (SDC) - The Path to Cleaner Architecture Without Tailwind</title>
      <dc:creator>Jozef Môstka</dc:creator>
      <pubDate>Wed, 28 Jan 2026 04:39:00 +0000</pubDate>
      <link>https://dev.to/tito10047/symfony-ux-single-directory-components-sdc-the-path-to-cleaner-architecture-without-tailwind-2bmn</link>
      <guid>https://dev.to/tito10047/symfony-ux-single-directory-components-sdc-the-path-to-cleaner-architecture-without-tailwind-2bmn</guid>
      <description>&lt;p&gt;Frontend development in Symfony has taken a huge leap forward in recent years. Thanks to &lt;strong&gt;Symfony UX&lt;/strong&gt; and &lt;strong&gt;Twig Components&lt;/strong&gt;, we've gained the ability to write reusable components much like in React or Vue. However, the more components you have, the more you encounter one annoying problem: &lt;strong&gt;files are scattered across the entire project.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A PHP class in &lt;code&gt;src/Components&lt;/code&gt;, a Twig template in &lt;code&gt;templates/components&lt;/code&gt;, CSS in &lt;code&gt;assets/styles&lt;/code&gt;, and JavaScript somewhere else entirely. When you want to change the color of a button, you're looking at a trip through four different directories.&lt;/p&gt;

&lt;p&gt;Some time ago, Hugo Alliaume published a great article &lt;a href="https://hugo.alliau.me/blog/posts/a-better-architecture-for-your-symfony-ux-twig-components" rel="noopener noreferrer"&gt;A Better Architecture for Your Symfony UX Twig Components&lt;/a&gt;, where he proposed the concept of &lt;strong&gt;Single Directory Components (SDC)&lt;/strong&gt;. The idea is simple: let's have everything in one folder.&lt;/p&gt;

&lt;p&gt;Today, we'll show you how to take this concept to the next level, completely get rid of manual configuration, and maybe even Tailwind.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Single Directory Components (SDC)?
&lt;/h2&gt;

&lt;p&gt;If you've ever worked with Angular or Vue, you know that feeling of order. An &lt;code&gt;Alert&lt;/code&gt; component is a single folder containing everything essential.&lt;/p&gt;

&lt;p&gt;In the SDC approach, it looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;src_component/
└── UI/
    └── Button/
        ├── Button.php           # Logic
        ├── Button.html.twig     # Template
        ├── Button.css           # Styles
        └── Button_controller.js # Interactions (Stimulus)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Main Advantages:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Maintainability:&lt;/strong&gt; When you no longer need a component, you just delete one folder.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Developer Experience:&lt;/strong&gt; No context switching between distant folders.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Isolation:&lt;/strong&gt; Styles and scripts belong only to that one component.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Is Tailwind Passé? The Power of Native CSS and Variables
&lt;/h2&gt;

&lt;p&gt;Lately, there's more and more talk about how, with the arrival of modern CSS features (like CSS variables, nesting, &lt;code&gt;@layer&lt;/code&gt;), the need for utility frameworks like Tailwind is decreasing. Articles like &lt;a href="https://medium.com/@all.technology.stories/efc42ac3f83b" rel="noopener noreferrer"&gt;The Power of CSS Variables&lt;/a&gt; remind us that pure CSS is stronger today than ever before.&lt;/p&gt;

&lt;p&gt;The SDC approach plays right into this trend. When you have a CSS file right next to your PHP class, you don't need to write 20 Tailwind classes in your HTML. You can define clean, semantic CSS that uses CSS variables linked directly to PHP logic.&lt;/p&gt;




&lt;h2&gt;
  
  
  Our Solution: UX SDC Bundle
&lt;/h2&gt;

&lt;p&gt;Hugo's article showed how to set up SDC manually. However, it requires changes to &lt;code&gt;composer.json&lt;/code&gt;, Twig configuration, AssetMapper, and Stimulus.&lt;/p&gt;

&lt;p&gt;Our bundle &lt;code&gt;tito10047/ux-sdc&lt;/code&gt; does it for you. It's a &lt;strong&gt;Zero Configuration&lt;/strong&gt; solution. Just install the bundle and mark the class with an attribute.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="na"&gt;#[AsSdcComponent('UI:Button')]&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Button&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// The bundle automatically finds Button.html.twig, Button.css, and Button_controller.js&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The "Magic" Inside (Without Performance Loss)
&lt;/h3&gt;

&lt;p&gt;The bundle solves technical challenges that you would have to patch yourself with a manual approach:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Asset Orchestration:&lt;/strong&gt; CSS and JS files are injected into the page &lt;strong&gt;only when&lt;/strong&gt; the component is actually rendered.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No More "Phantom" Controllers:&lt;/strong&gt; You don't have to create an empty Stimulus controller just so AssetMapper knows about your CSS. The bundle handles it for you.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;HTTP Preload &amp;amp; FOUC:&lt;/strong&gt; We automatically generate &lt;code&gt;Link&lt;/code&gt; headers for HTTP preload. The browser starts downloading CSS even before it starts parsing HTML. This eliminates the annoying flash of unstyled content (FOUC).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Production Performance:&lt;/strong&gt; Thanks to a compiler pass, no disk scanning happens in production. Everything is pre-prepared in the cache.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Real-world Example: Project "Formalitka"
&lt;/h2&gt;

&lt;p&gt;What does it look like in the real world? Take a look at the project &lt;a href="https://formalitka.mostka.sk" rel="noopener noreferrer"&gt;formalitka.mostka.sk&lt;/a&gt;. The entire UI kit is built on SDC without a single line of Tailwind.&lt;/p&gt;

&lt;p&gt;Let's take their &lt;code&gt;Button&lt;/code&gt; component. The PHP class defines properties like &lt;code&gt;color&lt;/code&gt; or &lt;code&gt;size&lt;/code&gt;. The CSS then processes these properties using variables:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="c"&gt;/* Button.css */&lt;/span&gt;
&lt;span class="k"&gt;@layer&lt;/span&gt; &lt;span class="n"&gt;components&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;.button&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--color-primary&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--color-on-primary&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nl"&gt;transition&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--transition&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="err"&gt;&amp;amp;.button--secondary&lt;/span&gt; &lt;span class="err"&gt;{&lt;/span&gt;
            &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--color-secondary&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="err"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Thanks to SDC, this complex component (containing seal animations, sounds, and special effects) is contained within one directory and is easy to maintain.&lt;/p&gt;




&lt;h2&gt;
  
  
  How to Get Started?
&lt;/h2&gt;

&lt;p&gt;Installation is lightning fast:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;composer require tito10047/ux-sdc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Register the bundle and in &lt;code&gt;config/packages/ux_sdc.yaml&lt;/code&gt;, set where your components reside:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;ux_sdc&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;ux_components_dir&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;%kernel.project_dir%/src_component'&lt;/span&gt;
    &lt;span class="na"&gt;component_namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;App\Component'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And add the asset placeholder to your &lt;code&gt;base.html.twig&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight twig"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
    &lt;span class="cp"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;render_component_assets&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="cp"&gt;}}&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it. From this moment on, just create directories and write code.&lt;/p&gt;




&lt;h2&gt;
  
  
  Performance Under the Microscope: Proof Instead of Promises
&lt;/h2&gt;

&lt;p&gt;When automation and "magic" come into play, the question often arises: &lt;em&gt;What about performance?&lt;/em&gt; We put the SDC approach to a stress test with 500 unique components. Here are the key findings from our benchmarks:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Scenario&lt;/th&gt;
&lt;th&gt;Classic Approach&lt;/th&gt;
&lt;th&gt;SDC Approach&lt;/th&gt;
&lt;th&gt;Difference&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Warmup (Prod)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;583.1 ms&lt;/td&gt;
&lt;td&gt;586.2 ms&lt;/td&gt;
&lt;td&gt;+3.1 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Render (Prod Runtime)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;26.5 ms&lt;/td&gt;
&lt;td&gt;31.6 ms&lt;/td&gt;
&lt;td&gt;+5.1 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Render (Dev Runtime)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;26.5 ms&lt;/td&gt;
&lt;td&gt;88.4 ms&lt;/td&gt;
&lt;td&gt;+61.9 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;What does this mean in practice?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;In production, the overhead is almost zero.&lt;/strong&gt; Thanks to the compiler pass, no disk scanning happens at runtime. Rendering a single component has an overhead of only about &lt;strong&gt;8.8µs&lt;/strong&gt;, a negligible price for fully automated asset and Stimulus management.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Dev mode is optimized for DX.&lt;/strong&gt; Instead of having to clear the cache every time a file changes, the bundle in dev mode uses &lt;strong&gt;runtime autodiscovery&lt;/strong&gt;. It scans the disk only for those components that are currently being rendered. This means that if you add a new CSS file or change a Twig template, you see the changes immediately. Thanks to internal metadata caching within a single request, repeated rendering of the same component remains very fast.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Memory Footprint:&lt;/strong&gt; With 500 components, the bundle consumes about 8MB more memory during container compilation, which is perfectly fine for modern applications.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can find the full report in our &lt;a href="https://github.com/tito10047/ux-twig-component-asset/blob/main/benchmark.md" rel="noopener noreferrer"&gt;benchmark.md&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Future and Your Feedback (Version 0.0.1)
&lt;/h2&gt;

&lt;p&gt;This bundle is currently in version &lt;strong&gt;0.0.1&lt;/strong&gt;. It's an early stage where we're looking for the right path. I wrote this article not only to share our solution but mainly to get feedback from you, the community.&lt;/p&gt;

&lt;p&gt;We're interested in:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What do you think about the proposed API?&lt;/li&gt;
&lt;li&gt;Is there anything missing that you would expect in an SDC approach?&lt;/li&gt;
&lt;li&gt;Do you see any pitfalls in this approach that we've overlooked?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We want this bundle to be built on solid foundations and real developer needs from the first stable version. Every GitHub issue, discussion, or comment moves us forward.&lt;/p&gt;




&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Single Directory Components combined with modern CSS are changing the way we think about Symfony UX. We're getting rid of unnecessary boilerplate code, cleaning up our project structure, and increasing our performance as developers.&lt;/p&gt;

&lt;p&gt;Try the &lt;a href="https://github.com/tito10047/ux-twig-component-asset" rel="noopener noreferrer"&gt;UX SDC Bundle&lt;/a&gt; and say goodbye to scattered files. The future of Symfony UX is in order and clarity.&lt;/p&gt;

</description>
      <category>symfony</category>
      <category>php</category>
      <category>webdev</category>
    </item>
    <item>
      <title>The Ultimate Image Solution for Symfony - stop writing srcset manually</title>
      <dc:creator>Jozef Môstka</dc:creator>
      <pubDate>Fri, 09 Jan 2026 12:09:12 +0000</pubDate>
      <link>https://dev.to/tito10047/stop-writing-srcset-manually-the-ultimate-image-solution-for-symfony-59cn</link>
      <guid>https://dev.to/tito10047/stop-writing-srcset-manually-the-ultimate-image-solution-for-symfony-59cn</guid>
      <description>&lt;h2&gt;
  
  
  Solving the Image Problem in Symfony: Meet PGI
&lt;/h2&gt;

&lt;p&gt;Images are the heaviest part of the modern web. They are responsible for slow page loads, frustrating Cumulative Layout Shift (CLS), and a nightmare developer experience when trying to implement truly responsive designs. If you’ve ever wrestled with complex &lt;code&gt;&amp;lt;picture&amp;gt;&lt;/code&gt; tags or manually calculated &lt;code&gt;srcset&lt;/code&gt; values for different breakpoints, you know the pain.&lt;/p&gt;

&lt;p&gt;Enter &lt;strong&gt;PGI&lt;/strong&gt; (Progressive Image Bundle)—the new standard for image handling in Symfony 6.4 and newer versions.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Hook: Why Core Web Vitals Changed Everything
&lt;/h3&gt;

&lt;p&gt;Google’s Core Web Vitals—specifically Largest Contentful Paint (LCP) and Cumulative Layout Shift (CLS)—have transformed how we build websites. It’s no longer enough to just "show an image." You need to show it fast, and you must ensure the layout doesn’t jump when the image finally appears.&lt;/p&gt;

&lt;p&gt;For many Symfony developers, achieving a perfect 100/100 PageSpeed score while maintaining a clean codebase has felt like a trade-off. You either spend hours writing custom responsive logic or you settle for "good enough" performance. PGI was born to eliminate that trade-off.&lt;/p&gt;

&lt;h3&gt;
  
  
  Code Showcase: From Messy to Masterful
&lt;/h3&gt;

&lt;p&gt;Let’s look at the difference. A standard responsive image in Twig often looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight twig"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="cp"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;asset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'images/hero.jpg'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="cp"&gt;}}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt; 
     &lt;span class="na"&gt;srcset=&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="cp"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;asset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'images/hero_sm.jpg'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="cp"&gt;}}&lt;/span&gt;&lt;span class="s"&gt; 600w, &lt;/span&gt;&lt;span class="cp"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;asset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'images/hero_lg.jpg'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="cp"&gt;}}&lt;/span&gt;&lt;span class="s"&gt; 1200w"&lt;/span&gt;
     &lt;span class="na"&gt;sizes=&lt;/span&gt;&lt;span class="s"&gt;"(max-width: 600px) 100vw, 50vw"&lt;/span&gt;
     &lt;span class="na"&gt;width=&lt;/span&gt;&lt;span class="s"&gt;"1200"&lt;/span&gt; &lt;span class="na"&gt;height=&lt;/span&gt;&lt;span class="s"&gt;"675"&lt;/span&gt;
     &lt;span class="na"&gt;loading=&lt;/span&gt;&lt;span class="s"&gt;"lazy"&lt;/span&gt;
     &lt;span class="na"&gt;alt=&lt;/span&gt;&lt;span class="s"&gt;"Hero image"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And that’s just for &lt;em&gt;one&lt;/em&gt; aspect ratio. With PGI, you get a clean, Tailwind-inspired syntax:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight twig"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;twig:pgi:Image&lt;/span&gt; 
    &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"images/hero.jpg"&lt;/span&gt; 
    &lt;span class="na"&gt;sizes=&lt;/span&gt;&lt;span class="s"&gt;"sm:12@square md:6@landscape"&lt;/span&gt;
    &lt;span class="na"&gt;alt=&lt;/span&gt;&lt;span class="s"&gt;"Responsive hero image"&lt;/span&gt; 
    &lt;span class="na"&gt;preload&lt;/span&gt;
&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Real-world Case: just6words.com
&lt;/h4&gt;

&lt;p&gt;To see PGI in action, let's look at how it handles a single image across different devices using an example from &lt;a href="https://just6words.com/" rel="noopener noreferrer"&gt;just6words.com&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Instead of writing multiple &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; tags or complex &lt;code&gt;&amp;lt;picture&amp;gt;&lt;/code&gt; logic, you define everything in one place:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight twig"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;twig:pgi:Image&lt;/span&gt; 
    &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"realcase.jpg"&lt;/span&gt; 
    &lt;span class="na"&gt;sizes=&lt;/span&gt;&lt;span class="s"&gt;"sm:12@square md:6@landscape"&lt;/span&gt; 
    &lt;span class="na"&gt;alt=&lt;/span&gt;&lt;span class="s"&gt;"Responsive real-world example"&lt;/span&gt;
&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h5&gt;
  
  
  What happens on different devices?
&lt;/h5&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Mobile (sm):&lt;/strong&gt; The browser loads a &lt;strong&gt;Square (1:1)&lt;/strong&gt; crop. It takes the full width of the container.
&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fld739iug4pu86tst6czu.jpg" alt="Mobile (sm) - Square" width="640" height="640"&gt; &lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Tablet/Desktop (md+):&lt;/strong&gt; The browser automatically switches to a &lt;strong&gt;Landscape (16:9)&lt;/strong&gt; crop. The image now occupies only half of the grid (6/12 columns).
&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fiqgeuz4l2fuq9lmu1af3.jpg" alt="Tablet/Desktop (md) - Landscape" width="384" height="216"&gt; &lt;/li&gt;
&lt;/ol&gt;

&lt;h5&gt;
  
  
  Rendered HTML (Clean &amp;amp; Semantic)
&lt;/h5&gt;

&lt;p&gt;PGI generates a single, optimized block of HTML that handles all the heavy lifting, including the &lt;strong&gt;Blur Placeholder&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"progressive-image-container"&lt;/span&gt; 
     &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"--img-width-sm: 640px; --img-aspect-sm: 1; --img-width-md: 384px; --img-aspect-md: 1.7777777777778;"&lt;/span&gt; &lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;canvas&lt;/span&gt; &lt;span class="na"&gt;data-tito10047--progressive-image-bundle--progressive-image-target=&lt;/span&gt;&lt;span class="s"&gt;"placeholder"&lt;/span&gt; &lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/canvas&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"/realcase.jpg"&lt;/span&gt; 
         &lt;span class="na"&gt;srcset=&lt;/span&gt;&lt;span class="s"&gt;"/media/cache/640x640/realcase.jpg 640w, /media/cache/384x216/realcase.jpg 384w"&lt;/span&gt; 
         &lt;span class="na"&gt;sizes=&lt;/span&gt;&lt;span class="s"&gt;"(min-width: 768px) 384px, (min-width: 640px) 640px, 100vw"&lt;/span&gt; &lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  What's happening here?
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;sm:12@square&lt;/code&gt;&lt;/strong&gt;: Full width on small screens, automatically cropped to 1:1.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;md:6@landscape&lt;/code&gt;&lt;/strong&gt;: Half width (6/12 columns) from medium breakpoint, automatically cropped to 16:9.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;xl:[430x370]&lt;/code&gt;&lt;/strong&gt;: Need a very specific size for a custom layout? PGI supports arbitrary values directly in the &lt;code&gt;sizes&lt;/code&gt; attribute.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;preload&lt;/code&gt;&lt;/strong&gt;: PGI automatically injects a &lt;code&gt;&amp;lt;link rel="preload"&amp;gt;&lt;/code&gt; into your &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt;, optimizing your LCP score instantly.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  The "Progressive" in PGI: Blur-up Experience
&lt;/h4&gt;

&lt;p&gt;One of the most satisfying features for users is the built-in &lt;strong&gt;Blurhash&lt;/strong&gt; support. Instead of showing a blank space or a generic spinner, PGI renders a beautiful, ultra-lightweight blurred version of the image immediately.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F57enz2jygzke7hgt5oyr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F57enz2jygzke7hgt5oyr.png" alt="Blured image" width="758" height="347"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This "blur-up" technique significantly improves &lt;strong&gt;perceived performance&lt;/strong&gt;. Users see the layout and the context of the image instantly, even on slow connections, while the high-resolution version loads in the background. Once ready, the high-res image smoothly fades in, replacing the placeholder.&lt;/p&gt;

&lt;h3&gt;
  
  
  Technical Deep Dive: Zero CLS and CSS Magic
&lt;/h3&gt;

&lt;p&gt;How does PGI prevent layout shift? It’s not just about adding &lt;code&gt;width&lt;/code&gt; and &lt;code&gt;height&lt;/code&gt; attributes. PGI leverages modern CSS &lt;code&gt;aspect-ratio&lt;/code&gt; and CSS variables.&lt;/p&gt;

&lt;p&gt;When the component renders, it calculates the aspect ratio based on your &lt;code&gt;sizes&lt;/code&gt; definition. It then wraps the image in a container that reserves the exact space needed. No more content jumping as images load—even if the image is lazy-loaded or slow to arrive.&lt;/p&gt;

&lt;h4&gt;
  
  
  Powering the Engine: LiipImagine Integration
&lt;/h4&gt;

&lt;p&gt;PGI doesn't reinvent the wheel for image processing. Instead, it stands on the shoulders of a giant: &lt;strong&gt;LiipImagineBundle&lt;/strong&gt;. By default, PGI can delegate the heavy lifting of resizing and filtering to LiipImagine, allowing you to leverage its full ecosystem.&lt;/p&gt;

&lt;p&gt;One of the most powerful features of this synergy is the &lt;strong&gt;automatic conversion to modern formats like WebP&lt;/strong&gt;. With just a few lines of configuration, you can ensure that every image served by PGI is not only perfectly sized but also perfectly compressed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example Configuration:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# config/packages/liip_imagine.yaml&lt;/span&gt;
&lt;span class="na"&gt;liip_imagine&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;default_filter_set_settings&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;format&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;webp&lt;/span&gt;
    &lt;span class="na"&gt;webp&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;generate&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;

&lt;span class="c1"&gt;# config/packages/progressive_image.yaml&lt;/span&gt;
&lt;span class="na"&gt;progressive_image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image_configs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;quality&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;75&lt;/span&gt;
        &lt;span class="na"&gt;post_processors&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;cwebp&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;q&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;30&lt;/span&gt; &lt;span class="pi"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By using the &lt;code&gt;liip_imagine&lt;/code&gt; decorator, PGI automatically routes image requests through LiipImagine's filter system. This means you can use all your existing Liip filters while benefiting from PGI's superior Twig syntax and Zero CLS features.&lt;/p&gt;

&lt;h3&gt;
  
  
  Under the Hood: The Configuration That Makes It Possible
&lt;/h3&gt;

&lt;p&gt;One of the most praised aspects of PGI is its flexibility. While it works out-of-the-box, the real power lies in its configuration. Let’s break down the most important parts of &lt;code&gt;progressive_image.yaml&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# config/packages/progressive_image.yaml&lt;/span&gt;
&lt;span class="na"&gt;progressive_image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# 1. Responsive Strategy&lt;/span&gt;
    &lt;span class="na"&gt;responsive_strategy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;grid&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;framework&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;tailwind&lt;/span&gt; &lt;span class="c1"&gt;# or bootstrap, or custom&lt;/span&gt;
        &lt;span class="na"&gt;ratios&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;landscape&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;16/9"&lt;/span&gt;
            &lt;span class="na"&gt;square&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;1/1"&lt;/span&gt;
            &lt;span class="na"&gt;hero&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;21/9"&lt;/span&gt;

    &lt;span class="c1"&gt;# 2. Resolvers (Where are your images?)&lt;/span&gt;
    &lt;span class="na"&gt;resolvers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;public_files&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;filesystem"&lt;/span&gt;
            &lt;span class="na"&gt;roots&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;%kernel.project_dir%/public'&lt;/span&gt; &lt;span class="pi"&gt;]&lt;/span&gt;
        &lt;span class="na"&gt;assets&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;asset_mapper"&lt;/span&gt;

    &lt;span class="c1"&gt;# 3. Transparent HTML Caching&lt;/span&gt;
    &lt;span class="na"&gt;image_cache_enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="na"&gt;image_cache_service&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cache.app"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  1. Responsive Strategy
&lt;/h4&gt;

&lt;p&gt;This is the brain of PGI. By telling the bundle you use &lt;strong&gt;Tailwind&lt;/strong&gt; or &lt;strong&gt;Bootstrap&lt;/strong&gt;, it automatically knows the container widths for every breakpoint. When you say &lt;code&gt;md:6&lt;/code&gt;, PGI looks up the &lt;code&gt;md&lt;/code&gt; container width, divides it by 2 (6/12 columns), and generates the exact image size needed.&lt;/p&gt;

&lt;h4&gt;
  
  
  2. Resolvers: File Freedom
&lt;/h4&gt;

&lt;p&gt;Whether you keep your images in the &lt;code&gt;public/&lt;/code&gt; folder or use the modern &lt;strong&gt;Symfony AssetMapper&lt;/strong&gt;, PGI can find them. You can even define a &lt;code&gt;chain&lt;/code&gt; resolver to look in multiple places. This is a lifesaver for projects transitioning to newer Symfony features.&lt;/p&gt;

&lt;h4&gt;
  
  
  3. Performance First: HTML Caching
&lt;/h4&gt;

&lt;p&gt;Generating Blurhash and reading metadata requires CPU power. PGI solves this with &lt;strong&gt;transparent HTML caching&lt;/strong&gt;. Once a component is rendered, the final HTML is stored in your cache. The next time someone visits the page, PGI serves the cached HTML instantly, skipping all PHP logic.&lt;/p&gt;

&lt;h4&gt;
  
  
  Point of Interest (PoI) Cropping
&lt;/h4&gt;

&lt;p&gt;One of the coolest features is "Smart Cropping." Instead of blindly cropping from the center, you can define a Point of Interest:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight twig"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;twig:pgi:Image&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"team.jpg"&lt;/span&gt; &lt;span class="na"&gt;pointInterest=&lt;/span&gt;&lt;span class="s"&gt;"75x25"&lt;/span&gt; &lt;span class="na"&gt;sizes=&lt;/span&gt;&lt;span class="s"&gt;"md:6@square"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This ensures that the most important part of the image (like a person's face) stays in the frame, regardless of whether it's being cropped to a square, portrait, or landscape ratio.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why PGI Belongs in Your Next Project
&lt;/h3&gt;

&lt;p&gt;PGI isn't just a wrapper; it's a complete ecosystem for Symfony:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Zero Configuration:&lt;/strong&gt; Install and it just works with Bootstrap or Tailwind.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Automatic Generation:&lt;/strong&gt; It generates all required sizes on the fly and caches them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;LiipImagine Integration:&lt;/strong&gt; It plays nice with existing tools if you need custom filters.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Transparent Caching:&lt;/strong&gt; It can cache the resulting HTML to avoid re-calculating metadata on every request.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you are building a modern Symfony application and care about SEO, UX, and your own sanity as a developer, PGI is the missing piece of the puzzle. It’s already powering high-performance sites like &lt;a href="https://just6words.com/" rel="noopener noreferrer"&gt;just6words.com&lt;/a&gt;, helping them achieve near-perfect PageSpeed scores.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ready to boost your PageSpeed?&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://github.com/tito10047/progressive-image-bundle" rel="noopener noreferrer"&gt;Check out PGI on GitHub&lt;/a&gt; and join the movement toward a faster, more stable web.&lt;/p&gt;

</description>
      <category>symfony</category>
      <category>php</category>
      <category>webdev</category>
      <category>images</category>
    </item>
  </channel>
</rss>
