Prettier 03 ๐จ CLI Basics โ –write, –check, and glob patterns
The Prettier CLI has two modes that matter: one that changes files and one that reports whether files need changing. Everything else is file selection. --write reformats files in place and is what you run during development. --check reports which files are not formatted and exits with a non-zero status if any are found, which is what CI runs to prevent unformatted code from merging. Between those two flags and the glob patterns that select which files they operate on, the CLI covers the entire formatting workflow.
This chapter explains how --write and --check differ in behavior and exit codes, how Prettier resolves file arguments and glob patterns, why quotes around globs are mandatory for cross-platform correctness, and how the ignore files interact with the selection process. By the end, you will be able to construct CLI commands that format exactly the files you intend, in both local development and automated pipelines.
Key point: --write modifies files and exits zero even if it changed something. --check modifies nothing and exits non-zero if any file would change. CI uses --check; developers use --write.
Why the CLI has two modes
The mutation problem. A formatter that only reports problems is useful in CI but useless during development, because the developer must manually apply every fix. A formatter that only writes is useful during development but dangerous in CI, because it can silently change files in a pipeline where the changes are discarded. Prettier splits the two behaviors into separate flags so each environment uses the mode appropriate to it.
The exit code problem. CI systems decide pass or fail based on exit codes, not output. --check returns a non-zero exit code when unformatted files exist, which is what makes it suitable as a gate . --write returns zero regardless of whether it changed anything, because the operation succeeded: the files are now formatted. A CI pipeline that runs --write and then checks git diff is doing the same job less directly.
The file selection problem. Both modes operate on whatever files the arguments select. A bare directory argument recurses into it. A file path selects that file. Anything else is treated as a glob and resolved against the filesystem . The selection logic is identical for both modes; only the action differs.
a. –write: format in place
--write is the development command. It parses each selected file, reprints it from the AST, and writes the result back to disk if the output differs from the input. If the file is already formatted, it is not rewritten, but the command still exits zero.
# Format every supported file in the current directory recursively
npx prettier . --write
# Format a single file
npx prettier src/index.js --write
# Format a directory
npx prettier src/ --write
The output lists each file that was changed, with the original and formatted sizes:
src/index.js 12ms
src/utils.js 8ms (unchanged)
--write is the correct choice for pre-commit hooks and editor save actions, because the goal there is to produce formatted files, not to report on them.
b. –check: report without modifying
--check is the verification command. It performs the same parsing and formatting work, but instead of writing the result, it compares the formatted output to the original file. If they differ, the file is listed and the command exits with a non-zero status .
# Check whether all files are formatted
npx prettier . --check
# Check a specific file
npx prettier src/index.js --check
The output on failure names each file that would be changed:
Checking formatting...
src/index.js
src/utils.js
Code style issues found in 2 files. Run Prettier with --write to fix.
If every file is already formatted, the command prints a success message and exits zero. This exit code behavior is what makes --check suitable for CI: a non-zero exit fails the pipeline step.
c. Glob patterns and file resolution
Prettier does not treat every argument as a glob. The CLI processes arguments in a specific order, and understanding that order prevents the most common selection mistakes .
First, every argument is treated as a literal path. If the path is an existing file, Prettier formats that file and does not expand it as a glob. If the path is an existing directory, Prettier recursively finds all supported files inside it, based on file extensions and well-known filenames. Only if the argument is neither an existing file nor an existing directory is it resolved as a glob pattern, using the fast-glob syntax .
# Directory: recurses into src/ and formats supported files
npx prettier src/ --write
# Literal file: formats exactly this file
npx prettier src/index.js --write
# Glob: expands to matching files
npx prettier "src/**/*.js" --write
The quotes around the glob are not optional. Without quotes, the shell expands the glob before Prettier sees it. On a system where the shell’s expansion differs from Prettier’s fast-glob resolutionโwhich is most systems, because the shell does not know which extensions Prettier supportsโthe result is a list of files that may not match what Prettier would have selected. The documentation is explicit: “Don’t forget the quotes around the globs! The quotes make sure that Prettier CLI expands the globs rather than your shell, which is important for cross-platform usage” .
Glob syntax follows fast-glob. Common patterns include **/*.js for all JavaScript files recursively, *.{js,ts} for multiple extensions in a single directory, and src/**/*.{css,scss} for style files under src/. Brace expansion does not tolerate spaces: {js, ts} matches nothing because the space becomes part of the pattern, while {js,ts} works .
Prettier ignores files in node_modules by default. To include them, --with-node-modules must be passed explicitly . Symbolic links are not followed when expanding arguments .
d. Ignore files and their interaction with selection
Prettier reads .gitignore and .prettierignore in the current directory by default. Files matched by either are excluded from the selected set, regardless of whether they match the glob .
# Custom ignore file
npx prettier . --write --ignore-path .myignore
# Multiple ignore files (overrides the default set)
npx prettier . --write --ignore-path .gitignore --ignore-path .prettierignore
Passing --ignore-path overrides the default ignore files rather than adding to them. To keep the defaults and add another file, list all three explicitly .
Complete Example Session
# ============================================
# PART 1: FORMAT A DIRECTORY IN PLACE
# ============================================
# Recursively formats all supported files under src/
npx prettier src/ --write
# Directory arguments recurse; supported files are
# determined by extension and well-known filenames.
# ============================================
# PART 2: FORMAT A SINGLE FILE
# ============================================
npx prettier src/index.js --write
# Literal file paths are not expanded as globs.
# Only this file is formatted.
# ============================================
# PART 3: USE A QUOTED GLOB
# ============================================
# Quotes prevent the shell from expanding the glob
npx prettier "src/**/*.js" --write
# Without quotes, the shell expands and Prettier
# receives a different list of files.
# ============================================
# PART 4: CHECK FORMATTING WITHOUT MODIFYING
# ============================================
npx prettier . --check
# Exits 0 if all files are formatted.
# Exits 1 if any file would be changed.
# CI uses this exit code as a gate.
# ============================================
# PART 5: COMBINE MULTIPLE EXTENSIONS
# ============================================
npx prettier "src/**/*.{js,ts,css}" --write
# Brace expansion with no spaces.
# {js, ts} would not match; the space is part of the pattern.
# ============================================
# PART 6: EXCLUDE WITH AN IGNORE FILE
# ============================================
# .prettierignore contains:
# dist/
# coverage/
npx prettier . --write
# Files in dist/ and coverage/ are skipped even though
# the directory argument would otherwise recurse into them.
# ============================================
# PART 7: CUSTOM IGNORE FILE
# ============================================
npx prettier . --write --ignore-path .prettierignore.ci
# Overrides the default .gitignore and .prettierignore.
# To keep defaults, pass all three --ignore-path flags.
# ============================================
# PART 8: INCLUDE node_modules (RARE)
# ============================================
npx prettier . --write --with-node-modules
# node_modules is ignored by default.
# Only use this when formatting dependencies is intentional.
# ============================================
# PART 9: CI PIPELINE STEP
# ============================================
# npm script: "format:check": "prettier . --check"
# CI runs:
npm ci
npm run format:check
# Non-zero exit fails the step.
# No files are modified in CI.
# ============================================
# PART 10: PRE-COMMIT HOOK
# ============================================
# package.json:
# "lint-staged": { "*.{js,ts}": "prettier --write" }
# The hook runs --write on staged files only.
# Exit is zero even if changes were made; lint-staged
# re-stages the formatted files.
The ten parts covered directory formatting, single-file formatting, quoted globs, check mode, multi-extension selection, ignore file interaction, custom ignore paths, the node_modules opt-out, CI usage, and pre-commit hooks.
Quick Reference
The two modes
| Flag | Modifies files | Exit code on unformatted files | Use case |
|---|---|---|---|
--write | Yes | 0 | Development, pre-commit |
--check | No | 1 | CI, verification |
File resolution order
| Argument type | Behavior |
|---|---|
| Existing file | Format exactly that file |
| Existing directory | Recurse, find supported files |
| Neither | Expand as fast-glob pattern |
Common glob patterns
| Pattern | Matches |
|---|---|
**/*.js | All JS files recursively |
*.{js,ts} | JS and TS in current directory (no spaces) |
src/**/*.css | CSS files under src/ recursively |
!**/dist/** | Negation (exclude dist/) |
Ignore behavior
| Default ignore files | .gitignore, .prettierignore |
|---|---|
| Override | --ignore-path <file> replaces defaults |
node_modules | Ignored unless --with-node-modules |
| Symlinks | Not followed |
Best Practices
โ Do This:
# Quote globs to let Prettier expand them
npx prettier "src/**/*.js" --write # โ
# Use --check in CI
npx prettier . --check # โ
# Use --write in development and pre-commit
npx prettier . --write # โ
# Keep brace expansions free of spaces
"**/*.{js,ts,css}" # โ
# List ignore paths explicitly when adding a custom one
--ignore-path .gitignore --ignore-path .prettierignore --ignore-path .ci-ignore # โ
โ Don’t Do This:
# Unquoted glob: shell expands it, not Prettier
npx prettier src/**/*.js --write # โ
# Use --write in CI and check git diff
npx prettier . --write && git diff --exit-code # โ (use --check)
# Brace expansion with spaces
"**/*.{js, ts}" # โ (matches nothing)
# Assume --check formats files
npx prettier . --check # โ (does not modify)
# Rely on --write exit code to detect unformatted files
npx prettier . --write; echo $? # โ (always 0 on success)
Common Pitfalls
| Pitfall | Why It Happens | Fix |
|---|---|---|
| Glob matches no files | Shell expanded it before Prettier saw it | Quote the glob |
| CI passes despite unformatted files | Pipeline runs --write instead of --check | Use --check and rely on exit code |
{js, ts} matches nothing | Space inside brace expansion | Remove spaces: {js,ts} |
node_modules gets formatted | --with-node-modules passed unnecessarily | Remove the flag |
| Custom ignore file removes defaults | --ignore-path overrides rather than adds | Pass all desired paths |
| Directory argument formats too much | Recursion includes files you did not intend | Use a glob with explicit extensions |
Real-World Examples
1. Format everything in the project
npx prettier . --write
2. Check formatting in CI
npx prettier . --check
3. Format only JavaScript under src
npx prettier "src/**/*.js" --write
4. Format multiple extensions
npx prettier "src/**/*.{js,ts,css}" --write
5. Check a single file
npx prettier src/app.js --check
6. Custom ignore file
npx prettier . --write --ignore-path .prettierignore.ci
7. Include node_modules (intentional)
npx prettier . --write --with-node-modules
8. Pre-commit on staged files
npx prettier --write $(git diff --name-only --cached)
9. Format a directory
npx prettier src/ --write
10. Verify a glob before writing
npx prettier "src/**/*.ts" --check
Visual
–write vs –check
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ --write --check โ
โ โ
โ Parse file Parse file โ
โ โ โ โ
โ โผ โผ โ
โ Format from AST Format from AST โ
โ โ โ โ
โ โผ โผ โ
โ Compare to original Compare to original โ
โ โ โ โ
โ โโโ different โโโบ write โโโ different โโโบ report โ
โ โ โ exit 1 โ
โ โโโ same โโโบ skip โโโ same โโโบ skip โ
โ โ
โ Exit: 0 always Exit: 0 if clean, 1 if not โ
โ Modifies files Never modifies files โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
File resolution order
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ ARGUMENT RESOLUTION โ
โ โ
โ Argument: "src/index.js" โ
โ โ โ
โ โผ โ
โ Is it an existing file? โโyesโโโบ Format it, done โ
โ โ โ
โ no โ
โ โผ โ
โ Is it an existing directory? โโyesโโโบ Recurse, find files โ
โ โ โ
โ no โ
โ โผ โ
โ Resolve as fast-glob pattern โ
โ โ โ
โ โผ โ
โ Match against filesystem (respecting ignore files) โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Why quotes matter
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ QUOTED VS UNQUOTED GLOB โ
โ โ
โ UNQUOTED: npx prettier src/**/*.js --write โ
โ โ โ
โ โผ โ
โ Shell expands **/*.js to a file list โ
โ (may include files Prettier does not support, โ
โ or miss files the shell does not know about) โ
โ โ โ
โ โผ โ
โ Prettier receives literal paths โ
โ โ
โ QUOTED: npx prettier "src/**/*.js" --write โ
โ โ โ
โ โผ โ
โ Shell passes the string unchanged โ
โ โ โ
โ โผ โ
โ Prettier expands with fast-glob โ
โ (supports Prettier's full extension set) โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Ignore file interaction
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ SELECTION PIPELINE โ
โ โ
โ Argument list โ
โ โ โ
โ โผ โ
โ Literal path / directory / glob resolution โ
โ โ โ
โ โผ โ
โ Candidate file set โ
โ โ โ
โ โผ โ
โ Filter: .gitignore + .prettierignore โ
โ โ โ
โ โผ โ
โ Filter: node_modules (unless --with-node-modules) โ
โ โ โ
โ โผ โ
โ Final set for --write or --check โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Summary
| Item | Value |
|---|---|
| Format in place | prettier <targets> --write |
| Verify without modifying | prettier <targets> --check |
--check exit code on failure | 1 |
--write exit code on success | 0 |
| Directory argument | Recurses, finds supported files |
| Glob expansion | Prettier, via fast-glob |
| Quotes around globs | Required |
| Default ignore files | .gitignore, .prettierignore |
node_modules default | Ignored |
--with-node-modules | Includes it |
Key takeaways:
--writeand--checkare not interchangeable. One modifies files and always exits zero on success; the other modifies nothing and exits non-zero when formatting is needed.- CI must use
--check. Using--writein CI discards the changes and provides no gate. The exit code is the gate. - Quotes around globs are mandatory. Without them, the shell expands the pattern, and the shell does not know Prettier’s supported extensions or ignore rules.
- Prettier resolves arguments in order: file, then directory, then glob. A directory argument recurses; a glob argument is expanded by Prettier itself.
- Ignore files filter the selected set.
.gitignoreand.prettierignoreare read by default, and--ignore-pathreplaces rather than extends them. node_modulesis excluded unless explicitly included. The--with-node-modulesflag is a deliberate opt-in, not a default.
Remember: The CLI is a file selector plus an action. The action is either “write the formatted output” or “report whether the output would differ.” The selector is whatever combination of paths, directories, and globs you provide, filtered through the ignore files. Get the selection right by quoting globs, using directory arguments for recursion, and keeping ignore files explicit. Get the action right by using --write where changes should happen and --check where changes should be detected. The two mistakes that cause the most trouble are unquoted globs (wrong file set) and --write in CI (no gate at all).
Stop using slow, ad-bloated tool sites! ๐คฎ
๐ Search “KandZ Tools” on Google to use many professional utilities for free.
KandZ.me is the ultimate minimalist hub for:
โ
Finance (Mortgage, Interest, Inflation)
โ
Tech (Base64, JSON, Dev Suite, IP)
โ
Health (BMI, BMR, TDEE)
โ
Productivity (Timer, Workspace, QR)
โก๏ธ Fast & Private
๐ No data leaves your device
๐ 100% Free
๐ Use it now: https://tools.kandz.me
๐ Bookmark itโyouโll need it later!