| | |

Biome 11 🧹 File Includes and Ignores — files.includes with glob patterns

⏱️ Estimated read: 12 min | |

The files.includes field is the single mechanism that controls which files Biome processes. It replaced the separate files.ignore and files.include fields in Biome 2.0, and the replacement is not a cosmetic merge. The old engine had a flaw: it prepended **/ to every user-provided glob, which meant that a glob like src/** was silently converted to **/src/** and matched paths in nested directories that the developer never intended to include . The new engine removes the implicit prefix, changes the meaning of * so it no longer crosses the path separator, and makes the paths relative to the configuration file instead of the working directory.

The field is an array of glob patterns. A pattern that does not start with ! is an inclusion rule: a file must match at least one of these patterns to be processed. A pattern that starts with ! is an exception: it removes the matching files from the set that the inclusion rules produced. The exceptions are processed in order, which means an exception can itself be excepted by a later inclusion, and the chain is the mechanism that replaced the old two-field model . The order is the entire semantics, and the documentation warns that the patterns are evaluated in the sequence they appear in the array .

Key point: files.includes is a single ordered array of glob patterns. A non-negated pattern includes files; a !-prefixed pattern excludes them. The exceptions are processed in order and can be re-included by a later pattern. The globs are relative to the configuration file, * does not cross the / separator, and a folder must be ignored without the trailing /** to avoid unnecessary traversal.


Why the old include/ignore split was replaced

The implicit-prefix problem. In Biome 1.x, the configuration had two separate fields: files.include and files.ignore. The engine prepended **/ to every user glob, so src/** became **/src/**, and the glob **/src/** matched /projectB/frontend/src/file.js even when the user only meant the src folder of the current project . The documentation’s comparison table shows the difference: with the old engine, the glob src/** did not match /projectA/src/file.js when run from the project root, but it did match the same relative path in a nested project. The new engine does not add the prefix, so the glob is the literal path the developer wrote.

The two-field ordering problem. With the old include and ignore fields, the ignore patterns were always applied after the include patterns, which meant a file that was included could never be re-included if an ignore pattern matched it . The new includes array is a single ordered list, and the exceptions are processed in order, so the chain ["src/**", "!**/*.test.js", "src/special.test.js"] includes the source files, excludes the test files, and then re-includes the one special test file. The chain is the mechanism that the two-field model could not express.

The * semantics problem. In the old engine, * matched any sequence of characters including the path separator /, so **/src/*.js was converted to **/src/**/*.js . In the new engine, * does not match the path separator. The ** is the recursive matcher, and it must be used as an entire path component, which means **a and b** are invalid and produce an error . The distinction is the mechanism that makes the glob predictable.

The relative-path problem. In Biome 1.x, the globs were relative to the working directory where the command ran. In Biome 2.x, the globs are relative to the configuration file . A project that runs biome check from a subdirectory now resolves the globs against the biome.json location, not the subdirectory. The change is the mechanism that keeps the behavior consistent regardless of where the command is invoked.

The folder-traversal problem. The documentation recommends ignoring a folder without the trailing /**, because Biome still traverses the folder to check its contents when the suffix is present, and the traversal risks loading a biome.json or a .gitignore file from the ignored folder . The !dist pattern ignores the dist folder and everything inside it without the traversal, and the dist/** pattern matches the folder and its files but still walks the tree. The recommendation is the mechanism that keeps the ignored folders out of the traversal.


a. The syntax and the evaluation order

The files.includes array is evaluated as a sequence. The inclusion patterns build the set of candidate files, and the ! patterns remove files from that set. The exceptions are processed in order, and a later pattern can re-include a file that an earlier exception removed . The documentation states that the exceptions are “processed in order, allowing you to specify exceptions to exceptions” .

// biome.json — the ordered includes
{
  "files": {
    "includes": [
      "**",
      "!**/dist",
      "!**/coverage",
      "!**/*.test.ts",
      "src/special.test.ts"
    ]
  }
}

The "**" includes everything in the project. The "!**/dist" removes the dist folder and its contents. The "!**/coverage" removes the coverage output. The "!**/*.test.ts" removes the test files. The "src/special.test.ts" re-includes the one special test file, because it appears after the exception that removed it . The order is the mechanism that makes the exception-of-exception possible.

The glob syntax supports the character classes and the ranges. The [0-9] matches a digit, the [!...] negates the class, and the * matches zero or more characters except the / . The ** is the recursive matcher, and it must be a complete path component. The ? is not listed in the Biome documentation’s supported syntax, and the character classes are the mechanism for the single-character matching.

// biome.json — the glob syntax
{
  "files": {
    "includes": [
      "src/**/*.ts",
      "!src/**/*.d.ts",
      "!src/**/[!a-z]*.ts"
    ]
  }
}

The "src/**/*.ts" includes the TypeScript files under src. The "!src/**/*.d.ts" excludes the declaration files. The "!src/**/[!a-z]*.ts" excludes the TypeScript files whose basename does not start with a lowercase letter . The character class is the mechanism that filters by the naming convention.

The folder handling is the separate concern. The dist/** matches the dist folder and all files inside it, and the !dist ignores the dist folder and all files inside it without the trailing suffix . The recommendation is to use the !dist form for the ignored folders, because the dist/** form still traverses the folder and risks loading the configuration files from it.


b. The interaction with the tool-specific includes and the CLI

The files.includes is the first filter. The linter.includes and the formatter.includes are the second filter, and they are applied after the files.includes . A file that does not match the files.includes cannot match the linter.includes, which means the tool-specific includes are a refinement of the global set, not a replacement for it.

// biome.json — the tool-specific refinement
{
  "files": {
    "includes": ["src/**", "!src/**/*.test.ts"]
  },
  "linter": {
    "includes": ["src/**/*.ts"]
  }
}

The files.includes includes the src files and excludes the test files. The linter.includes further restricts the linting to the TypeScript files under src . The test files are excluded from both the formatting and the linting, and the JavaScript files under src are formatted but not linted. The two-level filter is the mechanism that lets the project apply the different tools to the different subsets.

The CLI does not have a flag that overrides the files.includes for the single invocation. The --staged and the --changed flags filter the Git files, but they do not change the glob evaluation . The --no-errors-on-unmatched flag suppresses the error when the glob matches nothing, and the --files-ignore-unknown flag suppresses the diagnostics for the unsupported file types. The two flags are the mechanism that keeps the CI from failing on the empty file sets.

The overrides array is the file-specific configuration, and its includes are evaluated independently of the top-level files.includes. The documentation states that the override is applied when the file matches the override pattern, and the override pattern is evaluated in the order it appears . The override is the mechanism that applies the different settings to the different files, and the top-level files.includes is the mechanism that defines the global set.


Complete Example Session

// ============================================
// PART 1: THE BASIC INCLUDE
// ============================================

{
  "files": {
    "includes": ["src/**"]
  }
}

// The src folder and all its files are included.
// The paths are relative to the biome.json location.

// ============================================
// PART 2: THE BASIC EXCEPTION
// ============================================

{
  "files": {
    "includes": ["**", "!dist", "!coverage"]
  }
}

// Everything is included except the dist and coverage folders.
// The !dist form ignores the folder without the trailing /**.

// ============================================
// PART 3: THE ORDERED EXCEPTION
// ============================================

{
  "files": {
    "includes": [
      "src/**",
      "!src/**/*.test.ts",
      "src/special.test.ts"
    ]
  }
}

// The src files are included.
// The test files are excluded.
// The special test file is re-included because it appears last.

// ============================================
// PART 4: THE CHARACTER CLASS
// ============================================

{
  "files": {
    "includes": ["src/**/*.ts", "!src/**/*.d.ts"]
  }
}

// The TypeScript files are included.
// The declaration files are excluded.

// ============================================
// PART 5: THE TOOL-SPECIFIC REFINEMENT
// ============================================

{
  "files": {
    "includes": ["src/**"]
  },
  "linter": {
    "includes": ["src/**/*.ts"]
  }
}

// The formatter processes all the src files.
// The linter processes only the TypeScript files.

// ============================================
// PART 6: THE MIGRATION FROM V1
// ============================================

// The v1 configuration:
{
  "files": {
    "include": ["src/**"],
    "ignore": ["src/**/*.test.ts"]
  }
}

// The v2 configuration:
{
  "files": {
    "includes": ["src/**", "!src/**/*.test.ts"]
  }
}

// The include and ignore are merged into the includes array.

// ============================================
// PART 7: THE GLOB SYNTAX
// ============================================

// The * does not cross the path separator.
// The ** is the recursive matcher.
// The [0-9] is the character range.
// The [!...] is the negated character class.
// The ! prefix is the exception.

// ============================================
// PART 8: THE FOLDER TRAVERSAL
// ============================================

// The !dist ignores the dist folder without the traversal.
// The dist/** matches the folder and its files but walks the tree.
// The recommendation is the !dist form for the ignored folders.

// ============================================
// PART 9: THE CLI FLAGS
// ============================================

// The --staged and the --changed filter the Git files.
// The --no-errors-on-unmatched suppresses the empty-match error.
// The --files-ignore-unknown suppresses the unknown-type diagnostics.

// ============================================
// PART 10: THE CHECKLIST
// ============================================

// [ ] The includes array is relative to the biome.json.
// [ ] The * does not cross the / separator.
// [ ] The ** is the recursive matcher.
// [ ] The ! prefix is the exception.
// [ ] The exceptions are processed in order.
// [ ] The ignored folders use the !folder form.
// [ ] The tool-specific includes are the refinement.

The ten parts covered the basic include, the basic exception, the ordered exception, the character class, the tool-specific refinement, the migration from v1, the glob syntax, the folder traversal, the CLI flags, and the checklist. The through-line is that the files.includes is the ordered set, and the order is the mechanism that makes the exceptions chain.


Quick Reference

The glob syntax

PatternMatches
*Zero or more characters, not the /
**The recursive directories and files
[0-9]The character range
[!...]The negated character class
!patternThe exception

The include evaluation

PatternEffect
src/**Include the src files
!src/**/*.test.tsExclude the test files
src/special.test.tsRe-include the special file

The v1 to v2 migration

v1v2
files.includeThe files.includes entries
files.ignoreThe !-prefixed files.includes entries
The * crosses /The * does not cross /
The cwd-relativeThe config-relative

The tool-specific includes

FieldApplied after
linter.includesThe files.includes
formatter.includesThe files.includes

Best Practices

✅ Do This:

// Use the ordered exceptions
{ "files": { "includes": ["src/**", "!src/**/*.test.ts"] } }     // ✅

// Ignore the folders without the trailing /**
{ "files": { "includes": ["!dist"] } }                            // ✅

// Use the tool-specific includes for the refinement
{ "linter": { "includes": ["src/**/*.ts"] } }                     // ✅

❌ Don’t Do This:

// Use the trailing /** for the ignored folders
{ "files": { "includes": ["!dist/**"] } } // the unnecessary traversal // ❌

// Assume the * crosses the path separator
{ "files": { "includes": ["src/*.ts"] } } // the nested files are missed // ❌

Common Pitfalls

PitfallWhy It HappensFix
The glob does not match the nested fileThe * does not cross /Use **
The exception does not applyThe order is wrongPut the exception after the include
The re-include does not workThe exception is lastPut the re-include after the exception
The ignored folder is traversedThe trailing /** is usedUse the !folder form

Real-World Examples

1. The basic include

{ "files": { "includes": ["src/**"] } }

2. The basic exception

{ "files": { "includes": ["!dist"] } }

3. The ordered exception

{ "files": { "includes": ["src/**", "!src/**/*.test.ts"] } }

4. The re-include

{ "files": { "includes": ["src/**", "!**/*.test.ts", "src/special.test.ts"] } }

5. The character class

{ "files": { "includes": ["src/**/[!a-z]*.ts"] } }

6. The tool-specific include

{ "linter": { "includes": ["src/**/*.ts"] } }

7. The migration

{ "files": { "includes": ["src/**", "!src/**/*.test.ts"] } }

8. The folder ignore

{ "files": { "includes": ["!dist", "!coverage"] } }

9. The CLI staged

biome check --staged

10. The no-errors-on-unmatched

biome check --no-errors-on-unmatched

Visual

The Ordered Evaluation

┌──────────────────────────────────────────────────────────────┐
│  THE ORDERED INCLUDE ARRAY                                   │
│                                                              │
│  ["src/**", "!src/**/*.test.ts", "src/special.test.ts"]      │
│         │                                                    │
│         ▼                                                    │
│  ┌─────────────────────┐                                    │
│  │ The src files are   │  The include pattern               │
│  │ included            │                                    │
│  └──────────┬──────────┘                                    │
│             │                                                │
│             ▼                                                    │
│  ┌─────────────────────┐                                    │
│  │ The test files are  │  The exception                     │
│  │ excluded            │                                    │
│  └──────────┬──────────┘                                    │
│             │                                                │
│             ▼                                                    │
│  ┌─────────────────────┐                                    │
│  │ The special test    │  The re-include                    │
│  │ is included         │                                    │
│  └─────────────────────┘                                    │
└──────────────────────────────────────────────────────────────┘

The Glob Semantics

┌──────────────────────────────────────────────────────────────┐
│  THE GLOB SEMANTICS                                          │
│                                                              │
│  *                                                           │
│  ┌─────────────────────┐                                    │
│  │ Zero or more chars  │  Not the / separator               │
│  └─────────────────────┘                                    │
│                                                              │
│  **                                                          │
│  ┌─────────────────────┐                                    │
│  │ The recursive       │  Must be a complete component      │
│  │ matcher             │                                    │
│  └─────────────────────┘                                    │
│                                                              │
│  [!...]                                                      │
│  ┌─────────────────────┐                                    │
│  │ The negated         │  The characters not in the class   │
│  │ character class     │                                    │
│  └─────────────────────┘                                    │
└──────────────────────────────────────────────────────────────┘

The Tool-Specific Refinement

┌──────────────────────────────────────────────────────────────┐
│  THE TWO-LEVEL FILTER                                        │
│                                                              │
│  files.includes                                              │
│  ┌─────────────────────┐                                    │
│  │ The src files       │  The global set                    │
│  └──────────┬──────────┘                                    │
│             │                                                │
│             ▼                                                    │
│  linter.includes                                             │
│  ┌─────────────────────┐                                    │
│  │ The TypeScript      │  The linter refinement             │
│  │ files only          │                                    │
│  └─────────────────────┘                                    │
└──────────────────────────────────────────────────────────────┘

Summary

ItemValue
The fieldfiles.includes
The typeThe array of the glob patterns
The includeThe non-! pattern
The exceptionThe !-prefixed pattern
The orderThe exceptions processed in order
The re-includeThe later include after the exception
The *Does not cross the /
The **The recursive matcher
The folder ignoreThe !folder form without the /**

Key takeaways:

  • The files.includes is a single ordered array. The non-negated patterns include the files, and the !-prefixed patterns exclude them. The exceptions are processed in order, and a later include can re-include a file that an earlier exception removed .
  • The * does not cross the path separator. The * matches zero or more characters except the /, and the ** is the recursive matcher. The ** must be a complete path component, and the **a and the b** are invalid .
  • The ignored folders should use the !folder form. The !dist ignores the folder and its contents without the traversal, and the dist/** matches the folder and its files but still walks the tree. The recommendation is the !dist form for the ignored folders .
  • The paths are relative to the configuration file. In Biome 2.x, the globs are resolved against the biome.json location, not the working directory. The change is the mechanism that keeps the behavior consistent regardless of where the command runs .
  • The tool-specific includes are the refinement. The linter.includes and the formatter.includes are applied after the files.includes, which means a file that does not match the global set cannot match the tool-specific set .

Remember: The Biome files.includes is the single ordered array that replaced the include and ignore fields. The include patterns build the set, the ! patterns remove the files, and the order is the mechanism that makes the exceptions chain. The * does not cross the /, the ** is the recursive matcher, and the ignored folders use the !folder form. The paths are relative to the configuration file, and the tool-specific includes are the refinement.

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!