Pre-commit hook
Catch an oversized image before it's even committed.
Catching an over-budget image in CI (see Page-weight budget in CI) means it already made it into a commit — and, if the check only warns, possibly into the repo’s history for good. A pre-commit hook catches it before that ever happens.
Plain git hook
.git/hooks/pre-commit (or install via Husky /
pre-commit if the repo already uses one):
#!/usr/bin/env bash
set -euo pipefail
BUDGET="200kb"
staged=$(git diff --cached --name-only --diff-filter=ACM -- '*.jpg' '*.jpeg' '*.png' '*.webp')
[ -z "$staged" ] && exit 0
for file in $staged; do
bytes=$(wc -c < "$file")
# Cheap check first: if it's already under budget, no need to spend a
# real compression attempt on it.
if [ "$bytes" -le 204800 ]; then continue; fi
echo "compressing $file (currently ${bytes} bytes, over the ${BUDGET} budget)..."
if ! npx imagekbreducer compress "$file" --to "$BUDGET" --in-place --quiet; then
echo "error: $file cannot be brought under ${BUDGET} -- commit blocked."
echo "run 'imagekbreducer compress $file --to $BUDGET --accept-closest --in-place' to see how close it gets, or raise the budget for this file."
exit 1
fi
git add "$file"
done
--in-place is deliberate here, not a default worth being casual about: it’s the only flag that
overwrites the working file directly, which is exactly what you want in a pre-commit hook (the
recompressed bytes need to be what actually gets committed) and exactly what you don’t want
anywhere a mistake would be expensive — see Output files for the guard rails
around it, and don’t reach for --in-place in a CI step running against a checkout you don’t
control.
Husky
npx husky init
.husky/pre-commit:
#!/usr/bin/env sh
. "$(dirname "$0")/_/husky.sh"
npx --no-install imagekbreducer --version > /dev/null 2>&1 || npm install --no-save imagekbreducer
sh scripts/check-image-budget.sh
(with the loop above saved as scripts/check-image-budget.sh, chmod +x’d).
What this trades off
A pre-commit hook that recompresses adds latency to every commit that touches an image — usually
well under a second per file for the search this tool runs, but noticeable on a large batch. If
that’s not acceptable, use the check-only variant from
Page-weight budget in CI as a pre-commit hook instead (swap
--in-place for --dry-run and drop the git add), and let CI be the place that actually fixes
the file, or blocks the merge until a human does.