Most WordPress deployment advice stops at push your code and it goes live.
But a modern theme is rarely just PHP templates — it ships Sass that needs compiling, JavaScript that needs bundling, and Composer or npm dependencies that need installing. Where those steps run decides whether your production wp-content/themes folder stays clean or turns into a mess of committed build artifacts and merge conflicts.
This guide is about that build step specifically: how to compile your WordPress theme's assets during deployment so only minified, versioned files reach the server. If you still need the foundation — connecting a repo, wiring up a server, and moving a site off FTP — start with our companion walkthrough on how to deploy WordPress from local to production with Git, then come back here to make the build step do the heavy lifting.
The problem with committing compiled assets
The instinct for many WordPress developers is to run npm run build locally, commit the generated style.css, bundle.js, and vendor/ directory, and push everything together. It works — until it doesn't:
- Merge conflicts on generated files. Two developers build the same Sass locally and Git sees two different minified outputs. You end up resolving conflicts in machine-generated CSS nobody can read.
- A bloated repository. Compiled bundles, source maps, and a committed
vendor/folder inflate clone times and pollute your diff history with noise on every change. Works on my machine
drift. Your local Node or PHP version compiles slightly different output than a teammate's, so the asset that ships depends on whose laptop ran the build.
The fix is to treat compiled assets as build artifacts, not source. Your repository holds the source — .scss partials, ES modules, composer.json, package.json — and the compiled output is generated fresh, in a controlled environment, at deploy time. This is exactly what a build pipeline is for.
Why the build step belongs in deployment, not on the server
There are really only three places a theme's assets can be compiled, and two of them cause problems:
- In the repo (committed): the anti-pattern above — conflicts, bloat, drift.
- On the production server (build-on-server): requires Node, Composer, and build toolchains installed on your web host, ties up production resources during the build, and leaves a half-built theme live if a compile fails mid-run.
- In a dedicated build step before deploy: the source is checked out into a clean build environment, assets are compiled once, and only the finished output is transferred to the server.
Option three is what DeployHQ's Build Pipeline does. Your production server never needs Node or Composer installed, the build runs on isolated infrastructure, and a failed compile stops the release before anything touches wp-content/themes.
Anatomy of a WordPress theme build step
A complete theme build usually chains four stages. Here's what each one looks like as a build command.
1. Install PHP dependencies with Composer
If your theme uses Composer for autoloading, Timber, or any packagist library, install dependencies without dev tooling and with an optimised autoloader:
composer install --no-dev --optimize-autoloader
--no-dev keeps PHPUnit and other development-only packages out of the release, and --optimize-autoloader generates a classmap so WordPress isn't scanning the filesystem on every request. Our deeper walkthrough on running Composer builds for PHP and WordPress projects covers autoloader and caching nuances.
2. Install JavaScript dependencies
Use npm ci rather than npm install in a build step — it installs exactly what's in your lockfile and is faster and more reproducible for automated builds:
npm ci
If you need the mechanics of wiring Node into your pipeline, see our guide on using Node.js and npm in the DeployHQ Build Pipeline.
3. Compile Sass and bundle JavaScript
Run your theme's build script — whether that's Sass compilation, a Webpack bundle, or a Vite build:
# Compile Sass/SCSS and bundle JS via your theme's build script
npm run build
For a production theme you want minified CSS, tree-shaken JS, and no inline source maps shipping to visitors. Frontend build and bundling trade-offs — when to split bundles, how to keep payloads small — are covered in our notes on frontend deployment builds and bundles.
4. Version and cache-bust the output
Fingerprinted filenames (main.a1b2c3.css) let browsers cache aggressively while guaranteeing users get the newest asset after a deploy. If your bundler emits hashed filenames, make sure your theme enqueues them dynamically — read the manifest your build writes rather than hard-coding style.css in functions.php.
Once these commands are in place, deploying a theme change is just git push. If you haven't wired that trigger yet, DeployHQ can fire a release on every push with automatic deployments. Start a free DeployHQ project and add these commands to your Build Pipeline to see a full compile-and-ship run end to end.
Setting up the build step in DeployHQ
In your project's Build Pipeline area, add the commands above in order — Composer, npm ci, then npm run build. DeployHQ checks out your repository into a clean build environment, runs the commands, and transfers the result.
Two settings make this fast and safe for theme work:
- Build cache directives. Cache
vendor/andnode_modules/between builds so you're not re-downloading every dependency on each deploy. Only changed packages are fetched, cutting build time dramatically on theme-heavy projects. - Only-changed-files transfer. DeployHQ uses Atomic Deployments so a release is assembled fully before it goes live — visitors never hit a half-compiled theme, and only the assets that actually changed are uploaded.
Set your deployment path to the theme directory — for example /wp-content/themes/your-theme — so the pipeline only touches your theme and leaves core and plugins alone.
Keeping build artifacts out of Git
Because the pipeline generates assets, they should not be tracked. A typical theme .gitignore excludes the output while keeping the source:
/node_modules
/vendor
/assets/dist
*.map
Your source .scss, JS modules, and composer.json stay versioned; the compiled assets/dist is rebuilt on every deploy.
Gotchas worth knowing before you ship
These are the issues that most often trip up a WordPress theme build in a pipeline:
- Pin your Node and PHP versions. A build server defaulting to a newer Node major can break a Webpack config or an
node-sassbinary. Pin the version explicitly so builds are reproducible; our note on private repository dependencies also covers authenticating private npm and Composer packages a theme may rely on. - Don't ship dev source maps. Inline source maps expose your unminified code and bloat asset size. Build them for staging, strip them for production with a production build flag.
- Watch memory on large bundles. A big Webpack or Vite build can exhaust the default memory on a build worker. If a build dies mysteriously, raise the Node heap limit in your build command before assuming the config is wrong.
- Store secrets outside the repo. A theme rarely needs
wp-config.php, but if any credential or API key is involved, keep it out of Git and inject it as a protected configuration file that's written to the server on each deploy — never commit it. - Plan asset rollback. If a bad bundle ships, one-click rollback restores the previous release's files instantly. Pair it with atomic symlink-style releases so reverting is a pointer swap, not a re-upload.
If you manage themes across several client sites, standardising this build step once and reusing it is a big win — DeployHQ for developers is built around exactly that kind of repeatable, multi-project workflow.
Frequently Asked Questions
Should I commit compiled theme assets to Git?
No. Commit the source — .scss, JS modules, composer.json, package.json — and compile the output in a build step at deploy time. Committing generated CSS and JS causes merge conflicts and repo bloat, and ties the shipped asset to whoever ran the build locally.
Do I need Node and Composer installed on my WordPress server?
No — that's the point of building before deploy. The compile runs in the pipeline's build environment, and only the finished assets are transferred, so your web host stays lean and a failed build never reaches production.
How do I compile Sass during deployment?
Add your Sass command (usually via npm run build) to the Build Pipeline after npm ci. The pipeline compiles it in a clean environment and uploads the resulting CSS with the rest of the release.
npm install or npm ci in a build step?
Use npm ci. It installs strictly from your lockfile, so the build is reproducible and doesn't silently pull newer dependency versions than you tested against locally.
How do I roll back a broken asset build?
Use one-click rollback to restore the previous release's files immediately. Because releases are assembled atomically, the previous compiled assets are still intact and reverting is instant.
Getting the build step right is what separates a WordPress theme deploy that quietly ships clean, versioned assets from one that fights merge conflicts and half-built releases. Set the compile commands once, cache your dependencies, and let the pipeline handle Sass, bundling, and Composer on every push.
Questions about building your theme assets in a pipeline? Email us at support@deployhq.com or reach out on X/Twitter @deployhq — we're happy to help you get your WordPress theme build dialled in.