The scripts block in package.json is the front door to your project — it's how anyone (including future you) knows how to build, test and run it. Most repos barely use it. A little structure here saves everyone time.
The conventional names
Stick to the names people expect so a newcomer can guess them: dev, build, start, test, lint, format. Predictability is the whole point.
{
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "eslint .",
"format": "prettier --write .",
"test": "vitest",
"typecheck": "tsc --noEmit"
}
}pre and post hooks run automatically
npm runs a script named pre<x> before <x> and post<x> after it, automatically. 'prebuild' runs before 'build' with no wiring.
{
"scripts": {
"prebuild": "npm run typecheck && npm run lint",
"build": "next build"
}
}Series vs parallel
{
"scripts": {
"check": "npm run lint && npm run typecheck",
"ci": "npm run check && npm run test"
}
}&& runs in series and stops on the first failure (what you usually want for CI gates). For running things truly in parallel — a dev server plus a watcher — reach for a tiny tool like npm-run-all (run-p) or concurrently rather than backgrounding with &.
Passing arguments
# pass flags through with --
npm run test -- --watch
npm run lint -- --fixUse the conventional names, lean on pre/post hooks for gates, chain with && for CI, and document anything unusual. A good scripts block means 'clone, npm install, npm run dev' just works — the kindest thing you can do for anyone who opens your repo.