A Node.js project’s .gitignore covers node_modules (always), build outputs (per-framework), local environment files, and tool caches. The list above is a known-good set; this page explains each entry and the per-framework variants.
node_modules
node_modules/
The single non-negotiable entry. node_modules is the directory npm install (or yarn, pnpm) creates to hold your project’s dependencies. It can easily reach gigabytes, contains platform-specific compiled binaries, and is reproducible from package.json + the lockfile.
Committing node_modules is rarely useful even in offline-environment scenarios — most modern setups use a registry mirror or npm ci against a lockfile.
Lockfiles — keep them
# Don't ignore these:
# package-lock.json (npm)
# yarn.lock (yarn)
# pnpm-lock.yaml (pnpm)
Lockfiles are the mechanism for reproducible installs. npm install without a lockfile resolves versions fresh each time and may produce different node_modules on different machines. With a lockfile, npm ci produces the exact same tree every time.
Always commit the lockfile for your package manager. Don’t commit lockfiles for package managers you’re not using (no yarn.lock if you use npm).
Build outputs
dist/
build/
out/
Tools like webpack, esbuild, Rollup, tsc emit compiled output here. Regenerated by npm run build. Never commit.
Per-framework caches
# Next.js
.next/
out/ # next export output
# Vite
.vite/
.cache/
# Nuxt
.nuxt/
.output/
# Gatsby
.cache/
public/ # ⚠️ Gatsby-specific; "public" is the source dir for some other frameworks
# Astro
.astro/
dist/
# SvelteKit
.svelte-kit/
# Parcel
.parcel-cache/
# Storybook
storybook-static/
A common mistake when starting from a generic .gitignore: missing the framework-specific entries means you commit a 100 MB .next/ directory after the first build. Always check that the ignore list matches the framework you’re using.
The public/ confusion is the trickiest: Gatsby’s public/ is a build output; Next.js’s, Astro’s, Vite’s public/ are source directories. Don’t ignore public/ blindly.
Test, lint, and coverage caches
coverage/
.nyc_output/
.eslintcache
.stylelintcache
.tsbuildinfo
Each tool’s cache. Regenerated on demand.
Environment files
.env
.env.local
.env.*.local
Same as Python — .env typically holds secrets. Never commit. Commit a .env.example documenting the variables.
The .env.*.local pattern handles framework-specific local-only files like .env.development.local and .env.production.local (Next.js, Create React App).
TypeScript
*.tsbuildinfo
.tscompiled/
TypeScript’s incremental build files. Regenerable; useful for caching but not for committing.
Logs and temp files
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
.pnp.*
.yarn/cache
.yarn/unplugged
.yarn/build-state.yml
.yarn/install-state.gz
npm-debug.log is generated when an npm command crashes. The Yarn entries handle Yarn 2+ (Plug’n’Play) — Yarn 2’s .yarn/ directory is mostly cacheable artifacts.
Vendor / generated docs
docs/api/ # if you generate API docs
typings/ # if your build produces .d.ts files
Project-specific. Add as needed.
OS and editor
.DS_Store
Thumbs.db
.idea/
.vscode/ # contentious — see [gitignore generator](/tools/gitignore-generator/)
*.swp
Same as the general defaults.
Common pitfalls
- Ignoring lockfiles. Sometimes projects start with
package-lock.jsonignored “because it’s noisy in diffs.” This breaks reproducibility. Commit the lockfile. - Forgetting framework-specific dirs.
.next/is a few hundred MB after a single build. Add it before the first build. - Ignoring
*.log. Sometimes useful (CI log files in dev), but be careful — it also ignores intentional logs your application might write to. Scope to specific log files. - Committing
node_modulesonce. Once tracked,.gitignoredoesn’t untrack.git rm -rf --cached node_modulesthen commit. - Ignoring
dist/when also publishing it. If your repo publishes thedist/to npm, you need it locally for testing but ignored from Git. Use.npmignoreorpackage.jsonfilesfield instead — they’re separate from.gitignore.
For other stacks, see Python, Java, Go, Rust, .NET, Ruby, or the general .gitignore generator.