Commit composer.json, composer.lock, package.json and package-lock.json, plus your own theme and plugin code. Leave out the vendor folder and node_modules, because both are rebuilt from the lockfiles. Whether to commit built assets depends on how your host deploys, and on whether it can run a build step at all.
Many agency projects mix both package managers: Composer for PHP libraries and plugins, npm for the front end build. The rules differ slightly, and getting them wrong means different people running different versions.
What each tool says
Composer's documentation says to commit composer.lock, because everyone setting up the project then gets the exact same versions: your CI server, production machines and teammates. It adds that for libraries the lock file is not needed, but a client site is an application.
The npm documentation describes npm ci as intended for automated environments and says it needs an existing package-lock.json or npm-shrinkwrap.json, exits with an error if the lockfile does not match package.json, and never writes to the lockfile.
What to commit
| Item | Commit? | Reason |
|---|---|---|
| composer.json | Yes | Declares PHP dependencies |
| composer.lock | Yes | Pins exact versions |
| package.json | Yes | Declares build dependencies |
| package-lock.json | Yes | Pins exact versions |
| vendor directory | Usually no | Rebuilt by composer install |
| node_modules | No | Rebuilt by npm ci |
| Compiled CSS and JS | Depends | Commit if the host cannot run a build |
| wp-config.php with secrets | No | Keep secrets out of the repository |
| Uploads and media | No | Content, not code |
Use the install commands that respect locks
In CI and deploy scripts, use composer install, not composer update, so the lock file is followed. Use npm ci, not npm install, so the lockfile is followed and a mismatch fails loudly. Update dependencies deliberately, in their own pull request, so the diff is reviewable.
Keep the lockfile diff honest
When a lockfile changes, read it. New packages, changed sources and large version jumps should be explained in the pull request. This is how unexpected packages get noticed before they reach a client site.
Secrets stay out
The hardening guide recommends making sure only you and the web server can read wp-config.php, since it holds database credentials. Keep real credentials out of version control and inject them at deploy time.
When a host cannot run builds
Some hosts only accept uploaded files. In that case, build in CI and deploy the output, rather than building by hand on a laptop, so the artifact comes from locked inputs every time. For the theme build question in particular, see our post on whether node_modules reaches production.
Try it yourself
Click through it at your own pace, or open it full screen. Look at how direct and indirect dependencies are listed with versions; the lockfiles are where that data comes from.
Safeguard reads manifests and lockfiles to build an SBOM, and the free scan is a quick way to try it on a project.
Quick check
Should I commit the vendor directory?
Usually not. Commit composer.lock and rebuild vendor in CI, unless your host cannot run Composer, in which case build in CI and deploy the result.
Why npm ci instead of npm install?
npm ci installs exactly what the lockfile says and fails if it does not match package.json, which makes builds repeatable.
Sources
Last reviewed: 19 October 2026.