Need help with your JSON?
Try our JSON Formatter tool to automatically identify and fix syntax errors in your JSON. JSON Formatter tool
Integrating JSON Validation with Git Pre-Commit Hooks
If you want Git to validate JSON before a commit lands, keep the hook simple: syntax-check every staged .json file, add schema validation only where structure matters, and run the same checks again in CI. That catches broken configuration early without turning pre-commit into a slow bottleneck.
This page focuses on the setups that are still practical in 2026: a plain Git hook for minimal repos, Husky for Node.js projects, and the pre-commit framework for teams that want shared hooks across multiple languages.
Recommended default
- Validate only staged JSON files so commits stay fast.
- Use syntax validation for every JSON file and schema validation for important config files.
- Keep the same validation in CI because local hooks can still be skipped with
--no-verify.
What pre-commit hooks can and cannot guarantee
Git runs the pre-commit hook before it asks for the commit message. If the hook exits with a non-zero status, the commit stops immediately. That makes it a good place to reject malformed JSON before it hits shared history.
- Great for fast local feedback: developers see JSON failures before push or CI.
- Not a security boundary:
git commit --no-verifybypasses the hook. - Best when paired with CI: local hooks keep people fast, CI keeps the main branch honest.
Syntax validation vs. schema validation
Most teams need both, but not for the same files.
- Syntax validation catches broken commas, quotes, braces, and other parsing errors. Apply it to every staged
.jsonfile. - Schema validation checks that a valid JSON document also has the right shape, required keys, types, and enum values. Apply it to files like app config, content models, or machine-read settings.
If a file is edited by humans and consumed by code, schema validation usually pays for itself. If it is generated or only used as test data, syntax validation is often enough.
Option 1: Plain Git hook with no extra hook manager
This is the fastest path if you just want Git to reject invalid staged JSON and your team already has Node.js available. The script below uses Git pathspecs instead of grep, so it only asks Git for staged .json files.
Create .git/hooks/pre-commit
#!/usr/bin/env bash
set -euo pipefail
found_json=false
while IFS= read -r -d '' file; do
found_json=true
if ! node -e 'JSON.parse(require("node:fs").readFileSync(process.argv[1], "utf8"))' "$file"; then
echo "Invalid JSON: $file"
exit 1
fi
echo "OK $file"
done < <(git diff --cached --name-only -z --diff-filter=ACMR -- '*.json')
if [ "$found_json" = false ]; then
echo "No staged JSON files."
fiMake it executable with chmod +x .git/hooks/pre-commit. This hook is intentionally narrow: it validates staged JSON files only, and it fails on the first broken file so the error is obvious.
If your team already depends on jq, you can replace the Node command with jq empty "$file" and keep the rest of the hook the same.
Option 2: Husky + lint-staged for Node.js repositories
For a shared project, committing a raw file to .git/hooks is not enough because Git does not version those hooks for other contributors. Husky solves that by storing hook scripts in your repo, and lint-staged keeps the checks limited to staged files.
Install the current Husky setup
pnpm add -D husky lint-staged ajv-cli pnpm exec husky init
Husky's current setup flow creates .husky/pre-commit and updates your package scripts for you. Replace the generated command in .husky/pre-commit with this:
pnpm exec lint-staged
Add a focused lint-staged config
{
"*.json": "node scripts/validate-json-syntax.mjs",
"config/app.json": "pnpm exec ajv validate --spec=draft2020 -s schemas/config.schema.json -d config/app.json"
}Example syntax checker
lint-staged passes the staged file list to your command. A tiny Node script is enough for the syntax pass:
import fs from "node:fs";
for (const file of process.argv.slice(2)) {
JSON.parse(fs.readFileSync(file, "utf8"));
console.log(`OK ${file}`);
}This pattern scales well: keep one broad syntax rule for *.json, then add a few precise schema rules for the files that truly need structure checks.
If your schema uses JSON Schema draft 2019-09 or 2020-12, pass the matching Ajv CLI flag such as --spec=draft2020. If you stay on draft-07, you can omit --spec.
Option 3: The pre-commit framework for polyglot teams
If your repository is not centered on Node.js, the pre-commit framework is often the cleanest choice. It installs hooks from versioned repositories and works well across Python, Node, Go, and mixed-code projects.
Example .pre-commit-config.yaml
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v6.0.0
hooks:
- id: check-json
- repo: https://github.com/python-jsonschema/check-jsonschema
rev: 0.37.0
hooks:
- id: check-jsonschema
files: ^config/.*\.json$
args:
- --schemafile
- schemas/config.schema.jsonAfter installing pre-commit, run pre-commit install --install-hooks. This gives you a shared, versioned way to reject malformed JSON and validate important files against a schema without writing your own hook runner.
Common mistakes and troubleshooting
- Relying on syntax checks alone: a file can be perfectly valid JSON and still break your app if required keys are missing or types are wrong.
- Validating the whole repository on every commit: that slows down contributors and makes hooks easy to resent. Limit checks to staged files whenever possible.
- Applying JSON rules to JSONC or JSON5: comments and trailing commas are not valid JSON. Treat those files separately instead of forcing them through a strict JSON parser.
- Forgetting CI: hooks improve local quality, but they do not replace server-side checks because developers can bypass them.
- Overcomplicating schema maps: if dozens of files use different schemas, move the mapping into a small script instead of trying to encode everything directly in one hook command.
Which setup should you pick?
- Pick a plain Git hook if you want the smallest possible solution and your team can manage the local setup manually.
- Pick Husky + lint-staged if you already use Node.js and want versioned hooks that feel natural in a JavaScript repository.
- Pick pre-commit if your repo spans multiple languages or your team already standardizes on it for shared checks.
Conclusion
The best Git JSON validation setup is usually the boring one: parse staged files quickly, add schema checks only where they protect real production config, and keep the exact same validation in CI. Do that, and bad JSON stops being a release-time surprise and becomes a fast local fix.
Need help with your JSON?
Try our JSON Formatter tool to automatically identify and fix syntax errors in your JSON. JSON Formatter tool