tito10047 / ux-sdc
A performance-optimized SDC implementation for Symfony UX components with automatic discovery and static metadata registry.
Installs: 16
Dependents: 0
Suggesters: 0
Security: 0
Stars: 3
Watchers: 0
Forks: 1
Open Issues: 2
Type:symfony-bundle
pkg:composer/tito10047/ux-sdc
Requires
- php: >=8.1
- symfony/asset: ^6.4|^7.0|^8.0
- symfony/asset-mapper: ^6.4|^7.0|^8.0
- symfony/framework-bundle: ^6.4|^7.0|^8.0
- symfony/ux-twig-component: ^2.13
- symfony/web-link: ^6.4|^7.0|^8.0
Requires (Dev)
- friendsofphp/php-cs-fixer: ^3.92
- phpbench/phpbench: ^1.4
- phpstan/phpstan: ^2.0
- phpstan/phpstan-symfony: ^2.0
- phpunit/phpunit: ^10.5
- symfony/twig-bundle: ^6.4|^7.0|^8.0
This package is auto-updated.
Last update: 2026-01-30 13:57:23 UTC
README
A Symfony bundle that implements the Single Directory Component (SDC) methodology for Symfony UX. It bridges the gap between AssetMapper and Twig Components by providing a fully automated, convention-over-configuration workflow.
The Concept
This bundle is inspired by the architectural challenges discussed in "A Better Architecture for Your Symfony UX Twig Components" by Hugo Alliaume.
Instead of scattering your component files across src/, templates/, and assets/, this bundle allows you to keep everything in one place.
Quick Example
Just create a directory for your component. Everything else is handled automatically.
ux_sdc: ux_components_dir: '%kernel.project_dir%/src_component/Component' component_namespace: 'App\Component'
{
"autoload": {
"psr-4": {
"App\\": "src/",
"App\\Component\\": "src_component/"
}
}
}
src_component/
└── Component/
└── Alert/
├── Alert.php # Auto-registered logic
├── Alert.html.twig # Auto-mapped template
├── Alert.css # Auto-injected styles
└── alert_controller.js # Auto-mapped Stimulus controller
namespace App\Component\Alert; use Tito10047\UX\Sdc\Attribute\AsSdcComponent; use Tito10047\UX\Sdc\Twig\ComponentNamespaceInterface; use Tito10047\UX\Sdc\Twig\Stimulus; #[AsSdcComponent] // No need to define names, templates, or assets. It's all inferred! class Alert { use Stimulus; public string $type = 'info'; public string $message; }
In the Alert.html.twig template, you can then use the automatically generated stimulus controller name:
<div class="alert alert-{{ type }}" {{ stimulus_controller(controller) }}> {{ message }} </div>
Tip
Zero Configuration Magic: The bundle automatically registers the component, maps the template based on its location, and injects the required CSS/JS into your HTML header only when the component is rendered.
Key Features
- Automatic Registration: Every class marked with
#[AsSdcComponent]is automatically discovered and registered. - Smart Template Mapping: Forget
template: 'components/Alert.html.twig'. If the template is in the same folder as your class, it's found automatically. - Asset Orchestration: CSS and JS files in your component folder are collected during rendering and injected into the
<head>. - Automatic Stimulus Controllers: By using the
Stimulustrait andComponentNamespaceInterface, your component automatically gets acontrollervariable representing its Stimulus controller name based on its namespace. - No "Phantom" Controllers: Load component-specific CSS via AssetMapper without the need for empty Stimulus controllers just for imports.
- Performance First: * Compiler Pass: All file discovery happens at build time. Zero reflection in production.
- Response Post-processing: Assets are injected at the end of the request.
- HTTP Preload: Automatic generation of
Linkheaders to trigger early browser downloads.
Installation & Setup
- Install via Composer:
composer require tito10047/ux-sdc
- Register the bundle (if not done automatically by Symfony Flex):
// config/bundles.php return [ // ... Tito10047\UX\Sdc\UxSdcBundle::class => ['all' => true], ];
- Configure the bundle:
Create a configuration file (e.g.,
config/packages/sdc.yaml):
ux_sdc: # Directory where your SDC components are located (default: %kernel.project_dir%/src_component) ux_components_dir: '%kernel.project_dir%/src/Component' # PHP Namespace for your components (required for auto-registration) component_namespace: 'App\Component' # Enable/disable auto-discovery of .twig, .css, and .js files (default: true) auto_discovery: true # Custom placeholder for asset injection (optional) # placeholder: '<!-- __UX_TWIG_COMPONENT_ASSETS__ -->' # Stimulus integration (auto-registers component directory in Stimulus) stimulus: enabled: true
- Add the placeholder to your base template:
Place this in your
<head>to define where the collected assets should be injected:
<head> {# ... #} {{ render_component_assets() }} </head>
How It Works
- Discovery: During container compilation, the bundle scans your component directory. It maps PHP classes to their neighboring
.twig,.css, and.jsfiles. - Rendering: When a component is used on a page, the bundle's listener intercepts the
PreCreateForRenderEventand logs its required assets. - Injection: The
AssetResponseListenerreplaces your Twig placeholder with the actual<link>and<script>tags and adds HTTP preload headers to the response.
Why SDC?
- Maintainability: Everything related to a UI element is in one folder.
- Developer Experience: No more jumping between four different directories to change one button's color.
- Efficiency: Only the CSS/JS needed for the current page is sent to the user.
Benchmarks
This bundle is designed for high performance with minimal overhead. We've conducted extensive benchmarks comparing the SDC approach with the classic Twig component approach.
Performance Summary (500 Components)
| Scenario | Classic Approach | SDC Approach | Difference |
|---|---|---|---|
| Warmup (Dev/Debug) | 809.8ms | 782.0ms | -27.8ms |
| Warmup (Prod) | 583.1ms | 586.2ms | +3.1ms |
| Render (Prod Runtime) | 26.5ms | 31.6ms | +5.1ms |
| Render (Dev Runtime - 500 unique) | 26.5ms | 88.4ms | +61.9ms |
| Render (Dev Runtime - 10 unique repeated) | 26.5ms | 58.0ms | +31.5ms |
Key Findings
- Developer Experience (Dev Runtime): In
devmode, there is a measurable overhead for unique components (~84µs per component) due to runtime autodiscovery. This allows developers to add CSS/JS/Twig files and see changes instantly without clearing the cache. - Caching: Thanks to internal metadata caching, repeated rendering of the same component in
devis significantly faster as the file system is only scanned once per unique component class per request. - Production Performance: In
prodmode, the overhead for rendering 500 unique components is practically zero, as all metadata is pre-generated during container compilation. - Warmup: The SDC approach slightly increases container compilation time in
prod(~15ms for 500 unique components) but remains very efficient. - Memory Usage: The SDC approach requires approximately 8MB more memory during container compilation for 500 components, which is well within acceptable limits for modern applications.
For detailed results and methodology, see the Full Benchmark Report.
License
MIT