
Claude Code hooks: de complete gids (2026)
Een skill kan genegeerd worden. Dat is geen gebrek van skills, het is het hele ontwerp: Claude leest de beschrijving, beslist of de huidige taak past, en laadt de body alleen als het denkt van wel. Meestal klopt die inschatting. Soms niet, en de taak waar dat het meest telt, de codereview vlak voor een merge, de logregel die hoe dan ook moet bestaan, is precies de taak waarbij "waarschijnlijk" niet goed genoeg is.
Hooks zijn de andere helft van Claude Code. Een hook is een shellcommando dat het harnas draait wanneer een specifieke gebeurtenis afgaat, of een skill daar nu aan zou hebben gedacht of niet. Er zit geen modeloordeel tussen de gebeurtenis en het commando. Het draait, elke keer, in volgorde, en de exitcode kan Claude zelfs koudweg stoppen. Als je ooit hebt gewild dat "format dit bestand altijd na een edit" of "laat Claude deze map nooit aanraken" gewoon zo zou zijn, is een hook de tool die voor die zin gebouwd is.
Deze gids behandelt wat hooks zijn, het settings.json-schema erachter, zes recepten die je vandaag kunt plakken, hoe hooks en skills samenwerken, en de faalpatronen die een middag opeten als je niet weet waarnaar je moet zoeken.
Wat een hook eigenlijk is
Claude Code vuurt benoemde events tijdens een sessie: voordat een tool draait, nadat een tool draait, wanneer Claude klaar is met antwoorden, wanneer een notificatie zou verschijnen. Een hook bindt een shellcommando aan een van die events, optioneel gefilterd op specifieke tools. Het harnas draait jouw commando, geeft context mee als JSON op stdin, en leest dan de exitcode om te bepalen wat er verder gebeurt.
De events die je het meest gebruikt:
- PreToolUse — vuurt voordat een tool call wordt uitgevoerd. Een hook hier kan de call helemaal blokkeren.
- PostToolUse — vuurt nadat een tool call klaar is. Goed voor formatteren, testen, of loggen van wat er net gebeurde.
- Stop — vuurt wanneer Claude zijn beurt afrondt en op het punt staat de controle aan jou terug te geven.
- Notification — vuurt wanneer Claude Code een systeemnotificatie zou tonen (permissieverzoeken, idle-prompts).
- UserPromptSubmit — vuurt wanneer je een bericht indient, voordat Claude het ziet.
Dat is het mechanisme. De reden dat het ertoe doet, is de garantie die het je geeft die een skill structureel niet kan geven.
Het mentale model: deterministisch vs discretionair
Dit is het ene idee dat het onthouden waard is als je verder niets van deze gids onthoudt.
Een skill is discretionair. Claude leest de beschrijving ervan bij de start van een sessie, en beslist later, op basis van jouw verzoek, of het moet laden en volgen. Goede skills triggeren betrouwbaar, maar "betrouwbaar" is nog steeds een waarschijnlijkheid, geen garantie. Claude kan een ambigue prompt verkeerd lezen, of twee skills kunnen overlappende beschrijvingen hebben die de match verwarren, een faalpatroon dat we dieper behandelen in onze gids over waarom skills niet triggeren.
Een hook is deterministisch. Hij vraagt Claude niet of hij moet draaien. Hij leest geen beschrijving en beoordeelt geen relevantie. Het harnas ziet het event, en het commando draait, punt uit. Is het event PostToolUse op de Edit-tool, dan draait jouw formatter na elke edit, inclusief die ene die Claude maakte terwijl het aan iets heel anders dacht.
Dat verschil vertaalt zich direct naar wanneer je naar welke grijpt:
| Skill | Hook | |
|---|---|---|
| Draait wanneer | Claude oordeelt dat het relevant is | Elke keer dat het event afgaat |
| Kan worden overgeslagen | Ja, door een slechte match of drukke context | Nee |
| Beste voor | Oordeel, structuur, "hoe je X goed doet" | Handhaving, "X moet altijd gebeuren" |
| Faalmodus | Stille non-trigger | Stille slechte exitcode, of alles blokkeren |
Begint de zin die je afdwingt met "Claude moet altijd..." of "Claude mag nooit...", dan wil je een hook. Begint hij met "als Claude bezig is met X, moet het dat aanpakken als..." dan wil je een skill. Code formatteren na elke edit is een hook; idiomatisch Python schrijven is een skill. Commits naar main blokkeren is een hook; een goed commitbericht structureren is een skill.
Anatomie van een hook in settings.json
Hooks leven onder de hooks-sleutel in .claude/settings.json (projectniveau) of ~/.claude/settings.json (gebruikersniveau), een ander bestand dan CLAUDE.md en het is de moeite waard om ze niet te verwarren: CLAUDE.md is proza dat Claude leest, settings.json is config die het harnas uitvoert. Heb je nog geen van beide opgezet, dan behandelen onze CLAUDE.md-gids en onze volledige setup-walkthrough de rest van de stack waar dit bestand in leeft. Hier is een minimaal maar compleet hooks-voorbeeld, geannoteerd:
{
"hooks": {
// De eventnaam — PreToolUse, PostToolUse, Stop, Notification, enz.
"PostToolUse": [
{
// matcher filtert welke tool calls deze hook activeren.
// Laat het weg (of gebruik "*") om elke tool te matchen.
"matcher": "Edit|Write",
"hooks": [
{
// "command" is momenteel het enige hooktype.
"type": "command",
// Het shellcommando om te draaien. Ontvangt event-JSON op stdin.
"command": "npx prettier --write \"$(echo $CLAUDE_TOOL_INPUT | jq -r .file_path)\"",
// Optioneel: dood het commando als het blijft hangen.
"timeout": 15
}
]
}
]
}
}
Een paar dingen die de moeite waard zijn om te noemen omdat mensen erover struikelen:
Het matcher-veld werkt op de toolnaam, niet op bestandspaden of inhoud. "Edit|Write" matcht de Edit- en Write-tools; "Bash" matcht shellcalls. Moet je filteren op bestandspad of commando-inhoud, doe dat dan binnen je script door de JSON-payload te lezen, niet in de matcher.
Elke event-sleutel bevat een array van matcherblokken, en elk matcherblok bevat een array van hookcommando's, dus je kunt meerdere commando's aan één matcher hangen, of één commando aan meerdere matchers, zonder config te dupliceren.
Het commando ontvangt de event-payload als JSON op stdin: toolnaam, tool-input, en voor PostToolUse, het resultaat van de tool. Een hook die op het specifieke bestand werkt dat wordt bewerkt, leest die JSON in plaats van aan te nemen dat de werkdirectory van de shell het hele verhaal vertelt.
Exitcodes dragen betekenis. Exit 0 betekent "prima, ga door." Een niet-nul exit op een PreToolUse-hook blokkeert de tool call en geeft stderr terug aan Claude als reden. Een niet-nul exit op PostToolUse wordt gewoon gelogd; de tool is al gedraaid, dus er valt niets meer te blokkeren.
Zes recepten die je vandaag kunt gebruiken
Deze zijn bewust smal. Kopieer het blok, pas het commando aan, en bevestig dat het doet wat je verwacht op een weggooibestand voordat je het op echt werk vertrouwt.
1. Auto-formatteren na elke edit
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "cd \"$CLAUDE_PROJECT_DIR\" && npx prettier --write . --ignore-unknown"
}
]
}
]
}
}
Draait Prettier na elke edit of write. Voor grote repo's, wissel het brede . voor een pad afgeleid uit de JSON-input van de hook, zodat je alleen het aangeraakte bestand formatteert.
2. Bewerkingen aan beschermde paden blokkeren
{
"hooks": {
"PreToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "python3 .claude/hooks/guard_paths.py"
}
]
}
]
}
}
guard_paths.py leest het bestandspad uit de stdin-JSON, checkt het tegen een denylist (migrations/, .env, infra/prod/), en sluit af met code 1 met een boodschap op stderr als het matcht. Dit is het dichtst wat Claude Code heeft bij een harde permissiegrens.
3. Tests draaien na broncodewijzigingen
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "cd \"$CLAUDE_PROJECT_DIR\" && npm test -- --onlyChanged --silent"
}
]
}
]
}
}
Geeft Claude direct een signaal wanneer een edit een test breekt, in plaats van te wachten tot jij het bij de review merkt. Houd het testcommando smal (--onlyChanged, een snelle subset) of dit wordt recept zes in de "wanneer niet"-sectie hieronder.
4. Desktopnotificatie wanneer Claude klaar is
{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "command",
"command": "osascript -e 'display notification \"Claude finished\" with title \"Claude Code\"'"
}
]
}
]
}
}
macOS-specifiek (wissel voor notify-send op Linux). Nuttig zodra je langere autonome beurten begint te draaien en niet meer de hele tijd naar de terminal kijkt.
5. Elk bash-commando loggen
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "jq -r '.tool_input.command' >> \"$CLAUDE_PROJECT_DIR/.claude/bash-history.log\""
}
]
}
]
}
}
Een audit trail die niet afhangt van jou die eraan denkt het transcript te checken. Op een gedeelde machine of een repo met een compliance-eis is dit bijna verplicht.
6. Lintpoort voor commit
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": ".claude/hooks/block_bad_commit.sh"
}
]
}
]
}
}
block_bad_commit.sh leest stdin, checkt of het commando een git commit is, en zo ja, draait het eerst je linter, met een niet-nul exit als die faalt. Dat maakt van "lint alsjeblieft voor het committen" een regel waar Claude niet meer omheen kan, in plaats van een verzoek dat het kan vergeten.
GRATIS STARTERSPACK
Zet je hooks op naast je eerste skills? We sturen onze 3 best scorende skills plus de installatiechecklist die we voor elke SkillProof-test draaien. Gratis.
Download het gratis starterspackHooks en skills samen
Ze zijn geen concurrerende tools; de beste setups gebruiken beide waar elk goed in is. Een uitgewerkt voorbeeld: een team dat we testten wilde elke commit geschreven in hun huisstijl, gebiedende wijs, een gescoped prefix, een body die uitlegt waarom, en ze wilden ook commits blokkeren als de diff een databasemigratie raakte zonder bijbehorend rollback-bestand.
Het stijldeel is oordeel. Wat telt als een goede "waarom" varieert per wijziging, en er is geen shellscript dat betrouwbaar goed proza schrijft. Dat is de klus van een skill: zoiets als Git Workflow Coach geladen wanneer Claude op het punt staat te committen, dat de structuur leert en voorbeelden geeft van goede versus luie commitberichten. Claude leest het, past oordeel toe, en schrijft een bericht dat bij het patroon past zonder een templateinvulling te zijn. Draait het team ook strikt red-green-refactor, dan is Test-Driven Development dezelfde soort oordeel-niet-wet-toevoeging: het vormt hoe Claude het werk aanpakt, iets wat een hook niet kan.
De migratieregel is geen oordeel, het is wet: ofwel bestaat het rollback-bestand of niet, en het team wilde niet dat "Claude besloot dat deze er geen nodig had" een optie was. Dat is de PreToolUse-hook uit recept zes, aangepast om te checken op het bijbehorende bestand in plaats van een linter te draaien, die de git commit-call volledig blokkeert als het ontbreekt.
Draai ze samen en je krijgt een goed commitbericht dat ook gegarandeerd de migratiecheck doorstaat, omdat de skill het deel afhandelt dat een brein nodig heeft en de hook het deel dat een muur nodig heeft. Geen van beide vervangt de ander. De skill kan compliance niet garanderen, en een hook die "fix: various changes" op elke commit schrijft, zou nutteloos zijn. Onze beste codingskills-pagina rangschikt de oordeelskant van deze pairing op geteste score, kies je een eerste skill om naast je hooks te draaien.
Hooks debuggen
Hooks falen vaker stil dan luid. Wat meestal breekt:
Quoting. Hookcommando's zijn shellstrings binnen JSON-strings, dus een " die niet ge-escaped is, breekt de JSON-parse voordat je commando ooit draait. Twijfel je, zet dan de echte logica in een scriptbestand en laat het hookcommando dat gewoon aanroepen (bash .claude/hooks/my-hook.sh) in plaats van een complexe one-liner inline te zetten.
Exitcodes die niet betekenen wat je denkt. Een hookscript dat een niet-gerelateerde fout tegenkomt (missende dependency, permission denied) sluit met dezelfde niet-nul code af als een hook die bewust wil blokkeren. Begint een PreToolUse-hook elke tool call te blokkeren en heb je hem niet zo streng geschreven, check dan of het script daadwerkelijk faalt in plaats van oordeelt.
PATH-aannames. Hooks draaien in een shellomgeving die mogelijk niet overeenkomt met je interactieve terminal. Een commando dat prima werkt als je het zelf typt, kan falen binnen een hook omdat nvm, een virtualenv, of een tool geïnstalleerd via een shellplugin niet in PATH staat in die context. Gebruik absolute paden naar binaries, of source de juiste omgeving bovenaan het script.
Stille stdin-aannames. Verwacht je script JSON op stdin en krijgt het dat niet, omdat je het testte door het direct te draaien in plaats van een voorbeeldpayload erin te pipen, dan gedraagt het zich anders onder het harnas dan op je terminal.
Timeouts. Een hook zonder timeout die blijft hangen, hangt de hele beurt op. Zet een expliciete timeout op alles wat het netwerk of een traag subproces aanraakt.
Wanneer je geen hooks moet gebruiken
Hooks zijn goedkoop om te schrijven en makkelijk te overgebruiken. Het faalpatroon is niet dat een hook het verkeerde doet, het is een hook die het juiste te vaak doet. Een PostToolUse-hook die je volledige testsuite draait na elke enkele edit, verandert een wijziging van vijf seconden in een wachttijd van twee minuten, herhaald voor elke edit in een sessie die er twintig maakt.
De vuistregel: kost het commando van een hook meer dan een seconde of twee, versmal dan de matcher, versmal wat het checkt, of verplaats het naar een minder frequent event. Test-bij-elke-edit wordt test-bij-bestandschrijven wordt test-voor-commit naarmate de check duurder wordt. Match de kosten van de hook met hoe vaak het event afgaat, en overweeg of een skill, die alleen context laadt en geen proces draait, een betere fit is voor alles wat niet strikt handhaving is.
Het is ook de moeite waard om geen hook te grijpen om het triggerprobleem van een skill op te lossen. Vuurt een skill niet af wanneer het zou moeten, dan is de fix een betere beschrijving, geen hook, aangezien hooks shellcommando's draaien en geen skill-inhoud kunnen laden. Voor die faalmodus, zie waarom skills niet triggeren.
SKILLPROOF-PAKKET
Hooks pairen met de juiste skills is het grootste deel van een goede Claude Code-setup. De Developer Toolkit bundelt onze hoogst scorende codingskills, vooraf gecheckt op triggerconflicten, zodat de skillhelft van deze pairing al voor je gedaan is.
Haal de Developer Toolkit — $10FAQ
Vertragen hooks elke Claude Code-sessie?
Alleen de events waaraan je ze hangt, en alleen zolang jouw commando duurt. Een hook op PostToolUse voor Edit draait één keer per edit; een snelle formatter is onmerkbaar, een volledige testsuite voel je bij elke edit, wat het geval is dat hierboven behandeld wordt onder wanneer je geen hooks moet gebruiken.
Kan een hook Claude helemaal ergens van weerhouden?
Ja, daar zijn PreToolUse-hooks voor. Sluit af met niet-nul en de tool call wordt geblokkeerd voordat hij draait, met stderr doorgaans teruggegeven aan Claude als reden. Dat is het mechanisme achter recept twee (beschermde paden) en recept zes (lintpoort).
Waar zet ik mijn hooks-config, project- of gebruikersinstellingen?
Projectniveau (.claude/settings.json, gecommit) als het voor iedereen op die codebase moet gelden: formattering, beschermde paden, migratiechecks. Gebruikersniveau (~/.claude/settings.json) voor een persoonlijke voorkeur, zoals de desktopnotificatie in recept vier.
Wat is het verschil tussen een hook en een skill die zegt "formatteer code altijd"?
De hook draait echt altijd. Een skill die Claude vertelt om code altijd te formatteren, is nog steeds een instructie die Claude leest en beslist te volgen; het is een sterke duw, geen garantie, en concurreert met andere dingen in context voor aandacht op elke gegeven beurt. Is "altijd" een vereiste in plaats van een voorkeur, gebruik dan een hook.
Mijn hook draait helemaal niet. Wat moet ik als eerste checken?
Bevestig dat het settingsbestand geldige JSON is (een trailing komma of niet-ge-escapete quote kan het hele hooksblok stilletjes uitschakelen) en dat de eventnaam en matcher precies zo gespeld zijn als verwacht; beide zijn hoofdlettergevoelig. Check daarna of je projectinstellingen bewerkte terwijl de sessie gebruikersinstellingen leest, of andersom.
★ 9.6/10 × 3
Het gratis starterspakket
De 3 skills met onze hoogste testscores plus de installatiechecklist — de setup die wij op een verse machine zouden zetten. Gratis, per e-mail.