Skip to main content

Asset Pipeline (SCSS/ESBuild/RTL)

warning

Build tools are part of your deployment chain. Do not depend on running Node builds manually on production. Build on a workstation/CI, then deploy artifacts.

Asset Pipeline (SCSS/ESBuild/RTL) Explained

FolderPurposeExample
assets/src/Source SCSS/JSassets/src/app.scss
assets/build/Compiled outputassets/build/app.css
EnqueueLoad build outputswp_enqueue_style()
RTLRight-to-left CSS variantapp-rtl.css (optional)

Why It Matters

  • SCSS keeps styles modular and reduces copy/paste.
  • Bundling JS helps avoid scattered scripts.
  • Build outputs can be versioned and cached safely.

How It Works

You write source files, run a build command, and deploy only the compiled output. WordPress enqueues the build files.

flowchart LR
SRC[assets/src] --> BUILD[Build step]
BUILD --> OUT[assets/build]
OUT --> ENQ[wp_enqueue_*]

Practical Walkthrough

Step 1: Create Source/Build Folders

create-asset-pipeline-folders.sh
cd /var/www/html
mkdir -p wp-content/themes/generatepress-child/assets/src
mkdir -p wp-content/themes/generatepress-child/assets/build

Step 2: Create Minimal Source Files

create-source-files.sh
cat > wp-content/themes/generatepress-child/assets/src/app.scss <<'EOF'
.gp-pipeline-proof {
padding: 16px;
border: 2px solid #1e3a8a;
}
EOF

cat > wp-content/themes/generatepress-child/assets/src/app.js <<'EOF'
console.log('asset pipeline app.js');
EOF

Step 3: Minimal Build Scripts (Example)

wp-content/themes/generatepress-child/package.json
{
"name": "generatepress-child",
"private": true,
"devDependencies": {
"esbuild": "^0.20.0",
"sass": "^1.69.0"
},
"scripts": {
"build:css": "sass assets/src/app.scss assets/build/app.css --no-source-map",
"build:js": "esbuild assets/src/app.js --bundle --minify --outfile=assets/build/app.js",
"build": "npm run build:css && npm run build:js"
}
}

Step 4: Enqueue Build Outputs

wp-content/themes/generatepress-child/inc/enqueue.php
<?php

add_action( 'wp_enqueue_scripts', function() {
$css = get_stylesheet_directory() . '/assets/build/app.css';
$js = get_stylesheet_directory() . '/assets/build/app.js';

if ( file_exists( $css ) ) {
wp_enqueue_style(
'gp-child-build',
get_stylesheet_directory_uri() . '/assets/build/app.css',
array(),
filemtime( $css )
);
}

if ( file_exists( $js ) ) {
wp_enqueue_script(
'gp-child-build',
get_stylesheet_directory_uri() . '/assets/build/app.js',
array(),
filemtime( $js ),
true
);
}
} );

Practical Examples

Example 1: Commit Source, Deploy Build

Keep in GitDeploy to server
assets/src/assets/build/
package.jsoncompiled CSS/JS

Example 2: Cache Busting with filemtime()

When the build output changes, the version changes automatically.

Best Practices

PracticeWhy
Keep build outputs smallPerformance
Prefer build in CIRepeatable deploys
Guard enqueues with file_existsPrevents fatal/404 issues
Document build commandsHelps future maintainers

Troubleshooting

SymptomCauseFix
Build files 404Not deployedEnsure assets/build/ is deployed
CSS changes not visibleCachePurge LSCache and verify version changes
Build failsNode/tooling mismatchPin versions and build in CI

Hands-On

  1. Create assets/src/app.scss and build to assets/build/app.css.
  2. Enqueue assets/build/app.css with filemtime().
  3. Change SCSS, rebuild, and confirm cache-busting works.

Quick Reference

asset-pipeline-cheatsheet.sh
cd /var/www/html/wp-content/themes/generatepress-child
ls assets/src
ls assets/build

What's Next