jacobjoergensen/laravel-fmt
Laravel FMT is a formatting package for Laravel projects. It applies consistent, configurable code style across PHP and Blade files with a single command, keeping your codebase clean and readable, reducing review noise, and enforcing standards with minimal setup. Fast and lightweight.
laravel-fmt is a Prettier-style code formatter for Laravel codebases: PHP, Blade, and — since 0.3.0 — fenced code blocks inside Markdown (PHP, Blade, HTML, CSS, JSON, YAML, XML). Despite the package description talking about "rendering common values," what you actually get is a php artisan fmt CLI. Trust the changelog and the config keys over the blurb; the top of the funnel under-sells the tool.
1. Install pinned, as a dev dependency. The package ships its own beta warning. A floating constraint means a patch release can reformat your whole tree on someone else's composer update:
composer require --dev jacobjoergensen/laravel-fmt:0.3.0
Service provider and facade come in via auto-discovery — there is no manual registration step.
2. Publish and commit the config. Run php artisan vendor:publish and take the fmt config. Settings are namespaced per language (php.*, markdown.*, and siblings for Blade/HTML/CSS/JSON/YAML/XML). Committing it is the entire point: defaults are the thing most likely to shift between beta minors, and a committed config is what makes your laptop and CI agree.
3. Run the checker before the formatter — always.
php artisan fmt --check
On a mature app this prints a large number without touching a file. That number is your scope estimate, and it's the reason you run --check first.
First use case: pick one directory you own — resources/views/components or app/Http/Controllers — run --check, then a real run, then read git diff. Blade is usually the biggest and most defensible win, because almost nobody hand-formats Blade consistently, so the diff reads as obviously correct rather than as taste.
A Composer script instead of a habit. One boring entry point for the team:
"scripts": {
"fmt": "php artisan fmt",
"fmt:check": "php artisan fmt --check"
}
composer fmt before you commit; composer fmt:check as a non-blocking CI job. This is the correct starting shape: real signal, zero risk of a beta formatter failing a build.
CI with the GitLab reporter. The 0.3.0 headline for teams on GitLab is that --check --reporter gitlab writes a Code Quality report, so unformatted files annotate the MR diff instead of being an opaque red job with a log dump:
format:
script:
- php artisan fmt --check --reporter gitlab
artifacts:
when: always
reports:
codequality: gl-code-quality-report.json
Confirm the artifact filename on your first run rather than assuming it. Note --reporter is only meaningful alongside --check; in write mode it produces nothing useful. On GitHub Actions or Bitbucket this release gives you nothing — stay in report-only mode.
Scope runs to directories you're willing to churn. Point the formatter at paths you own; formatting a legacy or vendor-adjacent directory poisons git blame for months. If the command accepts a path argument in your version, use it; otherwise set excludes in the published config rather than reformatting and reverting.
Land the reformat as its own commit, and blame-ignore it. The "format everything" change should contain nothing but formatter output, then be added to .git-blame-ignore-revs. Reviewers skip it, and reverting it is one git revert.
Configure once, globally, for cross-language consistency. php.notOperatorSpace governs not just PHP but Blade echoes and Alpine attributes (open = ! open vs open = !open). Set it in the committed config so @php blocks, echoes, and Alpine directives all move together — never per-file. If your team hard-wraps docs, that's markdown.proseWrap: always.
Point it at README.md and docs/. Fenced blocks get formatted, and a block that fails to parse is kept exactly as written, so pseudo-code, // ... elisions and deliberately broken examples survive without a blocklist. That removes a recurring "this snippet no longer matches the API" review comment.
Sequence hook adoption deliberately: report-only CI → repo cleaned → pre-commit hook on staged paths → blocking CI gate as backstop. Skip straight to a hook and every commit fights you for a week until someone disables it.
Beta is the real gotcha. The package says it isn't production-ready. 0.3.0's changelog is almost entirely behaviour fixes — good sign, but also an explicit promise that 0.3.1 will move output again. Pin, upgrade deliberately, and keep reformat commits isolated.
Idempotency is the thing to verify on your code. The 0.3.0 notes read as a list of "run twice, get two diffs" bugs: method chains indented deeper after a multi-line operand, binary operators and destructuring changing layout on a second run, a lone @ in Blade text shifting surrounding layout, markdown.proseWrap: always re-wrapping lists and blockquotes. Non-idempotent formatters are disqualifying for format-on-save and pre-commit hooks, because they generate churn diffs that re-trigger hooks. Spend five minutes on a branch after every upgrade: run the formatter, run it again, assert git diff is empty.
Debug one file, twice. Don't run the whole tree when output looks wrong. Format the single file, then format the result. The first run shows the decision; the second tells you whether the decision is stable. A file plus its second-run diff is a far better upstream report than a screenshot.
Upgrades cause churn even where old output was "right." The comment-placement fixes (a comment after a docblock being forced onto its own line; the blank line before a trailing file comment being deleted) and the "{$a->f("x")}" interpolation fix will rewrite previously-formatted files. Keep the dependency bump and the reformat in separate commits, or reviewers can't tell 0.3.0's behaviour change from your change.
Diff Blade @php and <?php ?> blocks specifically after upgrading. Comment handling at the end of those blocks has changed more than once, including comments being deleted. A clean full-file run is not sufficient evidence; look at the files with inline comments inside Blade PHP blocks.
Some inputs now fail rather than format. A form feed or vertical tab between PHP tokens is rejected deliberately, because PHP itself rejects those bytes. If a generated or published file in your tree contains one, --check will flag it forever and the formatter can't fix it. Exclude that path — don't chase it.
Quote your Blade attribute values. <div x=<?= $a ?>> used to be cut at ?> and mangled into broken PHP. It's fixed, but the shape of the bug is the lesson: unquoted attribute values holding PHP tags are inherently ambiguous to a parser. Quoting costs nothing and removes a whole category of surprise.
Don't adopt this for PHP alone. Pint is mature, Laravel-official, and already scaffolded. The reason to take on a beta formatter is Blade + Markdown coverage and the single-config/single-CI-step story — PHP formatting is a solved problem you don't need to re-solve here.
How can I help you explore Laravel packages today?