Complete guide to Claude Code hooks. Automate linting, testing, formatting, and custom scripts that run before or after Claude Code actions.
Claude Code Hooks: Automate Your Development Workflow
Claude Code hooks let you run custom scripts before or after Claude actions. Auto-format code, run tests, lint changes, send notifications—automatically.
This guide covers hook types, configuration, and practical automation examples.
What Are Claude Code Hooks?
Hooks are scripts that execute at specific points in Claude Code's workflow:
- Pre-hooks: Run before Claude takes an action
- Post-hooks: Run after Claude completes an action
Example flow:
You: "Add a new API endpoint"
↓
[Pre-edit hook: backup files]
↓
Claude Code edits files
↓
[Post-edit hook: run linter]
↓
Results shown to you
Why Use Hooks?
Without Hooks
- Claude Code makes changes
- You manually run linter
- You manually run tests
- You manually format code
- You manually commit
With Hooks
- Claude Code makes changes
- Everything happens automatically
- You review clean, tested code
Hooks eliminate repetitive manual steps.
Hook Configuration
Configuration File
Create .claude/config.json in your project:
{
"hooks": {
"post-edit": "npm run lint:fix",
"post-create": "npm run format",
"pre-commit": "npm test"
}
}
Available Hook Points
| Hook |
When It Runs |
pre-edit |
Before editing any file |
post-edit |
After editing any file |
pre-create |
Before creating a new file |
post-create |
After creating a new file |
pre-delete |
Before deleting a file |
post-delete |
After deleting a file |
pre-command |
Before running shell commands |
post-command |
After running shell commands |
Hook Script Format
Hooks can be:
Simple command:
{
"hooks": {
"post-edit": "npm run lint"
}
}
Multiple commands:
{
"hooks": {
"post-edit": "npm run lint && npm run format"
}
}
Custom script:
{
"hooks": {
"post-edit": "./scripts/post-edit.sh"
}
}
Practical Examples
1. Auto-Format on Edit
{
"hooks": {
"post-edit": "npx prettier --write ."
}
}
Every time Claude edits a file, Prettier formats it.
2. Run Linter
{
"hooks": {
"post-edit": "npm run lint:fix"
}
}
ESLint (or your linter) runs after every edit.
3. TypeScript Check
{
"hooks": {
"post-edit": "npx tsc --noEmit"
}
}
Catch type errors immediately after edits.
4. Run Related Tests
{
"hooks": {
"post-edit": "./scripts/run-related-tests.sh"
}
}
Script that finds and runs tests for changed files:
#!/bin/bash
# scripts/run-related-tests.sh
# Get changed files
changed=$(git diff --name-only)
# Find related test files
for file in $changed; do
test_file="${file%.ts}.test.ts"
if [ -f "$test_file" ]; then
npx jest "$test_file"
fi
done
5. Backup Before Destructive Actions
{
"hooks": {
"pre-delete": "./scripts/backup-file.sh"
}
}
6. Notify on Completion
{
"hooks": {
"post-edit": "./scripts/notify.sh"
}
}
Send Slack message or desktop notification when Claude finishes.
7. Update Documentation
{
"hooks": {
"post-edit": "npm run docs:generate"
}
}
Auto-regenerate API docs after code changes.
8. Full CI Pipeline
{
"hooks": {
"post-edit": "./scripts/local-ci.sh"
}
}
#!/bin/bash
# scripts/local-ci.sh
echo "Running local CI..."
# Format
npm run format
# Lint
npm run lint:fix
# Type check
npm run typecheck
# Test
npm test
# Build check
npm run build
echo "All checks passed!"
Advanced Hook Patterns
Conditional Hooks
Run different hooks based on file type:
#!/bin/bash
# scripts/smart-post-edit.sh
file="$1"
case "$file" in
*.ts|*.tsx)
npx prettier --write "$file"
npx eslint --fix "$file"
npx tsc --noEmit
;;
*.py)
black "$file"
flake8 "$file"
;;
*.css|*.scss)
npx stylelint --fix "$file"
;;
esac
Error Handling
#!/bin/bash
# scripts/safe-lint.sh
if npm run lint:fix; then
echo "Linting passed"
else
echo "Linting failed - review changes"
# Don't exit with error to allow Claude to continue
fi
Logging
#!/bin/bash
# scripts/logged-hook.sh
log_file=".claude/hook-logs/$(date +%Y%m%d).log"
mkdir -p .claude/hook-logs
{
echo "=== $(date) ==="
echo "Action: $1"
echo "File: $2"
# Run actual commands
npm run lint
echo "Status: $?"
echo ""
} >> "$log_file"
Parallel Execution
Speed up hooks by running in parallel:
#!/bin/bash
# scripts/parallel-checks.sh
# Run in parallel
npm run lint &
npm run typecheck &
npm test -- --onlyChanged &
# Wait for all
wait
echo "All checks complete"
Project-Specific Configurations
React/Next.js Project
{
"hooks": {
"post-edit": "npm run lint:fix && npm run format",
"post-create": "npm run lint:fix"
}
}
Python Project
{
"hooks": {
"post-edit": "black . && isort . && flake8",
"post-create": "black . && isort ."
}
}
Full-Stack Monorepo
{
"hooks": {
"post-edit": "./scripts/smart-lint.sh",
"post-create": "./scripts/smart-lint.sh"
}
}
Where smart-lint.sh detects the package and runs appropriate linters.
Hook Best Practices
1. Keep Hooks Fast
Slow hooks break flow. Aim for <5 seconds.
Too slow:
{
"hooks": {
"post-edit": "npm run full-test-suite"
}
}
Better:
{
"hooks": {
"post-edit": "npm run lint:fix"
}
}
Run full tests manually or on commit.
2. Don't Block on Failure
Let hooks warn but not stop work:
#!/bin/bash
npm run lint:fix || echo "Linting issues - review before commit"
exit 0 # Always succeed
3. Use exit 0 Carefully
If a hook fails, Claude Code may retry or halt. Decide if that's what you want.
4. Avoid Infinite Loops
Don't create hooks that trigger themselves:
{
"hooks": {
"post-edit": "npm run auto-fix-everything"
}
}
If auto-fix-everything edits files, it may trigger more post-edit hooks.
5. Document Your Hooks
Add comments to your config:
{
"_comment": "Hooks configuration",
"hooks": {
"_comment_post-edit": "Format and lint all changed files",
"post-edit": "npm run format && npm run lint:fix"
}
}
Debugging Hooks
Test Manually
Run your hook script directly:
./scripts/post-edit.sh
Add Logging
#!/bin/bash
echo "Hook running at $(date)" >> /tmp/claude-hooks.log
npm run lint:fix
echo "Hook completed with status $?" >> /tmp/claude-hooks.log
Check Exit Codes
Hooks fail silently if they return non-zero. Check your exit codes.
Common Issues
Hook Not Running
- Check file permissions:
chmod +x scripts/hook.sh
- Verify path is correct
- Check Claude Code logs for errors
Hook Failing
- Test script manually
- Check all dependencies are installed
- Verify paths work from project root
Hook Too Slow
- Run heavy tasks asynchronously
- Only lint changed files, not entire project
- Cache where possible
The Bottom Line
Hooks transform Claude Code from a coding assistant into a complete development system. Every edit is automatically formatted, linted, and checked.
Start simple: add a post-edit hook for formatting. Build up from there as you identify repetitive tasks.
The goal: never manually run linter/formatter again.
Want to automate more of your development workflow? Cedar Operations helps teams build efficient, automated processes. Book a consultation →
Related reading: