Frontend development in Symfony has taken a huge leap forward in recent years. Thanks to Symfony UX and Twig Components, 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: files are scattered across the entire project.
A PHP class in src/Components, a Twig template in templates/components, CSS in assets/styles, 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.
Some time ago, Hugo Alliaume published a great article A Better Architecture for Your Symfony UX Twig Components, where he proposed the concept of Single Directory Components (SDC). The idea is simple: let's have everything in one folder.
Today, we'll show you how to take this concept to the next level, completely get rid of manual configuration, and maybe even Tailwind.
Why Single Directory Components (SDC)?
If you've ever worked with Angular or Vue, you know that feeling of order. An Alert component is a single folder containing everything essential.
In the SDC approach, it looks like this:
src_component/
└── UI/
└── Button/
├── Button.php # Logic
├── Button.html.twig # Template
├── Button.css # Styles
└── Button_controller.js # Interactions (Stimulus)
Main Advantages:
- Maintainability: When you no longer need a component, you just delete one folder.
- Developer Experience: No context switching between distant folders.
- Isolation: Styles and scripts belong only to that one component.
Is Tailwind Passé? The Power of Native CSS and Variables
Lately, there's more and more talk about how, with the arrival of modern CSS features (like CSS variables, nesting, @layer), the need for utility frameworks like Tailwind is decreasing. Articles like The Power of CSS Variables remind us that pure CSS is stronger today than ever before.
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.
Our Solution: UX SDC Bundle
Hugo's article showed how to set up SDC manually. However, it requires changes to composer.json, Twig configuration, AssetMapper, and Stimulus.
Our bundle tito10047/ux-sdc does it for you. It's a Zero Configuration solution. Just install the bundle and mark the class with an attribute.
#[AsSdcComponent('UI:Button')]
class Button {
// The bundle automatically finds Button.html.twig, Button.css, and Button_controller.js
}
The "Magic" Inside (Without Performance Loss)
The bundle solves technical challenges that you would have to patch yourself with a manual approach:
- Asset Orchestration: CSS and JS files are injected into the page only when the component is actually rendered.
- No More "Phantom" Controllers: You don't have to create an empty Stimulus controller just so AssetMapper knows about your CSS. The bundle handles it for you.
-
HTTP Preload & FOUC: We automatically generate
Linkheaders for HTTP preload. The browser starts downloading CSS even before it starts parsing HTML. This eliminates the annoying flash of unstyled content (FOUC). - Production Performance: Thanks to a compiler pass, no disk scanning happens in production. Everything is pre-prepared in the cache.
Real-world Example: Project "Formalitka"
What does it look like in the real world? Take a look at the project formalitka.mostka.sk. The entire UI kit is built on SDC without a single line of Tailwind.
Let's take their Button component. The PHP class defines properties like color or size. The CSS then processes these properties using variables:
/* Button.css */
@layer components {
.button {
background: var(--color-primary);
color: var(--color-on-primary);
transition: var(--transition);
&.button--secondary {
background: var(--color-secondary);
}
}
}
Thanks to SDC, this complex component (containing seal animations, sounds, and special effects) is contained within one directory and is easy to maintain.
How to Get Started?
Installation is lightning fast:
composer require tito10047/ux-sdc
Register the bundle and in config/packages/ux_sdc.yaml, set where your components reside:
ux_sdc:
ux_components_dir: '%kernel.project_dir%/src_component'
component_namespace: 'App\Component'
And add the asset placeholder to your base.html.twig:
<head>
{{ render_component_assets() }}
</head>
That's it. From this moment on, just create directories and write code.
Performance Under the Microscope: Proof Instead of Promises
When automation and "magic" come into play, the question often arises: What about performance? We put the SDC approach to a stress test with 500 unique components. Here are the key findings from our benchmarks:
| Scenario | Classic Approach | SDC Approach | Difference |
|---|---|---|---|
| Warmup (Prod) | 583.1 ms | 586.2 ms | +3.1 ms |
| Render (Prod Runtime) | 26.5 ms | 31.6 ms | +5.1 ms |
| Render (Dev Runtime) | 26.5 ms | 88.4 ms | +61.9 ms |
What does this mean in practice?
- In production, the overhead is almost zero. Thanks to the compiler pass, no disk scanning happens at runtime. Rendering a single component has an overhead of only about 8.8µs, a negligible price for fully automated asset and Stimulus management.
- Dev mode is optimized for DX. Instead of having to clear the cache every time a file changes, the bundle in dev mode uses runtime autodiscovery. 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.
- Memory Footprint: With 500 components, the bundle consumes about 8MB more memory during container compilation, which is perfectly fine for modern applications.
You can find the full report in our benchmark.md.
The Future and Your Feedback (Version 0.0.1)
This bundle is currently in version 0.0.1. 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.
We're interested in:
- What do you think about the proposed API?
- Is there anything missing that you would expect in an SDC approach?
- Do you see any pitfalls in this approach that we've overlooked?
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.
Conclusion
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.
Try the UX SDC Bundle and say goodbye to scattered files. The future of Symfony UX is in order and clarity.
Top comments (0)