npm/pnpm Lifecycle Hooks Reference

All npm lifecycle script names in execution order for install, pack and publish.

Reference for npm and pnpm package.json lifecycle hooks — preinstall, install, postinstall, prepare, prepack, prepublishOnly, version — with trigger command and exact execution order, plus a live command filter. It runs free in your browser on Gera Tools, with nothing uploaded.

Last updated Source: Gera Tools

What is the difference between prepare and prepublishOnly?

prepublishOnly runs only on npm publish, never on local installs, so it is ideal for publish-only guards like running tests. prepare runs on publish, on local npm install with no args, and on git-dependency installs, making it the right place for build steps that consumers need.

npm lifecycle hooks in execution order

npm runs a defined set of lifecycle scripts at specific moments during install, pack, publish, and version operations. Knowing the exact sequence — and which command triggers which hook — lets you place build steps, test guards, and release automation in the correct package.json field. Placing a script in the wrong hook means it either runs too early, runs on the wrong command, or silently never runs at all.

The four triggering commands

npm install

preinstall
  → dependency resolution and install
install
postinstall
prepare

prepare runs last, after all dependencies are installed. It is the right place for build steps that need dependencies available (like tsc or vite build) and that consumers of your package also need to trigger when they install it as a git dependency.

npm publish

prepublishOnly
prepack
prepare
  → tarball creation
postpack
publish
postpublish

prepublishOnly is publish-only, so it is the conventional location for tests and lint guards. prepare fires here too, which is why it must be idempotent — it will run on both install and publish.

npm pack (standalone)

prepack
prepare
  → tarball written
postpack

npm version

preversion
  → version bumped in package.json
version
  → git commit created
postversion

postversion is the standard place to run git push --follow-tags to push the version commit and the new tag to the remote.

The pre/post wrapper pattern

For any script named x, npm automatically looks for and runs prex before x and postx after it. This applies to built-in lifecycle names and your own custom scripts:

{
  "scripts": {
    "pretest": "node check-env.js",
    "test": "jest",
    "posttest": "node cleanup.js"
  }
}

Custom scripts like build get prebuild and postbuild for free.

Practical placements

{
  "scripts": {
    "preinstall": "node check-node-version.js",
    "prepare": "tsc -p tsconfig.build.json",
    "prepublishOnly": "npm test && npm run lint",
    "postversion": "git push --follow-tags"
  }
}
  • preinstall — check Node or environment constraints before dependencies are pulled.
  • prepare — compile TypeScript or run a bundler; runs on both install (git deps) and publish.
  • prepublishOnly — the publish gate; never runs during installs, so safe for slow tests.
  • postversion — push after npm version without manual steps.

pnpm differences

pnpm v7 and later ignores preinstall, install, and postinstall scripts of dependencies (not your own package) by default, for security reasons. If a dependency’s postinstall script is genuinely needed (for example to compile a native module), you must allow-list it in .npmrc with unsafe-perm or the allow-run-scripts setting.

Common mistakes

  • Putting tests in prepare instead of prepublishOnlyprepare runs on every npm install, so tests block every install. Move them to prepublishOnly.
  • Forgetting that prepare runs in git-dependency installs — if a downstream project installs your package from a git URL, npm runs prepare in your package. This is intentional: it builds the package so downstream consumers get compiled output, not source.
  • Using postinstall for app setup in a deployed environmentpostinstall fires for every npm install, including fresh CI installs. A heavy postinstall step adds to every cold build. Consider moving one-time setup to an explicit script called by CI instead.
  • Expecting prepublish to run only at publishprepublish fires on npm install too (an old npm 2 legacy). Use prepublishOnly instead for publish-only guards.