In Laravel Pint's pint.json, how do you pick a preset, override individual rules and exclude files from formatting?
answer
- one JSON file in the project root
- laravel, per, psr12, symfony, empty
- a rules entry replaces, never merges
- false switches a preset rule off
- exclude, notName, notPath
basics
~20 sA pint.json in the project root sets "preset" (laravel, per, psr12, symfony or empty), a "rules" object whose entries replace or disable the preset's rules, and "exclude", "notName" or "notPath" to skip folders, name patterns or exact files.
solid answer
~40 s`pint.json` in the project root is Pint's configuration file. `"preset"` picks the base rule list: `laravel` (the default), `per`, `psr12`, `symfony` or `empty`, the last being a blank slate. `"rules"` takes PHP CS Fixer rule names: `true` enables a rule with its own default options, `false` switches it off, and an object configures it. Each entry replaces the preset's entry for that rule wholesale, so restate every option you want to keep. Files are skipped with `"exclude"` (folders), `"notName"` (file-name patterns) and `"notPath"` (paths), on top of built-in skips such as `vendor` and `storage`. On the command line, `--preset` beats the file's preset, `--config` points at another file, local or `https://`, and `--no-config` ignores it.
code
json · 10 lines{
"extend": "../shared/pint.json",
"preset": "laravel",
"rules": {
"blank_line_before_statement": {
"statements": ["continue", "return", "throw"]
}
},
"notPath": ["routes/console.php"]
}go deeper
Recall that pint.json lives in the project root, that "preset" picks one of five presets, and that rules can be switched on or off by name.
Explain the shallow merge: a rules entry replaces the preset's entry wholesale, true means the rule's own defaults, and exclude, notName and notPath each match something different.
Show how you would share one style across repositories with --config or extend, keep overrides few and documented, and verify with --no-config what the bare preset does.
Weigh the cost of every override: each rule you change moves the codebase away from the style the wider Laravel ecosystem reads, so overrides need an owner and a reason.
## What pint.json is for Laravel Pint runs with no configuration, applying its `laravel` preset to every `.php` file it finds. When a team wants something different, it commits a **`pint.json`** file in the project root. The file holds four kinds of setting: - **`preset`**: which built-in rule list to start from; - **`rules`**: per-rule overrides on top of that list; - **finder keys** (`exclude`, `notName`, `notPath`, `in`): which files are inspected; - **`extend`**: another JSON file to inherit settings from. Pint reads the file itself; it does not look for a `.php-cs-fixer.dist.php` file, even though the rules underneath are PHP CS Fixer's. ## Choosing a preset A **preset** is a named, pre-built list of rules. Pint 1.32 ships five: | Preset | What it starts from | |---|---| | `laravel` | Pint's own list, the style Laravel itself is written in (the default) | | `per` | PHP CS Fixer's `@PER-CS` set plus `no_unused_imports` | | `psr12` | PHP CS Fixer's `@PSR12` set plus `no_unused_imports` | | `symfony` | PHP CS Fixer's `@Symfony` set plus `no_unused_imports` | | `empty` | no rules at all | Any other name makes Pint stop with `Preset not found.` The preset can also be given on the command line: `./vendor/bin/pint --preset psr12`. When both are present, **the command-line flag wins** over the file. ## Overriding rules The `rules` object maps a PHP CS Fixer rule name to a value: 1. `true` turns the rule on with that rule's **own default options**. 2. `false` turns off a rule the preset enabled. 3. An object turns the rule on with **exactly those options**. The detail that trips people up is how the two lists combine. Pint merges them **shallowly**, key by key: for a rule named in both places, the `pint.json` entry **replaces** the preset's entry completely. Options are not merged. The `laravel` preset, for example, configures `blank_line_before_statement` with a list of statements (`continue` and `return`). Writing `"blank_line_before_statement": true` does not keep that list; it runs the rule with PHP CS Fixer's defaults. To change one option, copy the whole options object and edit it. ```json { "preset": "laravel", "rules": { "concat_space": { "spacing": "one" }, "not_operator_with_successor_space": false, "simplified_null_return": true }, "exclude": ["app/Legacy"], "notName": ["*.stub.php"] } ``` Here the `laravel` preset's `concat_space` setting (`none`, so `'a'.'b'`) becomes `one` (`'a' . 'b'`), Pint stops enforcing the space after `!` that Laravel style adds, and `simplified_null_return`, which the preset explicitly switches off, is turned on. Two more points: - Pint allows PHP CS Fixer's *risky* rules without an extra flag, so a risky rule listed in `rules` simply runs. - Pint adds its own rules prefixed `Pint/`, such as `Pint/laravel_blade` for Blade templates; they are off until listed here. The `empty` preset exists for teams that want full control: nothing runs except what `rules` lists. ## Excluding files Before `pint.json` is read, Pint already skips `vendor`, `storage`, `node_modules`, `bootstrap/cache`, `build`, dot files, IDE helper files and, unless the Blade rule is on, `*.blade.php`. The file adds to that: - **`exclude`**: directories to skip, such as `"app/Legacy"`; - **`notName`**: file-name patterns, such as `"*-generated.php"`; - **`notPath`**: a specific path, such as `"routes/console.php"`; - **`in`**: directories to inspect instead of the whole project, when no path is passed on the command line. Exclusions also apply to `--dirty` and `--diff` runs: those options pick changed files, and Pint then drops any that its finder would not include. ## Sharing and precedence Several repositories often need the same style. Pint gives three ways to point at shared settings: 1. **`--config`** loads a specific file, for example one inside a company package in `vendor`, or a URL. Only `https://` URLs are accepted; a plain `http://` URL is refused. 2. **`extend`** in `pint.json` names a base file, resolved relative to the `pint.json` that extends it. Local settings override the base. Only one level is allowed: a base that itself extends another file makes Pint throw. 3. **`--no-config`** ignores any `pint.json` and runs the preset untouched, which is useful when checking what the preset alone would do. Keeping `pint.json` small matters. Every override is a place where the project drifts from the style the rest of the Laravel ecosystem uses, so a team should add rules deliberately and write down why.
- If pint.json sets "blank_line_before_statement": true, does the laravel preset's statement list survive?No. Pint merges `rules` over the preset key by key, so the `pint.json` entry replaces the preset's entry. `true` means the rule's own PHP CS Fixer defaults, not the preset's `continue`/`return` list. To tweak it, copy the full options object and edit that.
- How can several Laravel repositories share one Pint style?Put a base `pint.json` somewhere every repository can read, such as a shared package, and either run `pint --config path/to/pint.json` or add `"extend"` to each project's file. Only one level of `extend` is allowed, and remote configs must use `https://`.
- When would a team choose the empty preset?When it wants to own every rule explicitly, for example to adopt a published PHP CS Fixer rule set plus a handful of additions. With `empty`, nothing runs except what the `rules` object lists, so no preset update can change the output behind the team's back.
A preset is a publisher's house style guide, and pint.json rules are editor's notes pinned over single entries. A note does not amend the entry beneath it; it covers it, so anything the old entry said that the note omits is gone.
saying these in an interview costs you the question
- Pint reads its rules from a .php-cs-fixer.dist.php file in the project.
- A rule object in pint.json merges into the preset's options for that rule.
- The per preset is what Pint applies to a Laravel project by default.
- pint.json must sit in the project root; there is no way to load another file.
- When pint.json and --preset disagree, the file's preset wins.