Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Twig Components Laravel Package

redant/twig-components

Redant/Twig Components brings Laravel-style component architecture to Twig, making it easy to build reusable, self-contained UI components with slots, attributes, and view models. Keep templates organized and markup DRY across your PHP app.

View on GitHub
Deep Wiki
Context7

Getting Started

Laravel ships with Blade, not Twig, so the first decision is whether Twig is already in the stack. This package is a framework-agnostic Twig extension; in Laravel you wire it through rcrowe/twigbridge. If the project is Blade-only, this package is not a drop-in — budget for a Twig view layer or use it only for isolated fragments rendered from PHP.

Minimal setup:

composer require redant/twig-components
composer require rcrowe/twigbridge   # only if Twig is not already installed
php artisan vendor:publish --provider="TwigBridge\ServiceProvider"

Register the extension in config/twigbridge.php:

'extensions' => [
    'enabled' => [
        // ...
        RedAnt\TwigComponents\Twig\ComponentExtension::class,
    ],
],

For a plain Twig environment (scripts, tests, non-Laravel apps), add it directly:

$twig->addExtension(new RedAnt\TwigComponents\Twig\ComponentExtension(/* renderer deps */));

First component: create a class under app/Twig/Components, extend Component, point it at a template, and declare props as public typed properties with defaults.

namespace App\Twig\Components;

use RedAnt\TwigComponents\Component;

class Alert extends Component
{
    protected string $template = 'components/alert.html.twig';

    public string $type = 'info';
    public string $message = '';
    public bool $dismissible = false;
}

Template in resources/views/components/alert.html.twig:

<div class="alert alert-{{ type }}" role="alert">
    {{ message }}
    {% if dismissible %}<button type="button" class="close">&times;</button>{% endif %}
    {{ slot }}
</div>

Use it from a Twig view:

{% component 'alert' with { type: 'warning', message: 'Disk almost full' } %}{% endcomponent %}

First use case to target: a small design-system set — button, alert, card, badge — rendered from both Twig templates and PHP (mailables, PDFs, JSON fragments). That exercises props, slots, and the renderer early.

Where to look first in the repo: the Component base Class, ComponentRenderer, the Twig extension/token parser, and the README's exact tag/function syntax. Confirm names against the version you install; the last release is 2024-03-18 and the surface is small enough to read in one sitting.

Implementation Patterns

Treat components as typed view models. Public typed props are the contract. Give every prop a safe default (or make it nullable) and keep validation/normalization in the constructor or getter methods so templates stay declarative.

public function __construct()
{
    $this->type = in_array($this->type, ['info', 'warning', 'danger'], true)
        ? $this->type
        : 'info';
}

Variants over class explosion. One Button with variant, size, and disabled props beats PrimaryButton, LargeButton, etc. Reserve subclasses for structural differences (e.g. Modal vs Drawer).

Slots for composition. The tag body is the natural default slot; named slots handle headers/footers. Decide deliberately whether slot content is escaped or trusted markup, and document it on the component class. A card that accepts arbitrary HTML needs a different escaping policy than a button label.

Compose inside Twig. Let page templates assemble components, and let higher-level components render lower-level ones. Keep leaf components dumb (no DB/service calls). If a component needs data, pass it in as props from the controller or a view composer.

Render from PHP when Twig is not the whole app. Inject ComponentRenderer (or resolve it from Laravel's container) to render the same component for mailables, queued jobs, PDF generation, or API error fragments. This is the main day-to-day leverage in a Laravel codebase that is otherwise Blade/JSON.

$html = app(ComponentRenderer::class)->render('alert', [
    'type' => 'danger',
    'message' => 'Payment failed',
]);

Mirror namespaces and template paths. Keep app/Twig/Components aligned with resources/views/components; if the package resolves templates by convention, a mismatched class/file name is the most common failure. Override the template property explicitly when in doubt.

Test at the renderer boundary. Boot a Twig environment with the extension in a PHPUnit test, render each component with representative props, and assert on HTML. Snapshot tests catch accidental markup changes across a design system.

Keep a component gallery route. A single /dev/components route that renders every component with variants is cheap and pays for itself immediately — there is no Storybook-style tooling in this ecosystem.

Gotchas and Tips

  • TwigBridge is the real integration risk. Extension registration, view paths, and cache behavior all live in TwigBridge config, not in this package. Verify the extension appears in twig.extensions after boot; a silent config typo means the component tag is simply unknown.
  • Uninitialized typed props throw. A public string $message; with no default raises a PHP Error when the component is rendered without that prop. Default everything, or use nullable types and guard in the template.
  • Clear the view cache after adding components. Twig compiles templates, and tag/function registration changes are not always picked up in a warm cache. php artisan view:clear is the first debugging step when a new component "doesn't exist."
  • Slot escaping is the classic bug. Slot bodies often arrive as already-rendered strings; combining that with Twig autoescaping produces double-escaped markup or, if you reach for |raw too eagerly, XSS. Pick a policy per component and enforce it in review.
  • Prop names can collide with Twig internals. Avoid content, attributes, loop, and names of registered functions/filters unless you intend the shadowing.
  • Convention-based template resolution is brittle. Class name casing, subdirectories, and the .html.twig suffix must line up. Set $template explicitly for anything non-trivial.
  • Small ecosystem, no safety net. 38 stars, zero dependents, last release March 2024. Pin the version in composer.json, read the source before relying on undocumented behavior, and check PHP/Twig version constraints against your app. Do not assume Symfony-specific docs apply unchanged to TwigBridge.
  • Extension points are limited but sufficient. Subclass Component, swap or decorate ComponentRenderer, and add your own Twig functions via the extension. Prefer these over forking; the codebase is small enough that a fork will drift.
  • Debug with Twig's debug mode and dump(). Wrap renderer calls in try/catch during development so missing-prop errors surface with the component name rather than a generic Twig runtime error.
  • Don't over-componentize. Without preview tooling or linting, a 60-component library becomes unmaintainable. Start with the 5–10 components that actually repeat, and revisit once the gallery route exists.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource
noith/settings