https://github.com/lemmon/twig-jsx
https://github.com/lemmon/twig-jsx
Last synced: 13 days ago
JSON representation
- Host: GitHub
- URL: https://github.com/lemmon/twig-jsx
- Owner: lemmon
- License: mit
- Created: 2026-04-16T16:44:59.000Z (3 months ago)
- Default Branch: main
- Last Pushed: 2026-04-30T14:57:29.000Z (2 months ago)
- Last Synced: 2026-04-30T16:23:20.840Z (2 months ago)
- Language: PHP
- Size: 8.79 KB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE
Awesome Lists containing this project
README
# Twig JSX
JSX-like component syntax for Twig.
[](LICENSE)
[](https://php.net)
[](https://twig.symfony.com)
[](https://github.com/lemmon/twig-jsx/actions/workflows/ci.yml)
Write `` instead of a verbose `{% include %}` call. Twig JSX transforms JSX-like component tags into native Twig `{% include %}` and `{% embed %}` calls at the lexer level — no runtime overhead, no Symfony dependency.
## Requirements
- PHP `^8.1`
- Twig `^3.0`
## Installation
```bash
composer require lemmon/twig-jsx
```
## Quick Start
### 1. Register the Extension and Lexer
```php
use Lemmon\TwigJsx\JSXPreLexer;
use Lemmon\TwigJsx\AttributeExtension;
$twig = new \Twig\Environment($loader);
$twig->addExtension(new AttributeExtension());
$twig->setLexer(new JSXPreLexer($twig));
```
### 2. Create a Component
Save your component in `templates/components/Alert.twig`:
```twig
{% set type = props.type|default('info') %}
{% set important = props.important|default(false) %}
{% set title = props.title|default(null) %}
{% set message = props.message|default('No message provided.') %}
{% if title %}{{ title }}{% endif %}
{% block content %}{{ message }}{% endblock %}
```
### 3. Use It in a Template
```twig
Wait! Something needs your attention.
```
## Prop Syntax
| Syntax | Example | Compiles to (inside `props` bag) |
| :--- | :--- | :--- |
| **Static** | `type="info"` | `'type': 'info'` |
| **Expression** | `type={userType}` | `'type': userType` |
| **Expression** | `count={items\|length}` | `'count': items\|length` |
| **Expression** | `theme={dark ? 'd' : 'l'}` | `'theme': dark ? 'd' : 'l'` |
| **Shorthand** | `{type}` | `'type': type` |
| **Boolean** | `important` | `'important': true` |
A quoted value is a **static string** — it is not interpolated. For a dynamic
value, use the expression form: `class={'alert-' ~ type}` rather than
`class="alert-{{ type }}"`. A quoted value containing a `{{ … }}` output tag is
rejected with a `SyntaxError` that points at the expression form, instead of
silently passing the literal text. (To pass a literal `{{ … }}` through, wrap it
in a quoted expression: `tpl={'{{ name }}'}`.) See
[docs/decisions/0001-no-interpolation-in-quoted-props.md](docs/decisions/0001-no-interpolation-in-quoted-props.md)
for the reasoning.
## How a Component Reads Its Inputs
Every prop the caller passes — semantic inputs and HTML attributes alike — arrives in a single
`props` bag of type `ComponentAttributes`. The component template decides what to extract:
```twig
{# Destructure semantic inputs as locals #}
{% set type = props.type|default('info') %}
{% set message = props.message|default('') %}
{# Spread the remaining keys as HTML attributes #}
{{ message }}
```
- `props.key` — read any value
- `props.except('a', 'b', ...)` — returns a new bag without the listed keys; useful for spreading HTML fallthrough attributes onto the root element
- `{{ props|spread }}` — renders all entries as HTML attribute pairs
## Configuration
| Option | Default | Description |
| :--- | :--- | :--- |
| `directory` | `components` | Subdirectory inside `templates/` where component files are looked up. |
| `extension` | `.twig` | File extension for component templates. |
| `prefix` | `""` | Tag prefix. When empty, any Capitalized tag is treated as a component (JSX-style). |
| `props_variable` | `props` | Name of the variable that holds all props in the component template. |
| `content_block` | `content` | Twig block name where a bodied tag's children are rendered. |
## Alternatives
- [Symfony UX Twig Component](https://symfony.com/bundles/ux-twig-component/current/index.html) — PHP class-backed components; requires the Symfony UX bundle.
- [TwigX](https://github.com/alma-oss/twigx-bundle) — similar `` syntax as a Symfony bundle; requires Symfony Config and DI.
## Contributing
Bug reports and pull requests are welcome on [GitHub](https://github.com/lemmon/twig-jsx).
## License
MIT. See [LICENSE](LICENSE).