WordPress development work for agencies can be competitive at the best of times. It requires supreme efficiency and consistency across multiple client projects.

Regardless of how seasoned you are as a developer, managing an entire portfolio of custom themes and plugins still needs perpetual effort when it comes to workflow streamlining. Enter wp-scripts: a powerful suite of utilities that can revolutionize how your agency approaches WordPress development.

This comprehensive guide looks at the capabilities of wp-scripts, and explores techniques for your build processes. Throughout, it will cover optimized compilation and bundling, automated linting, unit testing, and much more — all of it will speak to you if you juggle multiple high-stakes WordPress projects.

The concept of a ‘build process’

Before we look at the specifics of wp-scripts, let’s understand the broader concept of your web development build process. This consists of a series of automated tasks to help take your source code into a production-ready app or website.

For instance, there are many jobs that benefit from automation in this way:

  • Compiling modern JavaScript into browser-compatible code.
  • Transpiling CSS preprocessor languages (Sass, for example) into standard CSS.
  • Minifying and optimizing assets such as JavaScript, CSS, and media.
  • Running linters to catch potential errors and enforce coding standards.
  • Executing unit tests to ensure better code functionality.

These are good aspects to automate for any development workflow, but for agencies, the process is just as crucial. For starters, you can maintain consistency across multiple projects (and your team).

You can also develop and deploy through quicker cycles and maintain all of your projects by leveraging that consistency— even the most complex ones. For the end-user, the optimized performance you gain will trickle down to their overall experience.

Typically, your agency may ‘cobble together’ custom build processes using tools such as Gulp, Grunt, or even manual processes. However, these approaches can often lead to inconsistencies between projects, not to mention a significant maintenance overhead.

wp-scripts: a workflow-changer for WordPress development within an agency

In the context of WordPress, a build process can also offer significant streamlining for theme and plugin development. It lets you use modern tools and practices and ensure platform compatibility at the same time.

The @wordpress/scripts package — wp-scripts throughout this post — is a collection of configuration files and scripts that helps you simplify the build process for WordPress projects.

A code snippet displaying the scripts section of a package.json file for a WordPress project. It lists various npm scripts for tasks such as building, checking engines and licenses, formatting, linting (for CSS, JavaScript, Markdown docs, and package.json), updating packages, creating plugin zip files, starting the project, and running tests (both end-to-end and unit tests). All the scripts use wp-scripts as the base command.
Part of a wp-scripts reference for the various scripts a project will use.

The Make WordPress Core team develops and maintains the package, which is integral to the Block and Site Editors. Best of all, you can use it for custom theme and plugin development, too.

For approaching WordPress development at scale within an agency, wp-scripts will be a central part of the workflow. It’s more than a simple build tool; it’s a comprehensive solution for modern WordPress projects that aligns with the need for a sophisticated development workflow.

The key functionality of wp-scripts

As more modern JavaScript practices bleed into the WordPress ecosystem, we need more standardized build tools to accommodate them. A unified build toolset in the form of wp-scripts benefits the entire WordPress development ecosystem.

As such, wp-scripts offers a range of functionality that makes WordPress development more efficient:

  • ‘Zero-config’ setup. You can begin without the need for complex webpack configurations.
  • Modern JavaScript support. Your ES6 code will transpile for browser compatibility, and give you greater confidence in its accuracy.
  • Built-in CSS processing. If you use CSS preprocessors such as Sass, you get out of the box support.
  • Code quality tools. The package integrates both ESLint and Prettier for consistent code style and quality.
  • Testing utilities. You have Jest available within the package for unit testing and easy execution.
  • Hot reloading. If you have the ability to reload your changes live, this can speed up your development time.

Combined, wp-scripts offers many key advantages for agencies that manage multiple WordPress projects. For instance, you can standardize your development environment across every project and replicate the build process across any new projects, too. The package will let you centralize your build tool dependencies, which makes updates and security patches more manageable.

Overall, you can worry less about compatibility issues, reduce your setup time, and eradicate many of the typical errors you make throughout the less streamlined build process.

Comparing wp-scripts to a typical WordPress development process

Typical WordPress development often involves using manual enqueuing for scripts and styles. In addition, you’ll likely write vanilla JavaScript or jQuery, and rely on third-party build tools — or no build process at all.

In contrast, wp-scripts provides a modern, integrated approach in almost every area:

Element Typical development wp-scripts
JavaScript Often vanilla JavaScript or jQuery ES6 and React support
CSS Direct CSS or basic preprocessors Support for Sass and PostCSS processing
Build process A manual or custom setup using Gulp or Grunt Zero-configuration setup using webpack, integrated into the package.
Code quality Manual linting or separate tools integrated with your code editor ESLint and Prettier are built into wp-scripts
Unit testing If it’s not an overlooked step, there is usually a separate setup The package integrates Jest testing

On the whole, wp-scripts offers greater flexibility thanks to its integration with tools you may not already use. For example, the effort of setting up PostCSS, webpack, or Jest could be something you skip over due to time constraints.

How to set up your development environment to incorporate wp-scripts

Using wp-scripts has its own requirements, but you likely already use those tools. If you need to, install Node.js and npm along with a local WordPress development environment. DevKinsta will be a fine solution, thanks to running on Docker and supporting Kinsta’s staging environments.

The DevKinsta interface displaying site information for a WordPress website. It includes the site type, database type, webserver, and PHP version. The top of the interface shows buttons for Open site, Sync, Database manager, and WP Admin. A small preview of the website is shown on the left-hand side. The bottom of the interface has a Site status section with a Stop site button.

The main DevKinsta interface.

If you already use the create-block package to develop WordPress Block plugins, this installs wp-scripts alongside its other assets. From here, you can begin to set up a package development project.

Setting up a new WordPress project with wp-scripts

The work you undertake will be within the wp-content directory of your WordPress installation. The specific subdirectory will relate to the type of project you create: wp-content/themes for themes and wp-content/plugins for plugins!

Regardless, your project folder should include a number of files and directories:

  • A package.json file.
  • A build directory.
  • An src directory that also includes an index.js file.

To create a package.json file, navigate to your project directory using your Terminal or command line app. Running the npm init command will take you through an interactive setup process, and your ‘entry point’ should be build/index.js:

A terminal window with a dark background showing the output of an npm init command for a WordPress theme project. The text explains that this utility will guide the user through creating a package.json file, covering common items and suggesting sensible defaults. It provides instructions on how to install packages and save them as dependencies. The terminal shows the initial setup details, including the package name, version, description, and entry point.
A partial run through of the npm init process, showing the entry point value prompt.

Next, install wp-scripts as a development dependency:

npm install @wordpress/scripts --save-dev

You should see a couple of auto-generated directories and files, too: node_modules, and package-lock.json. Regardless, you now need to reference the predefined scripts within the package.json file:

"scripts": {
  "build": "wp-scripts build",
  "start": "wp-scripts start",
}

You will likely return to this file often to add further scripts as and when needed. For example:

…

"lint:js": "wp-scripts lint-js",
"lint:css": "wp-scripts lint-style",
"lint:pkg-json": "wp-scripts lint-pkg-json",
"test": "wp-scripts test-unit-js"
…

You may also need to enqueue your theme or plugin assets here and then save your changes.

Understanding and using webpack with wp-scripts

For bundling assets under the hood, wp-scripts uses webpack. You don’t need to configure this, although understanding its role can help you leverage wp-scripts in a more effective way. Webpack has a lot of responsibilities when it comes your setup:

  • It helps to resolve dependencies between your JavaScript modules.
  • You can transpile modern JavaScript to browser-compatible code.
  • It will help process and optimize your styles.
  • You’re able to generate source maps to debug easier.
  • It can help you create production-ready, minified bundles.

You have a default webpack configuration within wp-scripts already. This works well for most WordPress projects. In some cases, though, you might need to create custom configurations.

Advanced webpack configuration for agency setups

While the default webpack config is ideal for the majority of development projects, there are times when you need to create a configuration for your specific needs. For example, you may deal with complex theme structures or unique plugin architectures. This is where a webpack.config.js file in your project root will come in handy:

const defaultConfig = require("@wordpress/scripts/config/webpack.config");

const path = require('path');


module.exports = {
  ...defaultConfig,
  entry: {
    main: path.resolve(__dirname, 'src/js/main.js'),
    admin: path.resolve(__dirname, 'src/js/admin.js'),
    // Add more entry points as needed
  },
  output: {
    filename: '[name].js',
    path: path.resolve(__dirname, 'dist'),
  },
  // Add custom loaders or plugins here
};

This configuration allows for multiple entry points, which is particularly useful for themes or plugins requiring separate scripts for different parts of the WordPress admin or frontend. As such, you can extend your default configuration and maintain the benefits of wp-scripts.

The basics of using wp-scripts

With a suitable development environment and the right file and folder structure in place, you can start to use wp-scripts. There are a few basic and core commands that will make up most of your time.

The start command watches your files for changes, recompiles assets on the fly, and provides hot reloading for a smoother development experience:

npm run start

You use this at the beginning of a project to start a development server, although it won’t optimize the compiled code within your build/index.js file.

When you need to deploy your project, the build command will compile your assets for production:

npm run build

Once you run this, it performs a few tasks. For example, it transpiles your JavaScript, compiles your Sass and SCSS to CSS, minifies all of your assets, and generates source maps. At the end, it will output everything to the build/index.js file. The build process also creates a build/index.asset.php file for cache busting.

The wp-scripts package also provides several linting commands to help you maintain high code quality:

  • npm run lint:js. You use this to lint your JavaScript files.
  • npm run lint:css. This lints your CSS or Sass files.
  • npm run lint:pkg-json. This validates your package.json file.

For unit testing, you simply call npm run test, which uses Jest to execute your test suite.

Exploring the core utilities in wp-scripts

Basic build tasks can take you a long time, and the typical commands will require a lot of automation. However, wp-scripts provides a suite of sophisticated utilities that will cater to the complex needs of your WordPress development:

  • Advanced compilation. With optimized configurations, you can transpile modern JavaScript — including ECMAScript Modules (ESM) and Javascript XML (JSX) — and compile Sass.
  • Intelligent bundling. You can leverage webpack’s code splitting and ‘tree shaking’ to optimize your asset delivery.
  • Comprehensive linting. You can enforce coding standards across JavaScript, CSS, and package.json files.
  • Integrated testing. With Jest, you can run unit tests and coverage reports.
  • Development server. You can utilize hot reloading for rapid development across multiple projects.

You can expand the default functionality of each to leverage a more customized WordPress development process, regardless of whether you work on themes or plugins.

Handling JSX and Modern JavaScript

Many WordPress projects work with modern JavaScript elements such as React components, arrow functions, destructuring, async/await, and more. Even aspects of the core codebase, such as the Block Editor, use modern JavaScript to build its functionality.

However, browsers don’t natively understand these advanced syntaxes, so more work is necessary to transpile and compile them.

JSX lets you write HTML-like code within your JavaScript, which makes it easier to describe what your interfaces and other elements should look like. This can improve readability and maintainability, for starters. You can also access powerful React components for creating dynamic user interfaces (UIs).

wp-scripts uses the Babel JavaScript compiler to transpile your modern JavaScript and JSX into code that browsers can understand. It handles all of the complex and necessary configuration, which lets you focus on writing code.

You leverage this through your src/index.js file. Check out this small example of how you might implement JSX and modern JavaScript using wp-scripts:

import { render } from '@wordpress/element';

// Modern JavaScript feature: Arrow function
const HelloWorld = () => {
    // Modern JavaScript feature: Template literal
    const greeting = `Hello, ${wp.data.select('core/editor').getCurrentPost().title}!`;    

    // JSX
    return (
        <div className="hello-world">
            <h1>{greeting}</h1>
            <p>This is a React component in WordPress!</p>
        </div>
    );
};

// Modern JavaScript feature: Optional chaining
const rootElement = document.getElementById('hello-world-root');
if (rootElement?.innerHTML) {
    render(<HelloWorld />, rootElement);
}

You also have to enqueue the script within your theme’s functions.php file, or within your main plugin file:

function enqueue_hello_world_script() {
    wp_enqueue_script(
        'hello-world-script',
        get_template_directory_uri() . '/build/index.js',
        ['wp-element'],
        filemtime(get_template_directory() . '/build/index.js'),
        true
    );
}

add_action('wp_enqueue_scripts', 'enqueue_hello_world_script');

Once you run the npx wp-scripts build command, wp-scripts will transpile your modern JavaScript and JSX to create a browser-compatible build/index.js file.

Code quality checks and sophisticated linting

The wp-scripts package includes several tools to help you maintain quality code and enforce a consistent style across your projects: Along with ESLint and Prettier, you can also access stylelint for CSS and SCSS linting.

The first step is to add the linting scripts to your package.json file:

"scripts": {
  "lint:js": "wp-scripts lint-js",
  "lint:css": "wp-scripts lint-style",
  "lint": "npm run lint:js && npm run lint:css"
}

Next, create custom configuration files in your project root directory. For example, ESLint requires you to work within the .eslintrc.js file. This example (in part) enforces using template literals for strings in your JavaScript code:

…
module.exports = {
  extends: [
    'plugin:@wordpress/eslint-plugin/recommended',
  ],
  rules: {
    // Custom rules here
    'no-console': 'error',
    'react-hooks/exhaustive-deps': 'warn',
    'prefer-template': 'error',
  },
};
…

For stylelint, you edit the .stylelintrc.js file:

…
module.exports = {
  extends: [
    '@wordpress/stylelint-config/scss',
  ],
  rules: {
    // Custom rules here
    'selector-class-pattern': '^[a-z]+(-[a-z]+)*$',
    'max-nesting-depth': 3,
  },
};
…

If you maintain a large codebase across multiple projects, having a consistent code style is crucial. This way, you can extend the default ESLint and stylelint configurations to enforce your agency’s coding standards.

From here, you can run npm run lint to set this process in motion, extended with the specific linter type. For example, if you have code that states some typical concatenation…

const name = "World";
console.log("Hello " + name + "!");

…running npm run lint:js will flag an error and suggest you use a template literal instead:

const name = "World";
console.log(`Hello ${name}!`);

This is an invaluable way to lint your WordPress theme or plugin code and gives you the scope to tailor the rulesets to your specific needs and standards.

Unit testing

Unit testing is crucial to ensure reliability and maintainability within your codebase. The tools wp-scripts uses as its testing framework is Jest.

When you run the test command, Jest looks for files with .test.js or .spec.js extensions, or for files in a __tests__ directory. It then runs the tests defined in these files and reports the results.

You first need to reference the test script within your package.json file:

…
"scripts": {
  "test": "wp-scripts test-unit-js"
}
…

If you need to, create a file such as src/utils.js:

…
export function capitalizeString(str) {
  return str.charAt(0).toUpperCase() + str.slice(1);
}

export function sum(a, b) {
  return a + b;
}
…

From here, create a test file, such as src/__tests__/utils.test.js:

import { capitalizeString, sum } from '../utils';

describe('capitalizeString', () => {
  it('capitalizes the first letter of a string', () => {
    expect(capitalizeString('hello')).toBe('Hello');
  });
  it('returns an empty string if given an empty string', () => {
    expect(capitalizeString('')).toBe('');
  });
});

describe('sum', () => {
  it('adds two numbers correctly', () => {
    expect(sum(2, 3)).toBe(5);
  });
  it('handles negative numbers', () => {
    expect(sum(-1, 1)).toBe(0);
  });
});

Once you run the npm run test command, wp-scripts will automatically find and execute all files with the .test.js extension. You can also extend the default Jest configuration to support any advanced testing needs, such as test coverage:

// jest.config.js
const defaultConfig = require('@wordpress/scripts/config/jest-unit.config');
module.exports = {
  ...defaultConfig,
  setupFilesAfterEnv: ['<rootDir>/tests/setupTests.js'],
  collectCoverageFrom: [
    'src/**/*.js',
    '!src/tests/**/*.js',
    '!src/vendor/**/*.js',
  ],
  coverageThreshold: {
    global: {
      branches: 80,
      functions: 80,
      lines: 80,
      statements: 80,
    },
  },
};

This configuration adds a custom setup file, specifies files to include in coverage reports, and sets coverage thresholds to ensure comprehensive test coverage across your projects. When you run these tests, Jest will provide output that shows any passing and failing tests.

Expanding the capabilities of your unit testing in this way can offer a significant enhancement to the quality and reliability of your WordPress themes and plugins and streamline your whole development process.

How to integrate wp-scripts into your workflows

The scope of using wp-scripts is as wide as you need it to be. To illustrate this, let’s review some quick-fire approaches you could take in using wp-scripts to automate typical tasks.

Creating reusable project templates

You’ll likely need to start new projects frequently — maybe even every day. Creating a custom project template through preconfiguring wp-scripts preconfigured can save you a lot of setup time.

You can start with a base project: a new WordPress theme or plugin that includes your wp-scripts setup:

mkdir my-agency-base-theme
cd my-agency-base-theme
npm init -y
npm install --save-dev @wordpress/scripts

Next, set up the project structure, and create the necessary directories and files:

mkdir src build
touch src/index.js src/style.scss
touch functions.php style.css

At this point, you configure wp-scripts and update the package.json file with relevant commands:

{
  "scripts": {
    "build": "wp-scripts build",
    "start": "wp-scripts start",
    "lint:js": "wp-scripts lint-js",
    "lint:css": "wp-scripts lint-style",
    "test": "wp-scripts test-unit-js"
  }
}

You can expand upon this to create configurations for webpack, ESLint, and stylelint.

To make this a reusable template that is simple to access, a GitHub repo is ideal. For instance, consider a remote origin of https://github.com/your-agency/theme-template.git.

When you begin a new project, you can run a straightforward command:

npx create-wordpress-theme@latest my-new-theme --template your-agency/theme-template

This will clone your template repo and set up a new theme with your predefined wp-scripts configuration.

You can customize the template further by adding agency-specific boilerplate code, such as commonly used functions or components. It’s important to keep this template repository current, using the latest wp-scripts version and implementing any workflow improvements you decide upon.

Version control and collaboration

You can do more when it comes to wp-scripts and working with version control. Often though, you should implement some typical practices to ensure you keep code quality high:

  • Include package.json and package-lock.json in version control. This ensures all team members will use the same dependencies.
  • Make sure you include build artifacts such as /build and /node_modules within your .gitignore file.
  • Ensure you reference all of the scripts you need within your package.json file before you commit it.
  • Consider using a .nvmrc file to specify the correct Node.js version for your project.

You might choose to implement pre-commit hooks through tools such as Husky. This is a great way to run a linter before each commit, as just one example:

…
"husky": {
    "hooks": {
        "pre-commit": "npm run lint:js && npm run lint:css"
    }
}
…

This way, you can ensure you lint and run tests automatically before commits and pushes. It’s another way you can maintain code quality across your team.

Continuous Integration and Continuous Deployment (CI/CD)

Integrating wp-scripts into your CI/CD pipeline can streamline your deployment process for both themes and plugins. GitHub Actions is just one way to integrate this into your wp-scripts configuration.

The first step is to create a dedicated workflow file within a workflows directory of your repo:

name: CI/CD

on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]
jobs:
  build-and-test:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v2
    - name: Use Node.js
      uses: actions/setup-node@v2
      with:
        node-version: '14'
    - name: Cache dependencies
      uses: actions/cache@v2
      with:
        path: ~/.npm
        key: ${{ runner.OS }}-node-${{ hashFiles('**/package-lock.json') }}
    - name: Install dependencies
      run: npm ci
    - name: Run linters
      run: |
        npm run lint:js
        npm run lint:css
    - name: Run tests
      run: npm run test
    - name: Build
      run: npm run build
    - name: Deploy
      if: github.ref == 'refs/heads/main'
      run: |
        # Add your deployment script here
        # For example, using rsync to a remote server:
        # rsync -avzc --delete ./build/ [email protected]:/path/to/wp-content/themes/your-theme/

The deployment step will vary based on your host. You might use rsync, integrate with services like DeployHQ or Buddy, or go for a simple cURL integration. If your deployment requires passwords or keys, you should add them as secrets to your GitHub repository settings.

This workflow runs linters, tests, and builds your project on every push and pull request. Best of all, it deploys only when you push changes to the main branch.

Creating a custom command line interface (CLI) tool

If you need custom tools, wp-scripts can assist. For instance, you might want to deploy a custom CLI tool that fits the needs of your agency.

In some cases, your tool will need some dependencies:

npm install @wordpress/scripts commander chalk

Here, Commander parses command line arguments, and chalk improves the visual display of the output text.

From here, you can begin to code the tool leveraging wp-scripts. Here’s an example of how that file would look:

#!/usr/bin/env node
const { program } = require('commander');
const { spawn } = require('child_process');
const path = require('path');
const chalk = require('chalk');

const wpScripts = path.resolve(__dirname, './node_modules/.bin/wp-scripts');

const runWpScripts = (script, args) => {
  console.log(chalk.blue(`Running wp-scripts ${script}...`));
  const result = spawn(wpScripts, [script, ...args], { stdio: 'inherit' });
  result.on('exit', (code) => {
    if (code !== 0) {
      console.log(chalk.red(`wp-scripts ${script} failed with code ${code}`));
    }
  });
};

program
  .version('1.0.0')
  .description('Custom WordPress development CLI for Agency XYZ');

program
  .command('build')
  .description('Build the project')
  .action(() => runWpScripts('build'));

program
  .command('start')
  .description('Start the development server')
  .action(() => runWpScripts('start'));

program
  .command('lint')
  .description('Lint JavaScript and CSS files')
  .action(() => {
    runWpScripts('lint-js');
    runWpScripts('lint-style');
  });

program
  .command('test')
  .description('Run unit tests')
  .action(() => runWpScripts('test-unit-js'));

program
  .command('deploy')
  .description('Deploy the project')
  .action(() => {
    console.log(chalk.green('Deploying project...'));
    // Add your deployment logic here
    // For example:
    // spawn('rsync', ['-avz', 'build/', 'user@server:/path/to/wordpress/wp-content/themes/your-theme/']);
  });

program.parse(process.argv);

Adding the bin field to your package.json file lets you make the CLI tool an executable:

…
{
  "name": "agency-wp-cli",
  "version": "1.0.0",
  "bin": {
    "agency-wp": "./cli.js"
  },
  // ... other fields
}
…

To link the CLI for a local installation, you can simply run npm link. Now, you can test the CLI out in your Terminal app:

agency-wp build
agency-wp lint
agency-wp deploy

You should publish the tool to a private npm registry so other team members can install it at their leisure:

npm publish --registry=https://your-private-registry.com

When you need it, you only need to run npm install --save-dev agency-wp-cli to install your tool. From here, you can reference the CLI within package.json:

{
  "scripts": {
    "build": "agency-wp build",
    "start": "agency-wp start",
    "lint": "agency-wp lint",
    "test": "agency-wp test",
    "deploy": "agency-wp deploy"
  }
}

Creating and using a tool like this ensures everyone in your agency uses the same commands and processes. This can reduce inconsistencies and streamline your WordPress development workflow even further.

Performance optimization

When managing multiple high-traffic WordPress sites, your performance optimization becomes crucial to your delivery. There are several advanced techniques wp-scripts can help you implement.

Advanced code splitting

For instance, code splitting lets you break your JavaScript bundle into smaller chunks, which you can load on demand. This can boost your initial loading times, especially for large applications.

First off, modify your webpack.config.js file to enable code splitting:

const defaultConfig = require('@wordpress/scripts/config/webpack.config');

module.exports = {
  ...defaultConfig,
  entry: {
    main: './src/index.js',
    admin: './src/admin.js',
  },
  output: {
    filename: '[name].js',
    chunkFilename: '[name].[contenthash].js',
  },
  optimization: {
    ...defaultConfig.optimization,
    splitChunks: {
      chunks: 'all',
      minSize: 0,
      cacheGroups: {
        vendor: {
          test: /[\\/]node_modules[\\/]/,
          name(module) {
            const packageName = module.context.match(/[\\/]node_modules[\\/](.*?)([\\/]|$)/)[1];
            return `vendor.${packageName.replace('@', '')}`;
          },
        },
      },
    },
  },
};

Throughout your JavaScript code, you use dynamic imports to split it into smaller chunks:

// Instead of: import { heavyFunction } from './heavyModule';

button.addEventListener('click', () => {
  import('./heavyModule').then(module => {
    module.heavyFunction();
  });
});

You also need to enqueue each split within your WordPress files:

function enqueue_split_scripts() {
  $asset_file = include(get_template_directory() . '/build/index.asset.php');  

  wp_enqueue_script('main', get_template_directory_uri() . '/build/main.js', $asset_file['dependencies'], $asset_file['version'], true);
  wp_enqueue_script('admin', get_template_directory_uri() . '/build/admin.js', $asset_file['dependencies'], $asset_file['version'], true);
}
add_action('wp_enqueue_scripts', 'enqueue_split_scripts');

This should give you lower loading times and doesn’t take too long to implement either.

Tree shaking

In the context of JavaScript, ‘tree shaking’ is how you get rid of unused code. Both wp-scripts and webpack perform tree shaking for production builds, but you can optimize it further. The application is more complex than we give here, but regardless, you want to ensure you use ES6 import and export syntax:

// Good for tree shaking
export function usedFunction() { /* ... */ }

// Bad for tree shaking
module.exports = {
  usedFunction: function() { /* ... */ },
};

Next, specify those files with ‘side effects’

{
  "name": "your-package",
  "sideEffects": ["*.css", "*.scss"]
}

…or mark it as free from side effects:

{
  "name": "your-package",
  "sideEffects": false
}

Some larger libraries don’t support tree shaking, as well as others. In these cases, you should use a specific plugin for the job:

npm install --save-dev babel-plugin-transform-imports

This will also mean you should update your babel configuration files too for the most optimal and error-free installation.

Asset optimization

The wp-scripts build process will minimize CSS and JavaScript files for you, and you can take this further too. For example, you can install a specific image optimization plugin:

npm install --save-dev imagemin-webpack-plugin

Once you add this to your webpack config file, you can use resource hints by adding the right code to your functions.php file or main plugin file:

function add_resource_hints( $urls, $relation_type ) {
  if ( 'preconnect' === $relation_type ) {
    $urls[] = 'https://fonts.googleapis.com';
    $urls[] = 'https://fonts.gstatic.com';
  }
  return $urls;
}

add_filter( 'wp_resource_hints', 'add_resource_hints', 10, 2 );

A task like this shows the flexibility you have with wp-scripts, in as much as how you can tailor it to your agency or project needs.

Analyze bundle size

Understanding your bundle composition is crucial for optimization. With wp-scripts, you can make it easy with the --analyze flag.

npm run build -- --analyze

The first step is to add the relevant script to your package.json file:

{
  "scripts": {
    "analyze": "wp-scripts build --analyze"
  }
}

Once you run the analyze command, it will generate a report that shows the size of each module in your bundle. This simple implementation helps you identify areas for optimization.

Implementing Critical CSS

Your site’s critical CSS is the bare minimum your page requires to load above-the-fold content. Putting this CSS inline can improve the perceived load time.

Achieving this requires the Critical CSS Webpack plugin:

npm install --save-dev critical-css-webpack-plugin

The next job is to update your webpack config file to reference the plugin:

const CriticalCssPlugin = require('critical-css-webpack-plugin');
const defaultConfig = require('@wordpress/scripts/config/webpack.config');

module.exports = {
  ...defaultConfig,
  plugins: [
    ...defaultConfig.plugins,
    new CriticalCssPlugin({
      base: 'dist/',
      src: 'index.html',
      target: 'styles/critical.css',
      extract: true,
      inline: true,
      minify: true,
      dimensions: [
        {
          height: 500,
          width: 300,
        },
        {
          height: 900,
          width: 1300,
        },
      ],
    }),
  ],
};

In order to use it, you need to add a snippet to your header.php file:

<style>
  <?php echo file_get_contents(get_template_directory() . '/build/styles/critical.css'); ?>
</style>

This essentially extracts and inlines critical CSS for a faster initial render. It generates critical CSS for specific viewport sizes, which can improve the perceived load time of your themes.

Summary

For agency development, wp-scripts can be a powerful tool that can significantly enhance your workflow for both theme and plugin projects. By providing a standardized build process, modern JavaScript support, and integrated testing and linting tools, wp-scripts lets you focus on creating high-quality WordPress projects while automating some of the most important tasks.

Leveraging wp-scripts doesn’t only help you keep up with modern development practices. It’s a way to position yourself at the forefront of WordPress development, ready to tackle the challenges and opportunities your agency has to face.

Does wp-scripts offer the functionality and scope you need for your agency development projects? Share your thoughts with us in the comments section below!

Steve Bonisteel Kinsta

Steve Bonisteel is a Technical Editor at Kinsta who began his writing career as a print journalist, chasing ambulances and fire trucks. He has been covering Internet-related technology since the late 1990s.