olveneer/twig-components-bundle
Twig Components Bundle for Symfony adds a component layer to Twig, letting you build reusable, encapsulated UI pieces with their own templates, logic, and styles. Compose pages from components instead of loose includes, keeping markup organized and props explicit.
composer require olveneer/twig-components-bundle
config/bundles.php if Flex didn't do it for you (older bundles predate Flex recipes):
Olveneer\TwigComponentsBundle\TwigComponentsBundle::class => ['all' => true],
# config/packages/twig_components.yaml
twig_components:
components_dir: '%kernel.project_dir%/templates/components'
templates/components/button.html.twig:
<a href="{{ url|default('#') }}" class="btn btn-{{ variant|default('primary') }}">
{{ label }}
</a>
component() function that includes the matching file and merges the hash you pass in:
{{ component('button', { label: 'Save', variant: 'success' }) }}
Where to look first: the package README, then src/Twig/ (the extension/function registration) and src/DependencyInjection/Configuration.php to see the real option names and defaults. The first practical use case is almost always an Alert, Button, or Card — repeated markup that varies only by props.
templates/components/ flat and shallow, one component per file, named after its use (button.html.twig, alert.html.twig, modal.html.twig). Treat props as an implicit contract and document them with a Twig comment at the top of each file.default filters inside the component so callers only supply what differs. This keeps callers terse:
{{ component('alert', { type: 'danger', message: 'Something failed' }) }}
{% component 'card' %}...{% endcomponent %}) or a slot API on a returned object. Inspect the Component base class — the slot methods are the non-obvious part of the API..twig files — that's the whole point of the "modular" pitch.component() themselves. Build a layout/ set (header, footer, nav) and compose them in base.html.twig so page templates read like a manifest of parts.app.* globals — it makes components reusable in tests and in emails/PDFs rendered outside an HTTP request.composer require olveneer/twig-components-bundle:^x.y) and read its composer.json — it likely declares Symfony ^3|^4 constraints and will need --with-all-dependencies or a compat shim on Symfony 5/6.bin/console cache:clear. Twig caches compiled templates by name, and a renamed component can silently serve the old include or throw a "template not found" that looks like a config error.component('Button') vs button.html.twig is a case-sensitivity trap on Linux (works on macOS, breaks in CI). Pick lowercase names and stick to them.variant wrong here?" bugs. Prefer explicit props for anything that matters.{{ dump(_context) }} (Twig debug bundle) to see exactly which props arrived. Faster than re-reading the extension source.Component class (subclass it to add shared helpers like attr() or class()), the Twig extension itself (register extra functions), and the configured component paths (point at multiple directories to allow project-level overrides of vendor components — order determines precedence).symfony/ux-twig-component is the maintained, first-party equivalent. If you outgrow this bundle (or it goes stale against your Symfony version), the mental model — component file + props + slots — transfers directly, so the rewrite cost is mostly renames.How can I help you explore Laravel packages today?