PHPackages                             treztreiz/twig-components - 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. treztreiz/twig-components

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

treztreiz/twig-components
=========================

1.4.0(3w ago)020MITPHPPHP ^8.3

Since May 22Pushed 3w agoCompare

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

READMEChangelogDependencies (8)Versions (10)Used By (0)

Twig Components
===============

[](#twig-components)

A lightweight, framework-free Twig component syntax layer.

Write components as HTML-like tags in your Twig templates — no Symfony, no bundle, no framework dependency. The library rewrites `` and `…` into native Twig before the engine tokenizes the template, so it slots into any Twig 3.x project.

```

    Default slot content

        OK

```

---

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

[](#requirements)

- PHP 8.3+
- Twig 3.x

No framework required.

---

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

[](#installation)

```
composer require treztreiz/twig-components
```

---

Wiring
------

[](#wiring)

```
use Twig\Environment;
use Twig\Loader\FilesystemLoader;
use TwigComponents\ComponentConfig;
use TwigComponents\ComponentRenderer;
use TwigComponents\ComponentExtension;
use TwigComponents\PreLexer;
use TwigComponents\PreLexerLoader;

$config = new ComponentConfig(
    templateExtension: '.html.twig',
    loaderNamespace: 'components',        // must match the path alias below
);

$inner = new FilesystemLoader([__DIR__ . '/templates']);
$inner->addPath(__DIR__ . '/components', 'components');

$loader = new PreLexerLoader($inner, new PreLexer($config));

$twig = new Environment($loader, [
    'cache'       => __DIR__ . '/var/cache/twig',  // optional; safe with PreLexerLoader
    'auto_reload' => true,                         // recommended in dev; omit in production
]);

$renderer = new ComponentRenderer($config);
ComponentExtension::register($twig, $renderer);
```

**Filesystem layout:**

```
src/
  components/
    Alert.html.twig
    Card.html.twig
  templates/
    page.html.twig

```

Component names are resolved as `PascalCase`: `` → `MyCard.html.twig`. Use colons as directory separators: `` → `components/Ui/Alert.html.twig`.

> **Cache note:** Twig only rechecks template freshness when `auto_reload` is enabled. Without it, editing a component template won't take effect until the compiled cache is cleared. Enable `auto_reload` in development; in production, clear `cache/` on deploy.

---

Syntax
------

[](#syntax)

### Self-closing (no children)

[](#self-closing-no-children)

```

```

Props become variables inside the component template. All props are also available as an `attrs` bag (see [Attribute passthrough](#attribute-passthrough)).

### Non-self-closing (with children / slots)

[](#non-self-closing-with-children--slots)

```

    Default slot content

```

The children become the `content` block. The component template uses `{% embed %}` under the hood, so standard Twig block semantics apply.

### Named slots

[](#named-slots)

```

    Sure you want to delete this?

        Cancel
        Confirm

```

Children before the first named slot become the `content` block automatically.

### Dynamic props

[](#dynamic-props)

Prefix a prop with `:` to pass a Twig expression instead of a string literal:

```

```

### Boolean props

[](#boolean-props)

Bare attribute names map to `true`:

```

```

### Subdirectory components

[](#subdirectory-components)

Use colons to reference components nested in subdirectories:

```

```

`` → `components/Ui/Alert.html.twig`. Kebab segments work too: `` → `components/MyUi/FormInput.html.twig`.

### Twig interpolation in static values

[](#twig-interpolation-in-static-values)

```

```

### Parent context access

[](#parent-context-access)

Components are isolated by default — they only see the props you pass. Add the bare `context` flag to opt a component into reading its **parent's** scope through a single `context` bag:

```

…
```

```
{{-- components/Sidebar.html.twig --}}
Welcome back, {{ context.currentUser.name }}
```

- The bag is **flat and one level deep** — even through nested opted-in components you always reach `context.foo`, never `context.context.foo`. Values from a closer ancestor win over a more distant one with the same name.
- `context` is a **reserved bare flag**. It takes no value: `context="x"`, `:context` and `:context="x"` all throw a `SyntaxError`. A component therefore cannot receive a prop literally named `context`.
- The flag only exposes the parent scope; it never leaks into `{{ attrs }}`.

---

Props
-----

[](#props)

Use `{% props %}` inside a component template to declare which variables are component-specific props. Declared props are extracted as named variables and **automatically stripped from `attrs`**, leaving only the remaining HTML attributes for passthrough.

```
{{-- components/Button.html.twig --}}
{% props label, variant = 'primary' %}

{{ label }}
```

```

{{-- renders: Submit --}}
```

- Props with a default value (`variant = 'primary'`) are optional.
- Props without a default are required — a missing required prop throws a `RuntimeError` at render time.
- `attrs` will only contain the non-prop attributes (`class`, `id`, `data-*`, etc.).

---

Attribute passthrough
---------------------

[](#attribute-passthrough)

Every component receives an `attrs` variable — a `ComponentAttributes` instance built from all the props passed at the call site.

```
{{-- components/Input.html.twig --}}

```

```

{{-- renders:  --}}
```

`attrs` is HTML-safe; use it without `|raw`.

**Filtering:**

```

```

MethodReturns`attrs.only('a', 'b')`new instance with only those keys`attrs.without('a', 'b')`new instance without those keys`attrs.has('class')`bool`attrs.get('class', 'default')`scalar or null`attrs.all()`raw array---

Defining block structure in component templates
-----------------------------------------------

[](#defining-block-structure-in-component-templates)

Use `` inside component templates as a shorthand for `{% block %}`:

```
{{-- components/Card.html.twig --}}

```

Standard `{% block %}` syntax still works and is unchanged.

---

Known limitations
-----------------

[](#known-limitations)

- **Same-quote in dynamic prop values**: a dynamic prop value (`:prop="..."`) cannot contain a double quote internally. Use a Twig variable instead.
- **No PHP class per component**: logic must live in the template or be passed as props. If you need computed properties or service injection, Symfony TwigComponent is a better fit (see below).
- **No LiveComponent / reactivity**: this library is render-only, server-side, no JavaScript binding.

---

Comparison with Symfony TwigComponent
-------------------------------------

[](#comparison-with-symfony-twigcomponent)

Featuretreztreiz/twig-componentsSymfony Twig ComponentsFramework requirementNoneSymfony + UX bundlePHP class per componentNoYes (optional with anonymous components)Computed properties / service injectionNoYes, via `mount()` and DITemplate-only (anonymous) componentsYes (always)Yes (opt-in)`` syntaxYesYesNamed slots / blocksYesYesAttribute passthrough (`attrs`)YesYesDynamic props (`:prop="expr"`)YesYesBoolean propsYesYes`{% props %}` tagYesYesSubdirectory components (`ui:card`)YesYesLiveComponent (reactive, JS binding)NoYes (separate package)Stimulus / Turbo integrationNoYesWorks without Composer autoloading magicYesNoCache-safe (compiled template invalidation)YesYes**Choose `treztreiz/twig-components` if:**

- You are outside of Symfony (plain PHP, other frameworks, static generators).
- Your components are purely presentational — no logic, no services.
- You want the HTML-like authoring experience without pulling in the full UX stack.

**Choose Symfony TwigComponent if:**

- You are in a Symfony application.
- Components need PHP-side logic, computed data, or injected services.
- You need LiveComponent for reactive UI without writing JavaScript.

---

License
-------

[](#license)

MIT

###  Health Score

43

—

FairBetter than 89% of packages

Maintenance95

Actively maintained with recent releases

Popularity8

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity54

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 ~5 days

Total

9

Last Release

25d ago

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/22977073?v=4)[Mathias H.](/maintainers/treztreiz)[@treztreiz](https://github.com/treztreiz)

---

Top Contributors

[![treztreiz](https://avatars.githubusercontent.com/u/22977073?v=4)](https://github.com/treztreiz "treztreiz (25 commits)")

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Code StylePHP CS Fixer

Type Coverage Yes

### Embed Badge

![Health badge](/badges/treztreiz-twig-components/health.svg)

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

###  Alternatives

[craftcms/cms

Craft CMS

3.6k3.6M3.2k](/packages/craftcms-cms)

PHPackages © 2026

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