| | |

Prettier 03 ๐ŸŽจ CLI Basics โ€” –write, –check, and glob patterns

โฑ๏ธ Estimated read: 11 min | |

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

FlagModifies filesExit code on unformatted filesUse case
--writeYes0Development, pre-commit
--checkNo1CI, verification

File resolution order

Argument typeBehavior
Existing fileFormat exactly that file
Existing directoryRecurse, find supported files
NeitherExpand as fast-glob pattern

Common glob patterns

PatternMatches
**/*.jsAll JS files recursively
*.{js,ts}JS and TS in current directory (no spaces)
src/**/*.cssCSS files under src/ recursively
!**/dist/**Negation (exclude dist/)

Ignore behavior

Default ignore files.gitignore, .prettierignore
Override--ignore-path <file> replaces defaults
node_modulesIgnored unless --with-node-modules
SymlinksNot 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

PitfallWhy It HappensFix
Glob matches no filesShell expanded it before Prettier saw itQuote the glob
CI passes despite unformatted filesPipeline runs --write instead of --checkUse --check and rely on exit code
{js, ts} matches nothingSpace inside brace expansionRemove spaces: {js,ts}
node_modules gets formatted--with-node-modules passed unnecessarilyRemove the flag
Custom ignore file removes defaults--ignore-path overrides rather than addsPass all desired paths
Directory argument formats too muchRecursion includes files you did not intendUse 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

ItemValue
Format in placeprettier <targets> --write
Verify without modifyingprettier <targets> --check
--check exit code on failure1
--write exit code on success0
Directory argumentRecurses, finds supported files
Glob expansionPrettier, via fast-glob
Quotes around globsRequired
Default ignore files.gitignore, .prettierignore
node_modules defaultIgnored
--with-node-modulesIncludes it

Key takeaways:

  • --write and --check are 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 --write in 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. .gitignore and .prettierignore are read by default, and --ignore-path replaces rather than extends them.
  • node_modules is excluded unless explicitly included. The --with-node-modules flag 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).

Was this helpful?


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!