aaronadal/twig-list-loop
Twig extension that adds a “list” tag to build reusable list/table/grid skeletons. Render items into a shared template via “using”, with access to loop plus list/else variables and optional inline if filters, similar to Twig’s for loop.
Installation:
composer require aaronadal/twig-list-loop
Register the Twig extension in your Laravel app’s config/app.php under providers:
Aaronadal\TwigListLoop\TwigListLoopServiceProvider::class,
First Use Case:
Create a reusable template (table.tpl.twig) in resources/views/partials/:
{# table.tpl.twig #}
<table class="table">
<tbody>
{% for item in list %}
{{ item | raw }}
{% endfor %}
</tbody>
</table>
Use it in a blade template:
{% list user in users using 'partials/table.tpl.twig' %}
<tr>
<td>{{ user.id }}</td>
<td>{{ user.name }}</td>
</tr>
{% endlist %}
Reusable Scaffolding:
table.tpl.twig, grid.tpl.twig) for consistent layouts.list tag, keeping markup logic in the skeleton.Dynamic Headers/Footers:
with args to inject metadata (e.g., headers, pagination links) into the skeleton:
{% set args = { headers: ['ID', 'Name'], footer: 'Total: {{ total }}' } %}
{% list item in items using 'skeleton.tpl.twig' with args %}
{# row content #}
{% endlist %}
Conditional Rendering:
else clause for empty states:
{% list item in items using 'skeleton.tpl.twig' %}
{# row #}
{% else %}
<p>No items found.</p>
{% endlist %}
Nested Loops:
for loops for hierarchical data (e.g., nested tables):
{# skeleton.tpl.twig #}
{% for category in list %}
<div class="category">
<h3>{{ category.name }}</h3>
<table>
{% for item in category.items %}
<tr>{{ item }}</tr>
{% endfor %}
</table>
</div>
{% endfor %}
@include if using Blade:
{% list item in items using '@include("partials.table")' %}
config/twig-list-loop.php) for easy updates:
'skeletons' => [
'default' => 'partials/table.tpl.twig',
],
Then reference via:
{% list item in items using config('twig-list-loop.skeletons.default') %}
Template Caching:
php artisan view:clear
using; prefer relative paths (e.g., partials/table.tpl.twig).Variable Scope:
list and else variables are only available inside the skeleton. Access them directly in the skeleton template:
{# skeleton.tpl.twig #}
{% if list|length > 5 %}
<p>Showing {{ list|length }} items.</p>
{% endif %}
Raw Output:
| raw sparingly in skeletons to avoid XSS risks. Sanitize dynamic content (e.g., {{ header|e('html') }}).Performance:
{{ dump(using_template) }} inside the list tag to verify the skeleton path is correct.else clause is handled in the skeleton (not just the list tag). The else variable is always populated but may be ignored.Custom Tags: Extend the package by creating a custom Twig extension:
// app/Providers/TwigServiceProvider.php
public function boot()
{
$this->twig->addExtension(new class extends \Twig\Extension\AbstractExtension {
public function getFunctions()
{
return [
new \Twig\TwigFunction('custom_list', [$this, 'renderCustomList']),
];
}
});
}
Then use in Twig:
{{ custom_list(items, 'custom-skeleton.tpl.twig') }}
Dynamic Arguments:
Override the with behavior by extending the ListLoopNode class (advanced):
// app/Extensions/CustomListLoopExtension.php
class CustomListLoopExtension extends \Aaronadal\TwigListLoop\ListLoopExtension {
public function getTokenParsers()
{
return [new CustomListLoopTokenParser()];
}
}
Laravel Blade Directives: Create a Blade directive to bridge the gap:
// app/Providers/BladeServiceProvider.php
Blade::directive('list', function ($expression) {
return "<?php echo \$__env->make('{$expression}', ['items' => $items])->render(); ?>";
});
Usage:
@list('partials.table', ['items' => $users])
How can I help you explore Laravel packages today?