# Configuration
`prek` reads **one configuration file per project**. You only need to choose **one** format:
- **prek.toml** (TOML) — recommended for new users
- **.pre-commit-config.yaml** (YAML) — best if you already use pre-commit or rely on tool/editor support
Both formats are first-class and will be supported long-term. They describe the **same** configuration model: you list repositories under `repos`, then enable and configure hooks from those repositories.
=== "prek.toml"
```toml
[[repos]]
repo = "https://github.com/pre-commit/pre-commit-hooks"
hooks = [{ id = "trailing-whitespace" }]
```
=== ".pre-commit-config.yaml"
```yaml
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
hooks:
- id: trailing-whitespace
```
## Pre-commit compatibility
`prek` is **fully compatible** with [`pre-commit`](https://pre-commit.com/) YAML configs, so your existing `.pre-commit-config.yaml` files work unchanged.
If you use **`prek.toml`**, there’s nothing to worry about from a `pre-commit` perspective: upstream `pre-commit` does not read TOML.
If you use the same `.pre-commit-config.yaml` with both tools, keep in mind:
- `prek` supports several extensions beyond upstream `pre-commit`.
- Upstream `pre-commit` may warn about unknown keys or error out on unsupported features.
- To stay maximally portable, avoid the extensions listed below (or keep separate configs).
Notable differences (when using YAML):
- **Workspace mode** is a `prek` feature that can discover multiple projects; upstream `pre-commit` is single-project.
- `files` / `exclude` can be written as **glob mappings** in `prek` (in addition to regex), which is not supported by upstream `pre-commit`.
- `repo: builtin` adds fast built-in hooks in `prek`.
- Upstream `pre-commit` uses `minimum_pre_commit_version`, while `prek` uses `minimum_prek_version` and intentionally ignores `minimum_pre_commit_version`.
### Prek-only extensions
These entries are implemented by `prek` and are not part of the documented upstream `pre-commit` configuration surface.
They work in both YAML and TOML, but they only matter for compatibility if you share a YAML config with upstream `pre-commit`.
- Top-level:
- [`minimum_prek_version`](#prek-only-minimum-prek-version-config)
- [`orphan`](#prek-only-orphan)
- Repo type:
- [`repo: builtin`](#prek-only-repo-builtin)
- Hook-level:
- [`env`](#prek-only-env)
- [`shell`](#shell)
- [`priority`](#prek-only-priority)
- [`minimum_prek_version`](#prek-only-minimum-prek-version-hook)
## Configuration file
### Location (discovery)
By default, `prek` looks for a configuration file starting from your current working directory and moving upward.
It stops when it finds a config file, or when it hits the git repository boundary.
If you run **without** `--config`, `prek` then enables **workspace mode**:
- The first config found while traversing upward becomes the workspace root.
- From that root, `prek` searches for additional config files in subdirectories (nested projects).
Workspace discovery respects `.gitignore`, and also supports `.prekignore` for excluding directories from discovery.
For the full behavior and examples, see [Workspace Mode](workspace.md).
!!! tip
After updating `.prekignore`, run with `--refresh` to force a fresh project discovery so the changes are picked up.
If you pass `--config` / `-c`, workspace discovery is disabled and only that single config file is used.
### File name
`prek` recognizes the following configuration filenames:
- `prek.toml` (TOML)
- `.pre-commit-config.yaml` (YAML, preferred for pre-commit compatibility)
- `.pre-commit-config.yml` (YAML, alternate)
In workspace mode, each project uses one of these filenames in its own directory.
!!! note "One format per repo"
We recommend using a **single format** across the whole repository to avoid confusion.
If multiple configuration files exist in the same directory, `prek` uses only one and ignores the rest.
The precedence order is:
1. `prek.toml`
2. `.pre-commit-config.yaml`
3. `.pre-commit-config.yml`
### File format
Both `prek.toml` and `.pre-commit-config.yaml` map to the same configuration model (repositories under `repos`, then `hooks` under each repo).
This section focuses on format-specific authoring notes and examples.
#### TOML (`prek.toml`)
Practical notes:
- Structure is explicit and less indentation-sensitive.
- Inline tables are common for hooks (e.g. `{ id = "ruff" }`).
TOML supports both **inline tables** and **array-of-tables**, so you can choose between a compact or expanded hook style.
Inline tables (best for small/simple hook configs):
```toml
[[repos]]
repo = "https://github.com/pre-commit/pre-commit-hooks"
rev = "v6.0.0"
hooks = [
{ id = "end-of-file-fixer", args = ["--fix"] },
]
```
Array-of-tables (more readable for larger hook configs):
```toml
[[repos]]
repo = "https://github.com/pre-commit/pre-commit-hooks"
rev = "v6.0.0"
[[repos.hooks]]
id = "trailing-whitespace"
[[repos.hooks]]
id = "check-json"
```
Example:
=== "prek.toml"
```toml
default_language_version.python = "3.12"
[[repos]]
repo = "local"
hooks = [
{
id = "ruff",
name = "ruff",
language = "system",
entry = "python3 -m ruff check",
files = "\\.py$",
},
]
```
The previous example uses multiline inline tables, a feature that was introduced in
[TOML 1.1](https://toml.io/en/v1.1.0), not all parsers have support for it yet.
You may want to use the longer form if your editor/IDE complains about it.
=== "prek.toml"
```toml
default_language_version.python = "3.12"
[[repos]]
repo = "local"
[[repos.hooks]]
id = "ruff"
name = "ruff"
language = "system"
entry = "python3 -m ruff check"
files = "\\.py$"
```
#### YAML (`.pre-commit-config.yaml` / `.yml`)
Practical notes:
- Regular expressions are provided as YAML strings.
If your regex contains backslashes, quote it (e.g. `files: '\\.rs$'`).
- YAML anchors/aliases and merge keys are supported, so you can de-duplicate repeated blocks.
Example:
=== ".pre-commit-config.yaml"
```yaml
default_language_version:
python: "3.12"
repos:
- repo: local
hooks:
- id: ruff
name: ruff
language: system
entry: python3 -m ruff check
files: "\\.py$"
```
#### Choosing a format
**`prek.toml`**
- Clearer structure and less error-prone syntax.
- Recommended for new users or new projects.
**`.pre-commit-config.yaml`**
- Long-established in the ecosystem with broad tool/editor support.
- Fully compatible with upstream `pre-commit`.
**Recommendation**
- If you already use `.pre-commit-config.yaml`, keep it.
- If you want a cleaner, more robust authoring experience, prefer `prek.toml`.
!!! tip
If you want to switch, you can use [`prek util yaml-to-toml`](cli.md#prek-util-yaml-to-toml) to convert YAML configs to `prek.toml`.
YAML comments are not preserved during conversion.
### Scope (per-project)
Each configuration file (`prek.toml`, `.pre-commit-config.yaml`, or `.pre-commit-config.yml`) is scoped to the **project directory it lives in**.
In workspace mode, `prek` treats every discovered configuration file as a **distinct project**:
- A project’s config only controls hook selection and filtering (for example `files` / `exclude`) for that project.
- A project may contain nested subprojects (subdirectories with their own config). Those subprojects run using *their own* configs.
Practical implication: filters in the parent project do not “turn off” a subproject.
Example layout (monorepo with a nested project):
- `foo/.pre-commit-config.yaml` (project `foo`)
- `foo/bar/.pre-commit-config.yaml` (project `foo/bar`, nested subproject)
If project `foo` config contains an `exclude` that matches `bar/**`, then hooks for project `foo` will not run on files under `foo/bar`:
=== "prek.toml"
```toml
# foo/prek.toml
exclude = { glob = "bar/**" }
```
=== ".pre-commit-config.yaml"
```yaml
# foo/.pre-commit-config.yaml
exclude:
glob: "bar/**"
```
But if `foo/bar` is itself a project (has its own config), files under `foo/bar` are still eligible for hooks when running **in the context of project `foo/bar`**.
!!! note "Excluding a nested project"
If `foo/bar/.pre-commit-config.yaml` exists but you *don’t* want it to be recognized as a project in workspace mode, exclude it from discovery using [`.prekignore`](workspace.md#discovery).
Like `.gitignore`, `.prekignore` files can be placed anywhere in the workspace and apply to their directory and all subdirectories.
!!! tip
After updating `.prekignore`, run with `--refresh` to force a fresh project discovery so the changes are picked up.
### Validation
Use [`prek validate-config`](cli.md#prek-validate-config) to validate one or more config files.
If you want IDE completion / validation, prek provides a JSON Schema at [https://prek.j178.dev/docs/prek.schema.json](https://prek.j178.dev/docs/prek.schema.json).
And the schema is also submitted to the [JSON Schema Store](https://www.schemastore.org/prek.json), so some editors may pick it up automatically.
That schema tracks what `prek` accepts today, but `prek` also intentionally tolerates unknown keys for forward compatibility.
## Configuration reference
This section documents the configuration keys that `prek` understands.
### Top-level keys
#### `repos` (required)
A list of hook repositories.
Each entry is one of:
- a remote repository (typically a git URL)
- `repo: local` for hooks defined directly in your repository
- `repo: meta` for built-in meta hooks
- `repo: builtin` for `prek`'s built-in fast hooks
See [Repo entries](#repo-entries).
#### `files`
Global *include* regex applied before hook-level filtering.
- Type: regex string (default, pre-commit compatible) **or** a prek-only glob pattern mapping
- Default: no global include filter
This is usually used to narrow down the universe of files in large repositories.
!!! note "What path is matched? (workspace + nested projects)"
`files` (and `exclude`) are matched against the file path **relative to the project root** — i.e. the directory containing the configuration file.
- For the root project, this is the workspace root.
- For a nested project, this is the nested project directory.
Example (workspace mode):
- Root project config: `./.pre-commit-config.yaml`
- Nested project config: `./nested/.pre-commit-config.yaml`
For a file at `nested/excluded_by_project`:
- Root project sees the path as `nested/excluded_by_project`
- Nested project sees the path as `excluded_by_project`
This matters most for anchored patterns like `^...$`.
!!! tip "Regex matching"
When `files` / `exclude` are regex strings, they are matched with *search* semantics (the pattern can match anywhere in the path).
Use `^` to anchor at the beginning and `$` at the end.
`prek` uses the Rust [`fancy-regex`](https://github.com/fancy-regex/fancy-regex) engine.
Most typical patterns are portable to upstream `pre-commit`, but very advanced regex features may differ from Python’s `re`.
!!! note "prek-only globs"
In addition to regex strings, `prek` supports glob patterns via:
- `files: { glob: "..." }` (single glob)
- `files: { glob: ["...", "..."] }` (glob list)
This is a `prek` extension. Upstream `pre-commit` expects regex strings here.
For more information on the glob syntax, refer to the [globset documentation](https://docs.rs/globset/latest/globset/#syntax).
Examples:
=== "prek.toml"
```toml
# Regex (portable to pre-commit)
files = "\\.rs$"
# Glob (prek-only)
files = { glob = "src/**/*.rs" }
# Glob list (prek-only; matches if any glob matches)
files = { glob = ["src/**/*.rs", "crates/**/src/**/*.rs"] }
```
=== ".pre-commit-config.yaml"
```yaml
# Regex (portable to pre-commit)
files: "\\.rs$"
# Glob (prek-only)
files:
glob: "src/**/*.rs"
# Glob list (prek-only; matches if any glob matches)
files:
glob:
- "src/**/*.rs"
- "crates/**/src/**/*.rs"
```
#### `exclude`
Global *exclude* regex applied before hook-level filtering.
- Type: regex string (default, pre-commit compatible) **or** a prek-only glob pattern mapping
- Default: no global exclude filter
`exclude` is useful for generated folders, vendored code, or build outputs.
!!! note "What path is matched?"
Same as [`files`](#top-level-files): the pattern is evaluated against the file path **relative to the project root** (the directory containing the config).
!!! note "prek-only globs"
Like `files`, `exclude` supports `glob` (single glob or glob list) as a `prek` extension.
For glob syntax details, see the [globset documentation](https://docs.rs/globset/latest/globset/#syntax).
Examples:
=== "prek.toml"
```toml
# Regex (portable to pre-commit)
exclude = "^target/"
# Glob (prek-only)
exclude = { glob = "target/**" }
# Glob list (prek-only)
exclude = { glob = ["target/**", "dist/**"] }
```
=== ".pre-commit-config.yaml"
```yaml
# Regex (portable to pre-commit)
exclude: "^target/"
# Glob (prek-only)
exclude:
glob: "target/**"
# Glob list (prek-only)
exclude:
glob:
- "target/**"
- "dist/**"
```
Verbose regex example (useful for long allow/deny lists):
=== "prek.toml"
```toml
# `(?x)` enables "verbose" regex mode (whitespace and newlines are ignored).
exclude = """(?x)^(
docs/|
vendor/|
target/
)"""
```
=== ".pre-commit-config.yaml"
```yaml
# `(?x)` enables "verbose" regex mode (whitespace and newlines are ignored).
exclude: |
(?x)^(
docs/|
vendor/|
target/
)
```
#### `fail_fast`
Stop the run after the first failing hook.
- Type: boolean
- Default: `false`
This is a global default; individual hooks can also set `fail_fast`.
#### `default_language_version`
Map a language name to the default `language_version` used by hooks of that language.
- Type: map
- Default: none (hooks fall back to `language_version: default`)
Example:
=== "prek.toml"
```toml
default_language_version.python = "3.12"
default_language_version.node = "20"
```
=== ".pre-commit-config.yaml"
```yaml
default_language_version:
python: "3.12"
node: "20"
```
`prek` treats `language_version` as a version request (often a semver-like selector) and may install toolchains automatically. See [Difference from pre-commit](diff.md).
#### `default_stages`
Default `stages` used when a hook does not specify its own.
- Type: list of stage names
- Default: all stages
Allowed values:
- `manual`
- `commit-msg`
- `post-checkout`
- `post-commit`
- `post-merge`
- `post-rewrite`
- `pre-commit`
- `pre-merge-commit`
- `pre-push`
- `pre-rebase`
- `prepare-commit-msg`
#### `default_install_hook_types`
Default Git shim name(s) installed by `prek install` when you don’t pass `--hook-type`.
- Type: list of `--hook-type` values
- Default: `[pre-commit]`
This controls which Git shims are installed (for example `pre-commit` vs `pre-push`).
It is separate from a hook’s `stages`, which controls when a particular hook is eligible to run.
Allowed values:
- `pre-commit`
- `pre-push`
- `commit-msg`
- `prepare-commit-msg`
- `post-checkout`
- `post-commit`
- `post-merge`
- `post-rewrite`
- `pre-merge-commit`
- `pre-rebase`
#### `minimum_prek_version`
!!! note "prek-only"
This key is a `prek` extension. Upstream `pre-commit` uses `minimum_pre_commit_version`, which `prek` intentionally ignores.
Require a minimum `prek` version for this config.
- Type: string (version)
- Default: unset
If the installed `prek` is older than the configured minimum, `prek` exits with an error.
Example:
=== "prek.toml"
```toml
minimum_prek_version = "0.2.0"
```
=== ".pre-commit-config.yaml"
```yaml
minimum_prek_version: "0.2.0"
```
#### `orphan`
!!! note "prek-only"
`orphan` is a `prek` workspace-mode feature and is not recognized by upstream `pre-commit`.
Workspace-mode setting to isolate a nested project from parent configs.
- Type: boolean
- Default: `false`
When `orphan: true`, files under this project directory are handled only by this project’s config and are not “seen” by parent projects.
Example:
=== "prek.toml"
```toml
orphan = true
[[repos]]
repo = "https://github.com/astral-sh/ruff-pre-commit"
rev = "v0.8.4"
hooks = [{ id = "ruff" }]
```
=== ".pre-commit-config.yaml"
```yaml
orphan: true
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.8.4
hooks:
- id: ruff
```
See [Workspace Mode - File Processing Behavior](workspace.md#file-processing-behavior) for details.
### Repo entries
Each item under `repos:` is a mapping that always contains a `repo:` key.
#### Remote repository
Use this for hooks distributed in a separate repository.
Required keys:
- `repo`: repository location (commonly an https git URL)
- `rev`: version to use (tag, branch, or commit SHA)
- `hooks`: list of hook selections
Remote hook definitions live inside the hook repository itself in the
`.pre-commit-hooks.yaml` manifest (at the repo root). Your config only selects
hooks by `id` and optionally overrides options. See [Authoring Hooks](authoring-hooks.md)
if you maintain a hook repository.
##### `repo`
Where to fetch hooks from.
In most configs this is a git URL.
`prek` also recognizes special values documented separately: `local`, `meta`, and `builtin`.
##### `rev`
The revision to use for the remote repository.
Use a tag or commit SHA for repeatable results.
If you use a moving target (like a branch name), runs may change over time.
##### `hooks`
The list of hooks to enable from that repository.
Each item must at least specify `id`.
You can also add hook-level options (filters, args, stages, etc.) to customize behavior.
Example:
=== "prek.toml"
```toml
[[repos]]
repo = "https://github.com/astral-sh/ruff-pre-commit"
rev = "v0.8.4"
hooks = [{ id = "ruff", args = ["--fix"] }]
```
=== ".pre-commit-config.yaml"
```yaml
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.8.4
hooks:
- id: ruff
args: [--fix]
```
Notes:
- For reproducibility, prefer immutable pins (tags or commit SHAs).
- `prek auto-update` can help update `rev` values.
#### `repo: local`
Define hooks inline inside your repository.
Keys:
- `repo`: must be `local`
- `hooks`: list of **local hook definitions** (see [Local hook definition](#local-hook-definition))
Example:
=== "prek.toml"
```toml
[[repos]]
repo = "local"
hooks = [
{
id = "cargo-fmt",
name = "cargo fmt",
language = "system",
entry = "cargo fmt",
files = "\\.rs$",
},
]
```
=== ".pre-commit-config.yaml"
```yaml
repos:
- repo: local
hooks:
- id: cargo-fmt
name: cargo fmt
language: system
entry: cargo fmt
files: "\\.rs$"
```
#### `repo: meta`
Use `pre-commit`-style meta hooks that validate and debug your configuration.
`prek` supports the following meta hook ids:
- `check-hooks-apply`
- `check-useless-excludes`
- `identity`
Restrictions:
- `id` is required.
- `entry` is not allowed.
- `language` (if set) must be `system`.
You may still configure normal hook options such as `files`, `exclude`, `stages`, etc.
Example:
=== "prek.toml"
```toml
[[repos]]
repo = "meta"
hooks = [{ id = "check-useless-excludes" }]
```
=== ".pre-commit-config.yaml"
```yaml
repos:
- repo: meta
hooks:
- id: check-useless-excludes
```
#### `repo: builtin`
!!! note "prek-only"
`repo: builtin` is specific to `prek` and is not compatible with upstream `pre-commit`.
Use `prek`’s built-in fast hooks (offline, zero setup).
Restrictions:
- `id` is required.
- `entry` is not allowed.
- `language` (if set) must be `system`.
Example:
=== "prek.toml"
```toml
[[repos]]
repo = "builtin"
hooks = [
{ id = "trailing-whitespace" },
{ id = "check-yaml" },
]
```
=== ".pre-commit-config.yaml"
```yaml
repos:
- repo: builtin
hooks:
- id: trailing-whitespace
- id: check-yaml
```
For the list of available built-in hooks and the “automatic fast path” behavior, see [Built-in Fast Hooks](builtin.md).
### Hook entries
Hook items under `repos[*].hooks` have slightly different shapes depending on the repo type.
#### Remote hook selection
For a remote repo, the hook entry must include:
- `id` (required): selects the hook from the repository
All other hook keys are optional overrides (for example `args`, `files`, `exclude`, `stages`, …).
!!! note "Advanced overrides"
`prek` also supports overriding `name`, `entry`, and `language` for remote hooks.
This can be useful for experimentation, but it may reduce portability to the original `pre-commit`.
#### Local hook definition
For `repo: local`, the hook entry is a full definition and must include:
- `id` (required): stable identifier used by `prek run ` and selectors
- `name` (required): label shown in output
- `entry` (required): command to execute
- `language` (required): how `prek` sets up and runs the hook
#### Builtin/meta hook selection
For `repo: builtin` and `repo: meta`, the hook entry must include `id`.
You can optionally provide `name` and normal hook options (filters, stages, etc), but not `entry`.
### Common hook options
These keys can appear on hooks (remote/local/builtin/meta), subject to the restrictions above.
#### `id`
The stable identifier of the hook.
- For remote hooks, this must match a hook id defined by the remote repository.
- For local hooks, you choose it.
`id` is also used for CLI selection (for example `prek run ` and `PREK_SKIP`).
!!! note "Hook ids containing `:`"
If your hook id contains `:` (for example `id: lint:ruff`), `prek run lint:ruff`
will not select that hook. `prek` interprets `lint:ruff` as the selector
`:`, with project `lint` and hook `ruff`.
To select the hook id `lint:ruff`, add a leading `:` and run
`prek run :lint:ruff`.
#### `name`
Human-friendly label shown in output.
- Required for `repo: local` hooks.
- Optional as an override for remote/meta/builtin hooks.
#### `entry`
The command line to execute for the hook.
- Required for `repo: local` hooks.
- Optional override for remote hooks.
- Not allowed for `repo: meta` and `repo: builtin`.
If `pass_filenames: true`, `prek` appends matching filenames to this command when running.
#### `shell`
!!! note "prek-only"
`shell` is a `prek` extension and may not be recognized by upstream `pre-commit`.
Run `entry` through a predefined shell adapter.
- Type: one of `sh`, `bash`, `pwsh`, `powershell`, `cmd`
- Default: `null` (run `entry` directly without a shell)
When `shell` is omitted, `prek` preserves the default no-shell behavior: it parses `entry` into argv, invokes the command directly, and appends `args` and matching filenames as process arguments.
When `shell` is set, `entry` is treated as source for that shell. `prek` writes the source to a temporary script file, runs it with the selected shell adapter, and passes hook `args` followed by matching filenames as script arguments.
| `shell` | Adapter command | Script arguments |
| -- | -- | -- |
| `bash` | `bash --noprofile --norc -eo pipefail