Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Twig Cs Fixer Laravel Package

vincentlanglet/twig-cs-fixer

A PHP tool that lints and automatically fixes Twig templates against the official Twig coding standards. Install it via Composer or as a PHAR and run lint or fix commands on any directory, with support for custom and node-based rules.

View on GitHub
Deep Wiki
Context7

Getting Started

Install as a dev dependency (not as a global binary — the composer install is what autoloads Twig's node classes for custom rules and node-based rules):

composer require --dev vincentlanglet/twig-cs-fixer

Confirm Twig actually exists in your app. In a stock Laravel project there are no .twig files — Laravel ships Blade. This tool becomes relevant when you serve Twig through a bridge package (e.g. TwigBridge), when you keep a resources/views tree of Twig for a non-Laravel sub-app, or when you're writing mail/broadcast templates with Twig. If you only have *.blade.php, this package will do nothing for you; stop here.

Create .twig-cs-fixer.php at the project root and point the finder only at directories that really contain Twig:

<?php

use TwigCsFixer\Config\Config;
use TwigCsFixer\File\Finder;

$finder = (new Finder())
    ->in(__DIR__.'/resources/views')
    ->exclude(['vendor', 'cache'])
;

return (new Config())
    ->setFinder($finder)
    ->setCacheFile(__DIR__.'/var/twig-cs-fixer.cache')
;

Look at the docs in this order: docs/configuration.md (finder + config surface), then docs/rules.md (what the default standard actually enforces), then docs/ignoring-errors.md. Only read docs/custom.md when the built-in rules don't cover you.

First run — lint before you fix:

vendor/bin/twig-cs-fixer lint resources/views
vendor/bin/twig-cs-fixer fix resources/views

lint is read-only and tells you the blast radius. Run it on a clean git tree, and only then run fix and inspect git diff. A sensible first use case is exactly this: take an inherited or legacy Twig template directory, lint it to size the problem, fix it in one dedicated "style only" commit, and from then on only lint changed files.

Wire composer scripts immediately so the team uses the same invocation:

"scripts": {
    "twig:lint": "twig-cs-fixer lint resources/views",
    "twig:fix": "twig-cs-fixer fix resources/views"
}

Implementation Patterns

Scope the Finder aggressively. One Finder with ->in() per template root, plus ->exclude() for storage/framework/views, compiled template caches, vendor/, and any generated/scaffolded template output. Never lint a directory that a build step writes into.

Pick a standard deliberately. The package ships three: the default TwigCsFixer standard (upstream Twig rules plus extras such as compact hashes and quote normalisation), a plain Twig-only standard, and a Symfony-flavoured standard with the additional Symfony coding-standard rules. Most Laravel-side teams want the default; teams migrating Twig out of a Symfony app should start with the Symfony standard so the fixer agrees with their existing CI. Add rules/standards (and custom rules) through the Config object — config is plain PHP, so you can branch on an env var if you need a looser ruleset locally than in CI.

Adopt incrementally, in three steps:

  1. CI runs lint as a non-blocking job, just collecting output.
  2. fix runs on files touched by the branch (git diff --name-only --diff-filter=ACMR origin/main... | grep '\.twig$') and the result is committed.
  3. Flip the CI job to blocking and add the lint to the pre-merge checklist.

CI integration. Cache the configured cache file between runs using your CI's cache action keyed on the lockfile — on large template trees this is the difference between seconds and minutes. GitHub Actions is just a run: step on composer twig:lint. GitLab users should use the code-quality report format (added in 3.6.0) so violations render as inline MR annotations instead of raw console text.

Keep it next to Pint, not inside it. Laravel Pint only handles PHP; it will never touch .twig. Define one umbrella script (composer lint → pint --test + twig:lint) so nobody forgets the Twig half, and let each tool own its file extensions. Ordering between them is irrelevant.

Pre-commit / on-save. A git pre-commit hook that runs fix on staged .twig files (and re-stages them) is the highest-leverage integration — it stops style churn from ever reaching review. The same thing can be done as a PhpStorm File Watcher or a VS Code runOnSave task; just make sure it invokes the project-local vendor/bin/twig-cs-fixer so the version matches CI.

Suppress narrowly, per rule. Use the inline ignore directives from docs/ignoring-errors.md for one-off conflicts instead of excluding whole files — rule-scoped ignores leave the rest of the file protected, whereas a file exclusion silently opts that template out of every future rule.

Custom rules are the extension point. Write a class implementing the rule interface, operate on the token stream, register it in the config. This is where the require_once __DIR__.'/vendor/autoload.php'; line in the config file matters — needed when running the PHAR, and effectively required whenever you reference node classes or your own rule classes.

Gotchas and Tips

The snake_case naming rule is the one that will bite you. It renames variables and macro arguments. If your PHP layer passes camelCase data into Twig (very common in bridge setups), fix will "correct" userName to user_name and break rendering. Decide up front: either align your PHP-side keys, or disable the naming rule. Do not run fix across the whole tree before you've checked this.

Whitespace-control delimiters are a special case. {{- foo -}} must have no space between the hyphen and the delimiter; the delimiter-spacing rule is configured to respect that (and it deliberately avoids fixing {# -#}-style comments — a fix landed in 3.8.1 for exactly that). If you see the fixer and a developer fighting over one tag, it's almost always a whitespace-control misread.

Treat fix output as code review. The fixer rewrites files in place; there is no dry-run diff. Run it on a clean worktree, review git diff, and land it as a style-only commit. Consider adding these commits to a .git-blame-ignore-revs file so git blame doesn't drown.

Pin the major and stay on the latest patch. The 4.x line split the old monolithic operator-spacing rule into several individual rules and changed token type handling — 3.x configs and any custom rules touching operator tokens will need updating. Conversely, the tokenizer has a steady stream of parser bug fixes in patch releases (unary operators, spread operators inside hashes, Symfony 8.x compatibility), so pinning ^4.1 is safer than a hard 4.0.0. Read the upgrade guide before crossing a major.

PHP version floor. 8.0 support was dropped in 3.14. On an older Laravel/PHP combination, check the constraint before composer require, or fall back to the signed PHAR from the releases page.

Cache path quirks. The cache directory is created if missing (since 3.6.0), but the path must be writable and inside the workspace — point it at something gitignored like var/ or storage/framework/cache/, not at /tmp, or CI caching won't capture it.

Don't chain commands blindly on fix. lint exits non-zero when violations exist, which is exactly what you want in CI. fix can also exit non-zero when it changed files; in deploy scripts use ; or check the exit code explicitly rather than assuming && semantics, and never let a "fixer changed files" exit code abort a release.

Custom rules are cheap to write but expensive to debug. Dump the token stream at the point of your rule before writing logic — Twig's token types are not intuitive (and were tightened in 4.0 to string-only Token::type). Write the rule against the smallest failing template you can construct, then run lint (not fix) while iterating.

Extension points worth knowing: custom rules, custom standards (bundles of rules), the Finder (multiple roots), the cache file, and the report formats. There is no plugin system beyond those — if you need a different output format for your CI provider, a thin wrapper script around lint's exit code is usually simpler than a custom reporter.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource