PHPackages                             tacman/twig-tree-tag - 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. tacman/twig-tree-tag

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

tacman/twig-tree-tag
====================

A Twig extension for succinctly traversing nested lists (e.g. navigation menus), supporting Twig 3

3.2.0(2mo ago)03.5k↓35.7%1MITPHPPHP ^8.1

Since Feb 15Pushed 2mo agoCompare

[ Source](https://github.com/tacman/twig-tree-tag)[ Packagist](https://packagist.org/packages/tacman/twig-tree-tag)[ Docs](https://github.com/tacman/twig-tree-tag)[ RSS](/packages/tacman-twig-tree-tag/feed)WikiDiscussions master Synced 1mo ago

READMEChangelog (10)Dependencies (3)Versions (16)Used By (1)

twig-tree-tag
=============

[](#twig-tree-tag)

A Twig extension for succinctly traversing nested lists (e.g. navigation menus). Based on , adapted for PHP 8 and Twig 3 by Tac Tacelosky.

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

[](#requirements)

Requires PHP 8.1 or higher

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

[](#installation)

```
composer require tacman/twig-tree-tag
```

Now register it in services.yaml

```
# services.yaml
services:
    twig.tree:
      class: JordanLev\TwigTreeTag\Twig\Extension\TreeExtension
      tags:
        - { name: twig.extension }
```

Idea
----

[](#idea)

The `{% tree %}` tag works almost like `{% for %}`, but inside a `{% tree %}` you can call `{% subtree var %}` to recursively run your `{% tree %}` block with the given `var`. The primary use-case for this tag is nested navigation menus.

This extension was written by [Alain Tiemblo](https://github.com/ninsuo), (with a few very minor changes by [Jordan Lev](https://github.com/jordanlev)).

Usage Example
-------------

[](#usage-example)

In this example, `menu` is an array of objects, each containing `name`, `url`, and `children` properties (`children` is itself an array of objects with the same properties, etc).

```
{% tree item in menu %}
  {% if treeloop.first %}{% endif %}

        {{ item.name }}
        {% subtree item.children %}

  {% if treeloop.last %}{% endif %}
{% endtree %}
```

Just like a `{% for %}` loop, you can access the key of each list item:

```
{% tree key, item in menu %}

    Item {{ key }}: {{ item.name }}
    {% subtree item.children %}

{% endtree %}
```

See the [demo directory](demo/) for more examples

What is the `treeloop` var?
---------------------------

[](#what-is-the-treeloop-var)

The `treeloop` var serves the same purpose inside a `{% tree %}` tag as the `loop` var does inside a `{% for %}` tag. It is named differently so that you can still use `loop` when you have a `{% for %}` tag inside your `{% tree %}` tag (otherwise they would conflict).

`treeloop` contains all the same [special variables as `loop`](http://twig.sensiolabs.org/doc/2.x/tags/for.html#the-loop-variable):

- `treeloop.index`: The current iteration of the loop *within the current nesting level*. (1 indexed)
- `treeloop.index0`: The current iteration of the loop *within the current nesting level*. (0 indexed)
- `treeloop.revindex`: The number of iterations from the end of the loop *within the current nesting level* (1 indexed)
- `treeloop.revindex0`: The number of iterations from the end of the loop *within the current nesting level* (0 indexed)
- `treeloop.first`: True if first iteration *of the current nesting level*
- `treeloop.last`: True if last iteration *of the current nesting level*
- `treeloop.length`: The number of items in the sequence *of the current nesting level*
- `treeloop.parent`: The context of the parent nesting level (or the parent context of the `tree` tag itself if currently at the root level of the tree).

Additionally, `treeloop` also contains 2 extra variables that tell you about the current nesting level:

- `level`: The current nesting level (1 indexed -- so root level of the tree is 1, 2nd-level is 2, etc)
- `level0`: The current nesting level (0 indexed -- so root level of the tree is 0, 2nd level is 1, etc)

What if I want a tree tag inside another tree tag?
--------------------------------------------------

[](#what-if-i-want-a-tree-tag-inside-another-tree-tag)

To handle the edge case where you want to start a new tree inside another tree (that is, a new tree "root" with its own markup), use `as` in your `{% tree %}` tag to assign each tree to a var name, then pass it into `subtree` via `with`. This allows Twig to know which `{% tree %}` should be called when it comes across the `{% subtree %}` tag. For example...

```
{% tree item in menu as treeA %}
  {% if treeloop.first %}{% endif %}

        {{ item.name }}
        {% subtree item.children with treeA %}

        Some other tree (that has its own "root", not a sub-tree of treeA):
        {% tree otherthing in item.otherthings as treeB %}
          {{ otherthings.name }}
          {% subtree otherthings.subitems with treeB %}
          {# We use "with treeB" above so Twig knows which parent tree tag to call #}
        {% endtree %}

  {% if treeloop.last %}{% endif %}
{% endtree %}
```

License
-------

[](#license)

The MIT License (MIT)

Please read the [LICENSE](LICENSE) file for more details.

###  Health Score

53

—

FairBetter than 97% of packages

Maintenance83

Actively maintained with recent releases

Popularity22

Limited adoption so far

Community13

Small or concentrated contributor base

Maturity80

Battle-tested with a long release history

 Bus Factor1

Top contributor holds 58.5% 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 ~253 days

Recently: every ~146 days

Total

14

Last Release

85d ago

Major Versions

v1.0.0 → v3.0.02022-07-25

v2.0.0.x-dev → v3.0.12023-04-20

PHP version history (2 changes)v2.0.0.x-devPHP &gt;= 8.1

3.1.6PHP ^8.1

### Community

Maintainers

![](https://www.gravatar.com/avatar/21b39551f92ed4143772c622f9e571589c5a72c96ab3c53fe67489ce0d83e806?d=identicon)[tacman1123](/maintainers/tacman1123)

---

Top Contributors

[![tacman](https://avatars.githubusercontent.com/u/619585?v=4)](https://github.com/tacman "tacman (24 commits)")[![ninsuo](https://avatars.githubusercontent.com/u/4152132?v=4)](https://github.com/ninsuo "ninsuo (16 commits)")[![jordanlev](https://avatars.githubusercontent.com/u/149992?v=4)](https://github.com/jordanlev "jordanlev (1 commits)")

###  Code Quality

Static AnalysisPHPStan, Rector

Type Coverage Yes

### Embed Badge

![Health badge](/badges/tacman-twig-tree-tag/health.svg)

```
[![Health](https://phpackages.com/badges/tacman-twig-tree-tag/health.svg)](https://phpackages.com/packages/tacman-twig-tree-tag)
```

###  Alternatives

[twig/extra-bundle

A Symfony bundle for extra Twig extensions

91492.0M315](/packages/twig-extra-bundle)[twig/intl-extra

A Twig extension for Intl

36763.2M221](/packages/twig-intl-extra)[rcrowe/twigbridge

Adds the power of Twig to Laravel

9105.9M50](/packages/rcrowe-twigbridge)[twig/string-extra

A Twig extension for Symfony String

22046.0M133](/packages/twig-string-extra)[twig/cssinliner-extra

A Twig extension to allow inlining CSS

22918.5M55](/packages/twig-cssinliner-extra)[symfony/ux-twig-component

Twig components for Symfony

21914.8M162](/packages/symfony-ux-twig-component)

PHPackages © 2026

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