PHPackages                             ttbooking/twig-component - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Templating &amp; Views](/categories/templating)
4. /
5. ttbooking/twig-component

ActiveLibrary[Templating &amp; Views](/categories/templating)

ttbooking/twig-component
========================

Class components and slots for Twig: {% component %} and {% slot %} tags, auto-discovery registry with a manifest cache. Framework-free core + optional Laravel integration.

0.5.0(2w ago)043MITPHPPHP ^8.3CI passing

Since Jul 5Pushed 2w agoCompare

[ Source](https://github.com/ttbooking/twig-component)[ Packagist](https://packagist.org/packages/ttbooking/twig-component)[ Docs](https://github.com/ttbooking/twig-component)[ RSS](/packages/ttbooking-twig-component/feed)WikiDiscussions main Synced 1w ago

READMEChangelog (8)Dependencies (21)Versions (9)Used By (0)

TTBooking Twig Component
========================

[](#ttbooking-twig-component)

[![Tests](https://camo.githubusercontent.com/5f76cc0722f63bdb8fd257cb05a92218eea36b4f2e897c2ae59045e65c737d43/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f616374696f6e732f776f726b666c6f772f7374617475732f7474626f6f6b696e672f747769672d636f6d706f6e656e742f74657374732e796d6c3f6272616e63683d6d61696e266c6162656c3d7465737473267374796c653d666c61742d737175617265)](https://github.com/ttbooking/twig-component/actions/workflows/tests.yml)[![Latest Stable Version](https://camo.githubusercontent.com/bf7b8f6e253c59533e8c82c5698ec863551f24d5720ca628f929ba87ebc8ca6e/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f7474626f6f6b696e672f747769672d636f6d706f6e656e742e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/ttbooking/twig-component)[![Total Downloads](https://camo.githubusercontent.com/d77668350e00acecab41529b4a52b131a9c56065aa91023e3360bb5fc62938d1/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f7474626f6f6b696e672f747769672d636f6d706f6e656e742e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/ttbooking/twig-component)[![PHP Version](https://camo.githubusercontent.com/52adc23217e18e80ce6ec04b731d506e3438e930e26b00e003701c6793d56cf5/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f7068702d762f7474626f6f6b696e672f747769672d636f6d706f6e656e742e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/ttbooking/twig-component)[![License](https://camo.githubusercontent.com/a1f676c303610621a89f59d855594582f90fafba6f08f19daf3d7d1cfcf5dc0e/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f6c2f7474626f6f6b696e672f747769672d636f6d706f6e656e742e7376673f7374796c653d666c61742d737175617265)](LICENSE)

Class components and slots for Twig: the `{% component %}` tag, the `component()` function, Vue 3 style `{% slot %}` slots and an auto-discovery component registry with a manifest cache.

The core is framework-neutral — it runs on bare Twig in any PHP application. The Laravel integration (DI through the container, rendering via `view()`, artisan commands) is an optional layer; integrating with another framework means implementing two small interfaces.

Features
--------

[](#features)

- **Components as classes.** Widgets (your own class with logic + DI) and — in Laravel — presentational components on [`spatie/laravel-data`](https://github.com/spatie/laravel-data)(props only).
- **Tag and function.** `{% component 'name' with {...} %}…{% endcomponent %}` and inline `component('name', {...})`.
- **Vue 3 style slots.** A default slot (the tag body) + named `{% slot 'name' %}` with a `` fallback in the component template. The slot body executes in the caller's scope.
- **Auto-discovery.** Components are found by namespace/path; the name is derived from the class by convention (`App\View\TwigComponents\UI\Box` → `ui:box`). The manifest is cached for production.
- **Integration points.** `ComponentFactory` (how to instantiate a component) and `TemplateRenderer` (how to render a template) — nothing else touches the framework.

Requirements
------------

[](#requirements)

- PHP ≥ 8.3
- Twig ≥ 3.21

Optional (Laravel integration): Laravel ^13, [`rcrowe/twigbridge`](https://github.com/rcrowe/TwigBridge) ^0.14, [`spatie/laravel-data`](https://github.com/spatie/laravel-data) ^4 for Data components.

Installation
------------

[](#installation)

```
composer require ttbooking/twig-component
```

### Without a framework (bare Twig)

[](#without-a-framework-bare-twig)

```
use TTBooking\TwigComponent\{ComponentExtension, ComponentRegistry, NativeComponentFactory, TwigTemplateRenderer};
use Twig\Environment;
use Twig\Loader\FilesystemLoader;

$registry = new ComponentRegistry(
    namespace: 'App\\View\\TwigComponents\\',
    componentsPath: __DIR__.'/src/View/TwigComponents',
    manifestPath: __DIR__.'/var/cache/twig-component.php',
);

$twig = new Environment(new FilesystemLoader(__DIR__.'/templates'));
$twig->addExtension(new ComponentExtension(
    $registry,
    new NativeComponentFactory($psr11Container), // container is optional: without it — props-only constructors
    new TwigTemplateRenderer($twig),             // also accepts a closure fn (): Environment
));
```

`NativeComponentFactory` instantiates widgets via `new` with named arguments; if any PSR-11 container is provided, constructor parameters without a default are autowired from it by type. Warm the manifest during deployment: `$registry->cache()`.

### Laravel

[](#laravel)

The ServiceProvider is registered via package discovery. Publish the config and enable the Twig extension in `config/twigbridge.php`:

```
php artisan vendor:publish --tag=twig-component-config
```

```
// config/twigbridge.php
'extensions' => [
    'enabled' => [
        // …
        TTBooking\TwigComponent\ComponentExtension::class,
    ],
],
```

The provider binds the Laravel implementations itself: widgets are built by the container (`app($class, $props)`), templates render through `view()`, Data components are available.

A component can also be rendered directly from PHP — handy in tests of application components; no Twig environment is needed for that:

```
$html = app(ComponentExtension::class)->renderComponent('ui:box', ['title' => 'Orders']);
```

### Another framework

[](#another-framework)

Implement `ComponentFactory` (instantiating a component from props using your DI) and, if needed, `TemplateRenderer` (when rendering must not go directly through Twig) — then assemble `ComponentExtension` as in the example above.

Configuration (Laravel)
-----------------------

[](#configuration-laravel)

`config/twig-component.php` — where to look for components and where to write the manifest:

```
return [
    'namespace' => 'App\\View\\TwigComponents\\',
    'path'      => app_path('View/TwigComponents'),
    'manifest'  => base_path('bootstrap/cache/twig-component.php'),
];
```

Quick start
-----------

[](#quick-start)

A widget component:

```
namespace App\View\TwigComponents\UI;

use TTBooking\TwigComponent\TwigComponent;

class Box implements TwigComponent
{
    public function __construct(
        public string $title = '',
        public bool $collapsible = false,
    ) {}

    public function template(): string
    {
        // a name the active renderer understands:
        // standalone — a Twig template path: return 'components/ui/box.html.twig';
        // Laravel — a view name (view()->name() enables IDE navigation):
        return view('components/ui/box')->name();
    }

    public function context(): array
    {
        return [];
    }
}
```

The `components/ui/box.html.twig` template (`this` is the component instance, `slots` are the passed slots):

```

        {% slot 'header' %}{{ this.title }}{% endslot %}

    {% slot %}{% endslot %}

```

Usage at the call site:

```
{% component 'ui:box' with { title: 'Orders' } %}
    The body content is the default slot.
    {% slot 'header' %}A custom heading instead of the fallback{% endslot %}
{% endcomponent %}
```

A presentational component (data only, **Laravel integration**) extends `Spatie\LaravelData\Data`; props are available in the template directly. Outside Laravel this kind is unavailable — a limitation of laravel-data itself; use widgets instead.

Manifest cache
--------------

[](#manifest-cache)

The component registry is cached into a flat manifest. In Laravel (wired into `optimize`):

```
php artisan twig-component:cache   # build (part of php artisan optimize)
php artisan twig-component:clear   # remove (part of optimize:clear)
```

Without a framework — `$registry->cache()` / `$registry->clearCache()` in your deploy script.

How slots work
--------------

[](#how-slots-work)

A component renders in a separate Twig pass, so the slot body is **captured into a string**and injected into the component template rather than being bound with native `{% embed %}`. Consequences: slots render eagerly, there are no scoped slots.

Rules (modeled on Vue 3):

- A **top-level** `{% slot 'name' %}` inside `{% component %}` passes a named slot (like ``); all remaining body content is the default slot `content`.
- The default slot can also be passed explicitly: `{% slot %}…{% endslot %}` without a name (like ``). Combining an explicit default slot with loose content is a template compilation error; passing the same slot twice is one too.
- A body of nothing but whitespace/newlines (formatting around `{% slot %}`) does not count as the default slot — the `{% slot %}` fallback in the component template is preserved.
- A `{% slot %}` deeper than the top level (e.g. inside `{% if %}`) is not slot passing but a "hole with a fallback" in the current template's scope. In a component template this lets you forward your slots into a nested component; on a regular page there is no `slots`and such a tag simply renders its fallback in place.
- The `this` and `slots` keys of the render context are reserved: a `context()` key (or a Data component prop — Data props become the context) with such a name raises an error instead of a silent override.

Tests
-----

[](#tests)

```
composer install
vendor/bin/phpunit                    # both suites
vendor/bin/phpunit --testsuite Core   # the core on bare Twig, no framework
vendor/bin/phpunit --testsuite Laravel # the integration layer on Orchestra Testbench
```

No database required.

Documentation
-------------

[](#documentation)

Detailed guides live in the [`docs/`](docs) folder (Russian translation: [`docs/ru/`](docs/ru)):

- [Getting started](docs/getting-started.md) — installation, standalone bootstrap, your first component.
- [Components](docs/components.md) — widgets, props and `this`, `context()`, DI, the `component()` function, the naming convention.
- [Slots](docs/slots.md) — default and named slots, passing rules, forwarding into a nested component.
- [Laravel](docs/laravel.md) — ServiceProvider, config, Data components, artisan commands, rendering in tests.
- [Recipes](docs/recipes.md) — ready-made examples: a box with a slot, a modal with named slots, a select with logic in `context()`.

License
-------

[](#license)

[MIT](LICENSE).

###  Health Score

41

—

FairBetter than 87% of packages

Maintenance97

Actively maintained with recent releases

Popularity10

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity44

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~4 days

Total

8

Last Release

17d ago

PHP version history (2 changes)0.1.0PHP ^8.2

0.3.4PHP ^8.3

### Community

Maintainers

![](https://www.gravatar.com/avatar/fa409df9a204429e6f7bb8a0f2baf6516891c903be4d176b463bc79453a1b555?d=identicon)[EgorGruzdev](/maintainers/EgorGruzdev)

---

Top Contributors

[![EgorGruzdev](https://avatars.githubusercontent.com/u/930669?v=4)](https://github.com/EgorGruzdev "EgorGruzdev (14 commits)")

---

Tags

laravelslotstwigtwig-componentstwig-extensionlaraveltwiguicomponentstwig-extensiontwig-componentsslots

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/ttbooking-twig-component/health.svg)

```
[![Health](https://phpackages.com/badges/ttbooking-twig-component/health.svg)](https://phpackages.com/packages/ttbooking-twig-component)
```

###  Alternatives

[laravel/framework

The Laravel Framework.

34.9k556.2M21.5k](/packages/laravel-framework)[symfony/symfony

The Symfony PHP framework

31.4k87.4M2.2k](/packages/symfony-symfony)[drupal/core-recommended

Locked core dependencies; require this project INSTEAD OF drupal/core.

6943.5M450](/packages/drupal-core-recommended)[sulu/sulu

Core framework that implements the functionality of the Sulu content management system

1.3k1.4M236](/packages/sulu-sulu)[symfony/ux-twig-component

Twig components for Symfony

22020.2M442](/packages/symfony-ux-twig-component)[contao/core-bundle

Contao Open Source CMS

1301.7M3.1k](/packages/contao-core-bundle)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
