instead of creating unwanted side effects
File Watchers in PhpStorm can automate compilation, formatting and build steps, or produce endless loops that slow the IDE down and throw output files into chaos. The difference lies in the configuration: when a watcher makes sense, which options are critical, and when an alternative is the better fit.
Table of Contents
- 1. What File Watchers are, and what they are not
- 2. Basic configuration of a File Watcher
- 3. Configuring a Scss/Less watcher correctly
- 4. Prettier as a File Watcher, when it makes sense
- 5. PHP CS Fixer as a File Watcher, knowing the pitfalls
- 6. Recognizing and avoiding infinite loops
- 7. Alternatives to File Watchers
- 8. File Watchers compared: when they make sense, when they do not
- 9. Summary
- 10. FAQ
1. What File Watchers are, and what they are not
File Watchers in PhpStorm are an automation feature that runs an external command as soon as a file in the project is saved, or after a configurable delay. The command is invoked with the changed file as an argument and can produce an output file that PhpStorm integrates into the project. The classic example: a .scss file is saved, the watcher compiles it into .css, and PhpStorm immediately shows the compiled CSS file in the project tree.
What File Watchers are not: not a task runner replacement, not a CI system, and not a substitute for the terminal in complex build processes. If a build step combines multiple input files (as Tailwind CSS does, scanning all template files to generate the CSS), the single-file model of File Watchers does not fit. In such cases an external watch process (npm run watch) or a Run Configuration is a better fit.
File Watchers are found under "Settings > Tools > File Watchers". PhpStorm ships prebuilt templates for common use cases: Sass/SCSS, Less, TypeScript, CoffeeScript and more. These templates are carefully configured and a good starting point, adopting them directly is in many cases better than configuring one from scratch.
2. Basic configuration of a File Watcher
Every File Watcher has the following core fields: Name (descriptive, e.g. "SCSS -> CSS"), File type (which file type triggers the watcher), Scope (in which directory), Program (which binary is executed), Arguments (with macros such as $FileName$, $FileDir$) and Output paths to refresh (which output files PhpStorm should read after execution).
The most important, and most frequently misconfigured, option is "Trigger the watcher on external changes". When enabled, the watcher is also triggered when another application (e.g. another watcher) changes a file, not only when the developer saves. This is the most common cause of infinite loops: watcher A changes file X, watcher B watches file X and changes file Y, watcher A watches file Y... a loop forms.
The option "Auto-save edited files to trigger the watcher" determines whether the watcher fires on every keystroke (with a small delay) or only on an explicit save (Ctrl+S). For compute-intensive operations such as Scss compilation or PHP formatting, this option should be disabled, the watcher should only run on a deliberate save, not on every typing step.
// PhpStorm File Watcher configuration structure (exported as XML)
// Settings > Tools > File Watchers > Export
<TaskOptions>
<option name="arguments" value="--style compressed $FileName$:$FileNameWithoutExtension$.css" />
<option name="checkSyntaxErrors" value="true" />
<option name="description" />
<option name="exitCodeBehavior" value="ERROR" />
<option name="fileExtension" value="scss" />
<option name="immediateSync" value="false" /> <!-- Do NOT trigger on external changes -->
<option name="name" value="SCSS -> CSS (compressed)" />
<option name="output" value="$FileNameWithoutExtension$.css:$FileNameWithoutExtension$.css.map" />
<option name="outputFilters">
<array>
<FilterInfo pattern="$FILE_PATH$:$LINE$:$COLUMN$: $MESSAGE$" />
</array>
</option>
<option name="outputFromStdout" value="false" />
<option name="program" value="$ProjectFileDir$/node_modules/.bin/sass" />
<option name="runOnExternalChanges" value="false" /> <!-- Key: prevents loops -->
<option name="scopeName" value="Project Files" />
<option name="trackOnlyRoot" value="false" />
<option name="workingDir" value="$FileDir$" />
</TaskOptions>
3. Configuring a Scss/Less watcher correctly
The Scss watcher is the textbook example of a sensible File Watcher: one input file (style.scss) produces one output file (style.css). The file-to-file model fits perfectly. PhpStorm ships a prebuilt template for Sass/SCSS, take this as the starting point and only adjust the path to the Sass binary.
Important adjustments: the Sass binary must be specified via a local path, not a system-wide binary. In a project with node_modules, $ProjectFileDir$/node_modules/.bin/sass is recommended. The working directory should be set to $FileDir$ so that relative import paths in the Scss file resolve correctly. Source maps (--source-map) are useful during development and should be reflected in the output path configuration.
For Magento projects with Hyvä and Tailwind CSS, the Scss watcher is only relevant for projects that use legacy Scss files alongside the Tailwind build. In a pure Tailwind CSS 4 setup (CSS-first approach) there are no Scss files at all, here the watch process of the Tailwind compiler is the right solution, not a PhpStorm File Watcher.
// Correct Scss File Watcher configuration for Magento theme
// (Only for projects that actually use Scss - not needed for pure Tailwind v4)
// Program: $ProjectFileDir$/web/node_modules/.bin/sass
// Arguments: --no-source-map --style=compressed $FileName$:$FileNameWithoutExtension$.css
// Working dir: $FileDir$
// Output: $FileNameWithoutExtension$.css
// Scope: Project Files (not "All Places" - avoids vendor/node_modules)
// "Trigger on external changes": DISABLED
// "Auto-save to trigger": DISABLED (only on explicit Ctrl+S)
// For Magento Hyvä with Tailwind CSS v4, use a Run Configuration instead:
// npm run watch --prefix app/design/frontend/Mironsoft/default/web/tailwind
// This is better than File Watcher because Tailwind scans ALL template files
// not just the single changed file, the File Watcher model doesn't apply here
// Example: tailwind.config.js equivalent for CSS-first approach
// @source "../../../Magento_Theme/templates/**/*.phtml"
// @source "../../../Magento_Catalog/templates/**/*.phtml"
// Watcher can't handle this multi-file input -> use npm run watch
4. Prettier as a File Watcher, when it makes sense
Configuring Prettier as a File Watcher makes sense in projects where the editor should format on save but "Format on Save" from the IDE's own formatter is not being used. This is typical when the team shares a common .prettierrc and wants to ensure every commit is already formatted, without relying on pre-commit hooks alone.
Since 2022.x, PhpStorm also has a native Prettier integration ("Settings > Languages & Frameworks > JavaScript > Prettier: On save"), which serves as an alternative to the File Watcher and is simpler to configure. The difference: the native integration runs directly inside PhpStorm, the File Watcher spawns an external process. For performance and configuration simplicity, the native integration is preferable.
If Prettier is configured as a File Watcher, the critical setting is: leave the output path empty (Prettier overwrites the input file). But this means: if Prettier changes a file and "Trigger on external changes" is enabled, that triggers the watcher again (the file has changed), a loop forms. The solution: disable "Trigger on external changes". Prettier then only runs on a deliberate save, not whenever the output file (= the input file, overwritten by Prettier) changes.
5. PHP CS Fixer as a File Watcher, knowing the pitfalls
Using PHP CS Fixer as a File Watcher is tempting, but comes with several pitfalls. The most obvious one: PHP CS Fixer changes the input file in place. As with Prettier, a loop forms if "Trigger on external changes" is enabled. This is a common mistake because the template in the PhpStorm dialog has this option enabled by default.
A less obvious pitfall: PHP CS Fixer can produce code changes the developer did not intend, for example by automatically sorting use statements or adding declare(strict_types=1). If this happens on save without the developer seeing it, such changes end up in the next commit. That can pollute git history and make diffs hard to read.
The better alternative for PHP CS Fixer: configure it as a Quality Tool under "Settings > PHP > Quality Tools > PHP CS Fixer" instead of as a File Watcher. CS issues then appear as inspection warnings in the editor, but the automatic fix only runs on an explicit user action (Ctrl+Alt+L or "Apply Fix"). That gives control back to the developer and prevents unexpected changes.
<?php
// PHP CS Fixer, configured as Quality Tool (NOT File Watcher)
// Settings > PHP > Quality Tools > PHP CS Fixer
// .php-cs-fixer.php configuration for Magento PHP 8.4 projects
use PhpCsFixer\Config;
use PhpCsFixer\Finder;
$finder = Finder::create()
->in(__DIR__ . '/src/app/code/Mironsoft')
->name('*.php')
->notPath('Test/Integration') // Skip integration tests
->notPath('generated'); // Never touch generated code
return (new Config())
->setRules([
'@PSR12' => true,
'@PHP84Migration' => true,
'declare_strict_types' => true,
'ordered_imports' => ['sort_algorithm' => 'alpha'],
'no_unused_imports' => true,
'array_syntax' => ['syntax' => 'short'],
'trailing_comma_in_multiline' => true,
'phpdoc_order' => true,
])
->setFinder($finder)
->setUsingCache(true) // Important: much faster on repeated runs
->setRiskyAllowed(false); // Avoid risky fixers in File Watcher context
// As Quality Tool: shows warnings inline, fix on demand
// NOT as File Watcher: avoids unintended auto-changes on save
// Pre-commit hook (git hooks / lint-staged) for CI enforcement
6. Recognizing and avoiding infinite loops
An infinite loop caused by File Watchers happens when the output of one watcher changes a file that triggers another (or the same) watcher. The symptom: the IDE becomes slow, CPU usage climbs permanently, and the File Watcher status keeps showing "Running" over and over. In the worst case the watcher produces hundreds of log entries or output files in a short time.
The main tool for avoiding this: disable "Trigger the watcher on external changes". With this setting, the watcher only reacts to changes made by the developer in PhpStorm (saving), not to changes made by external processes (other watchers, editors, build tools). A watcher that edits in place (like Prettier or PHP CS Fixer) then cannot re-trigger itself, because its own output counts as an "external change".
A second safeguard is the Scope: instead of "All Places", use a restricted scope that contains only the relevant source files, not node_modules/, not vendor/, not var/, not pub/static/. A Scss watcher configured for "All Places" would fire on every npm build, because npm writes CSS files inside node_modules. That is unnecessary and can lead to loops.
// PhpStorm File Watcher, checklist to prevent infinite loops
// 1. "Trigger on external changes" DISABLED
// -> Watcher only fires on developer save, not on tool output
// 2. Scope: use named scope, not "All Places"
// Settings > Appearance & Behavior > Scopes > Add
// Pattern: file[mironsoft-default]:src/app/design/frontend/Mironsoft/default/web/css/src//*.scss
// Exclude: node_modules, vendor, var, pub/static, generated
// 3. For in-place formatters (Prettier, PHP CS Fixer):
// Output paths to refresh: EMPTY (PhpStorm refreshes the input file)
// -> PhpStorm doesn't interpret the overwritten file as "new output"
// -> No second trigger
// 4. Use "Track only root files" for partials:
// If style/_variables.scss changes -> don't run watcher on _variables.scss
// Only run on style.scss (the root that imports partials)
// Prevents N watcher runs for N partial changes
// 5. Check via Help > Diagnostic > Show Log in Explorer
// Filter for "FileWatcher" -> shows all watcher triggers with timestamps
// "2026-05-10 14:23:45 FileWatcher: Triggered for _variables.scss" -> too many
7. Alternatives to File Watchers
For many use cases there are better alternatives to File Watchers. Format on Save in PhpStorm ("Settings > Tools > Actions on Save") performs formatting on save, directly inside the IDE, without an external process, using all of the IDE's formatting rules. For most PHP and HTML formatting tasks that is the better choice than a File Watcher running PHP CS Fixer or Prettier.
For build processes that combine multiple input files (Tailwind CSS, Webpack, Rollup), Run Configurations are the better fit. A Run Configuration starts the build process as a long-running process (npm run watch) and shows its output live in the Run panel. That is more stable, easier to debug and more flexible for complex build processes than a File Watcher.
Pre-commit hooks (via lint-staged or Husky) enforce code quality at CI time without interrupting the local development flow. The developer writes code, saves as often as needed, and formatting is only enforced at commit time. That cleanly separates development comfort from quality enforcement. For teams, this is often the more robust solution compared to File Watchers, which can be configured differently per developer.
8. File Watchers compared: when they make sense, when they do not
| Use case | File Watcher | Better alternative | Why |
|---|---|---|---|
| Compile Scss (1:1) | Ideal | (none) | Clear file-to-file model |
| Tailwind CSS build | Not suitable | npm run watch (Run Config) | Multi-file input, no 1:1 model |
| Prettier on save | Possible, but complex | Native Prettier integration | Simpler, no loop risk |
| PHP CS Fixer | Risky (unintended changes) | Quality Tool + pre-commit | Developer keeps control |
| Compile TypeScript | Good (tsc --watch via config) | tsc --watch Run Configuration | Both options are valid |
Mironsoft
PhpStorm workflows, Magento 2 and PHP development
Want PhpStorm automation set up without side effects?
We help PHP and Magento teams configure File Watchers, Format-on-Save and build processes in PhpStorm cleanly, without infinite loops and performance losses.
IDE configuration
Set up File Watchers, Format-on-Save and Quality Tools correctly
Build automation
Integrate Tailwind, Scss and TypeScript builds cleanly into the development workflow
Code quality
PHP CS Fixer, Prettier and pre-commit hooks as robust quality assurance
9. Summary
File Watchers in PhpStorm are a powerful tool for clear file-to-file transformations like Scss compilation, but a risky tool for more complex use cases such as formatting and multi-file builds. The most important setting is disabling "Trigger on external changes", which prevents infinite loops in almost every scenario. Using a restricted Scope instead of "All Places" prevents watchers from being triggered by build-tool output in third-party directories.
For Magento projects with Hyvä and Tailwind CSS v4, File Watchers are less relevant: the Tailwind build is not a 1:1 file transformation but scans all template files, so a Run Configuration with npm run watch is the right choice. PHP formatting via PHP CS Fixer belongs in the IDE as a Quality Tool, or as a pre-commit hook in the git workflow, not as a File Watcher that automatically changes code on every save. The result is cleaner git history, fewer surprising commits, and an IDE that is not slowed down by infinite loops.
File Watchers in PhpStorm, the essentials at a glance
Preventing infinite loops
ALWAYS disable "Trigger on external changes". Use a restricted scope instead of "All Places". For in-place formatters: leave the output path empty.
When File Watchers make sense
Clear 1:1 transformations: Scss -> CSS, TypeScript -> JS. A single input file, a single output file, no multi-file input.
Use alternatives
Tailwind: npm run watch as a Run Configuration. Prettier: native integration. PHP CS Fixer: Quality Tool plus pre-commit hook instead of a File Watcher.
Setting the scope correctly
Create your own named scope: source files only, without node_modules, vendor, var, pub/static. Prevents unwanted watcher triggers caused by external tools.