Install the Package:
composer require jms/twig-js
Ensure Node.js/NPM is installed for testing/compilation.
Basic Compilation:
Create a Twig template (resources/views/example.twig):
<h1>{{ "Hello, " ~ name | upper }}</h1>
<ul>
{% for item in items %}
<li>{{ item | raw }}</li>
{% endfor %}
</ul>
Compile it to JavaScript using the CLI:
vendor/bin/twig-js compile resources/views/example.twig public/js/example.js
Include the generated JS in your frontend:
<script src="{{ asset('js/example.js') }}"></script>
<div id="twig-output"></div>
<script>
Twig.render('example', { name: 'World', items: ['Item 1', 'Item 2'] }, document.getElementById('twig-output'));
</script>
First Use Case: Replace a simple server-rendered partial with client-side rendering for dynamic content (e.g., a dashboard widget). Verify the output matches the PHP Twig version.
Template Development:
resources/views and compile them to JS for client-side use.jms/twig-js for interactive client-side components (e.g., modals, dynamic lists).{# resources/views/dashboard/widget.twig #}
<div class="widget">
<h2>{{ title | e('html_attr') }}</h2>
<div class="content">
{% for item in data %}
{{ item.value | raw }}
{% endfor %}
</div>
</div>
Integration with Laravel:
// app/Console/Commands/CompileTwigJs.php
use JMS\TwigJs\TwigJsCompiler;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
class CompileTwigJs extends Command
{
protected function execute(InputInterface $input, OutputInterface $output)
{
$compiler = new TwigJsCompiler();
$compiler->compile('resources/views/example.twig', 'public/js/example.js');
$output->writeln('Template compiled!');
}
}
Register it in app/Console/Kernel.php:
protected $commands = [
\App\Console\Commands\CompileTwigJs::class,
];
Run it via:
php artisan twig-js:compile
Build Automation:
// webpack.mix.js
const TwigJsPlugin = require('jms/twig-js/webpack-plugin');
mix.webpackConfig({
plugins: [
new TwigJsPlugin({
src: 'resources/views',
dest: 'public/js/twig',
}),
],
});
Run with:
npm run dev
Data Passing:
// In your Blade/Laravel view
<script>
window.twigData = @json(['name' => 'World', 'items' => ['Item 1', 'Item 2']]);
</script>
Use in compiled JS:
Twig.render('example', window.twigData, document.getElementById('twig-output'));
Dynamic Updates:
fetch('/api/data')
.then(response => response.json())
.then(data => {
Twig.render('widget', data, document.getElementById('widget-container'));
});
Leverage Laravel’s Service Container: Bind the compiler as a service for dependency injection:
// app/Providers/AppServiceProvider.php
use JMS\TwigJs\TwigJsCompiler;
public function register()
{
$this->app->singleton(TwigJsCompiler::class, function ($app) {
return new TwigJsCompiler();
});
}
Use it in controllers:
use JMS\TwigJs\TwigJsCompiler;
public function renderWidget()
{
$compiler = app(TwigJsCompiler::class);
$compiler->compile('widget.twig', 'public/js/widget.js');
return view('dashboard');
}
Cache Compiled Templates: Use Laravel’s cache to avoid recompiling templates on every request:
$cacheKey = 'twig_js_widget_' . md5('widget.twig');
if (!Cache::has($cacheKey)) {
$compiler->compile('widget.twig', 'public/js/widget.js');
Cache::put($cacheKey, true, now()->addDays(7));
}
Environment-Specific Compilation: Compile templates only in production or during builds:
if (app()->environment('production')) {
$compiler->compile('template.twig', 'public/js/template.js');
}
Unsupported Filters/Functions:
date, sort) will result in runtime errors or silent failures.{# Unsupported: #}
{{ item.date | date('Y-m-d') }}
{# Workaround: Pass formatted data from PHP #}
{{ item.formattedDate | raw }}
use Twig\Extension\AbstractExtension;
use Twig\TwigFunction;
class TwigJsExtension extends AbstractExtension
{
public function getFunctions()
{
return [
new TwigFunction('unsupported_filter', function ($var) {
throw new \RuntimeException('Unsupported filter: date. Use PHP to pre-format data.');
}),
];
}
}
Security Risks:
e filter for dynamic content:
{{ userInput | e('html') }}
$cleanData = array_map(function ($item) {
return htmlspecialchars($item, ENT_QUOTES, 'UTF-8');
}, $rawData);
raw filter unless absolutely necessary.Performance Overhead:
Cache-Control headers).Build Dependency:
Stale Project:
Template Inheritance:
extends, blocks) may not work as expected in the compiled JS output.import baseTemplate from './base.js';
Twig.extend(baseTemplate);
Enable Verbose Output: Run the compiler with verbose logging to diagnose issues:
vendor/bin/twig-js compile --verbose resources/views/template.twig public/js/template.js
Check Compiled JS: Inspect the generated JavaScript for errors or unexpected behavior:
How can I help you explore Laravel packages today?