Overview
Vale’s filter system allows you to dynamically select and modify which rules run using powerful expression syntax. Instead of editing .vale.ini for every rule change, you can:
Filter rules by style, severity, or scope
Override rule levels on the fly
Apply different rules for different scenarios
Create reusable filter profiles
Filters use the --filter flag and are evaluated at runtime.
Basic Usage
Filter rules directly from the command line:
vale --filter ".Level == 'error'" docs/
This runs only rules with severity level error.
Filter Options
The --filter flag accepts three input types:
Inline Expression
File Path
StylesPath Reference
Pass the filter expression directly: vale --filter ".Level == 'error'" file.md
Good for quick, one-off filtering. Reference a file containing the filter expression: vale --filter /path/to/filter.txt file.md
The file should contain just the expression: Store filters in your Vale configuration: vale --filter my-filter file.md
Vale looks for StylesPath/config/filters/my-filter. This is the recommended approach for team-shared filters.
Filter Syntax
Filters use the expr expression language with access to rule properties.
Available Fields
Each rule exposes these fields (see internal/check/definition.go:32-44):
type Definition struct {
Action core . Action
Description string
Extends string
Level string // "suggestion", "warning", "error"
Limit int
Link string
Message string
Name string // e.g., "Vale.Spelling"
Scope [] string // e.g., ["text", "heading"]
Selector Selector
}
Filters operate on the Definition structure, which represents rule metadata.
Basic Expressions
Filter by Level
Filter by Style
Filter by Scope
Show only errors: vale --filter ".Level == 'error'" file.md
Show warnings and errors: vale --filter ".Level in ['warning', 'error']" file.md
Exclude suggestions: vale --filter ".Level != 'suggestion'" file.md
Run only Microsoft style rules: vale --filter "startsWith(.Name, 'Microsoft.')" file.md
Exclude a specific style: vale --filter "!startsWith(.Name, 'write-good.')" file.md
Multiple styles: vale --filter "startsWith(.Name, 'Google.') || startsWith(.Name, 'Microsoft.')" file.md
Rules that check headings: vale --filter "'heading' in .Scope" file.md
Rules that don’t check code: vale --filter "!('code' in .Scope)" file.md
How Filters Work
Vale’s filter implementation (see internal/check/filter.go:14-112):
Step 1: Load Filter Expression
Vale reads the filter from one of three sources: func filter ( mgr * Manager ) ( map [ string ] Rule , error ) {
var filter string
stringOrPath := mgr . Config . Flags . Filter
if system . FileExists ( stringOrPath ) {
// Case 1: Valid file path
b , err := os . ReadFile ( stringOrPath )
filter = string ( b )
} else if found := core . FindAsset ( mgr . Config , stringOrPath ); found != "" {
// Case 2: StylesPath reference
b , err := os . ReadFile ( found )
filter = string ( b )
} else {
// Case 3: Inline expression
filter = stringOrPath
}
return mgr . rules , nil
}
Step 2: Build Environment
All loaded rules are exposed to the filter: env := FilterEnv {}
for _ , rule := range mgr . rules {
env . Rules = append ( env . Rules , rule . Fields ())
}
The environment contains a Rules array with all rule definitions.
Step 3: Compile and Execute
The filter expression is compiled and executed: code := fmt . Sprintf ( `filter(Rules, { %s })` , filter )
program , err := expr . Compile ( code , expr . Env ( env ))
if err != nil {
return mgr . rules , err
}
output , err := expr . Run ( program , env )
The filter() function selects matching rules.
Step 4: Update Configuration
Vale adjusts the minimum alert level if needed: if strings . Contains ( code , ".Level" ) {
lvl := core . LevelToInt [ rule . Level ]
if lvl < mgr . Config . MinAlertLevel {
mgr . Config . MinAlertLevel = lvl
}
}
This ensures filtered rules can actually run.
Filter Pipeline
The order of evaluation is always:
.vale.ini → --filter → final rules
The filter always has the final say. This means:
Filtered results can only be a subset of what .vale.ini would normally load. Filters can’t enable rules that weren’t loaded from your configuration.
Advanced Expressions
Complex Conditions
Pattern Matching
Dynamic Level Override
Combine multiple criteria: vale --filter ".Level == 'error' && startsWith(.Name, 'Vale.')" file.md
Use parentheses for clarity: vale --filter "(.Level == 'error' || .Level == 'warning') && 'heading' in .Scope" file.md
Match rule names with patterns: vale --filter "matches(.Name, '^(Google|Microsoft) \\ .')" file.md
Exclude specific rules: vale --filter "!matches(.Name, 'Spelling|Terms')" file.md
Change rule severity: vale --filter ".Level = 'suggestion'; true" file.md
Make all warnings errors: vale --filter ".Level == 'warning' ? (.Level = 'error'; true) : true" file.md
The expression must return true for the rule to be included after modifying .Level.
Reusable Filter Files
Store common filters for your team:
# StylesPath structure
StylesPath/
├── config/
│ └── filters/
│ ├── strict
│ ├── quick-check
│ └── docs-only
└── ...
Example Filters
strict
quick-check
docs-only
.Level in ['warning', 'error']
Usage: vale --filter strict docs/
.Level == 'error' && !startsWith(.Name, 'Vale.Spelling')
Fast linting without spell-checking. 'text' in .Scope || 'heading' in .Scope
Skip code-specific rules for prose-heavy documents.
CI/CD Integration
Use filters to implement progressive checks:
# .github/workflows/vale.yml
name : Vale
on : [ pull_request ]
jobs :
# Quick check for blockers
errors :
runs-on : ubuntu-latest
steps :
- uses : actions/checkout@v3
- run : vale --filter ".Level == 'error'" docs/
# Full check, but only warn
warnings :
runs-on : ubuntu-latest
continue-on-error : true
steps :
- uses : actions/checkout@v3
- run : vale docs/
Use strict filters for PR checks and relaxed filters during development.
Debugging Filters
View Configuration
See which rules would run:
vale --filter ".Level == 'error'" ls-config
Check the Checks section in the output.
Test Expressions
Create a test filter file and iterate:
echo ".Level == 'error'" > test-filter.txt
vale --filter test-filter.txt file.md
Modify test-filter.txt until you get the desired results.
Common Filter Patterns
Only Check Specific Styles
vale --filter "startsWith(.Name, 'MyCompany.')" docs/
Exclude Long-Running Rules
vale --filter "!startsWith(.Name, 'Vale.Spelling')" docs/
Useful for quick iteration.
Start with errors only: vale --filter ".Level == 'error'" docs/
Then add warnings: vale --filter ".Level in ['error', 'warning']" docs/
Finally, everything:
Different filters for different doc types: # API docs: strict technical rules
vale --filter api-docs api/
# Guides: focus on readability
vale --filter readability-focus guides/
Limitations
Filters cannot:
Enable rules not loaded by .vale.ini
Modify rule patterns or logic
Access file content or paths
Create new rules dynamically
Filters operate only on rule metadata, not the rules themselves.
Best Practices
Start Simple Begin with basic level filters before complex expressions.
Store Team Filters Keep common filters in config/filters/ for everyone to use.
Document Filters Add comments explaining what each filter does: # Strict mode: only errors from core styles
.Level == 'error' && startsWith(.Name, 'Vale.')
Test Changes Always test filter expressions on sample files before using in CI.
Expression Reference
Common expr language features:
Operator Description Example ==, !=Equality .Level == 'error'inMembership 'text' in .Scope&&, `` Logic `.Level == ‘error’ .Level == ‘warning’` !Negation !startsWith(.Name, 'Vale.')startsWith()Prefix match startsWith(.Name, 'Google.')matches()Regex match matches(.Name, '^Vale\\.')
See the expr documentation for complete syntax reference.