PHPackages                             aslamhus/wordpress-hmr - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. aslamhus/wordpress-hmr

ActiveLibrary

aslamhus/wordpress-hmr
======================

HMR setup with webpack and wp-scripts for wordpress development and production

3.0.9(1mo ago)184—0%1MITPHP

Since Mar 26Pushed 1mo ago1 watchersCompare

[ Source](https://github.com/aslamhus/WordpressHMR)[ Packagist](https://packagist.org/packages/aslamhus/wordpress-hmr)[ RSS](/packages/aslamhus-wordpress-hmr/feed)WikiDiscussions main Synced 1mo ago

READMEChangelogDependencies (1)Versions (35)Used By (0)

Wordpress Development setup with HMR and WP-Scripts
===================================================

[](#wordpress-development-setup-with-hmr-and-wp-scripts)

Features
--------

[](#features)

- Hot module replacement (HMR) for theme development (no CSS reloads!)
- Containerized WordPress environment with Docker
- Simple whr.json config for defining, conditionally enqueuing, and managing all theme assets
- Optimized production builds via wp-scripts with statically generated asset enqueuing
- Child theme scaffolding, WP-CLI, and PHPMyAdmin included out of the box
- One-command setup — `vendor/bin/whr install` gets a full containerized WP site running from scratch
- CLI tool — run WP-CLI commands, manage containers, and scaffold themes without touching Docker directly

Introduction
------------

[](#introduction)

This is an experimental package inspired by `wp-env`, which allows you to develop containerized wordpress sites with `Docker`, `Webpack` and `hot module replacement (HMR)`. It uses the Wordpress `wp-scripts` package in tandem with a custom enqueuing algorithm to enqueue assets in your theme.

WordpressHMR now comes with a CLI tool that allows you to spin up a containerized wordpress site with one command.

Defining and enqueuing assets is as simple as managing a single `whr.json` (**W**ordpress **Hot** module **R**eplacement) file. This file contains the configuration for your assets, including the path to your scripts and styles, the hooks where they should be enqueued, and any conditions for enqueuing them.

Who is this package for?
------------------------

[](#who-is-this-package-for)

Anyone who wants to develop Wordpress themes with the fast paced development experience of HMR in a containerized environment. If you're familiar with `wp-env`, setup is a breeze.

Why not just use `wp-env`?
--------------------------

[](#why-not-just-use-wp-env)

`wp-env` is fantastic for custom block development, but it lacks the ability to hot reload styles and scripts. While you can leverage hot module replacement in custom block development, it is not currently possible to do so for theme development. This package combines the power of `wp-scripts` to manage asset dependencies with a dynamic enqueuing algorithm to provide hot module replacement for your theme development.

Requirements
------------

[](#requirements)

This package requires [Docker](https://www.docker.com/get-started/), [PHP](https://www.ionos.ca/digitalguide/websites/web-development/install-php/), [Composer](https://getcomposer.org/download/), and [Node.js](https://nodejs.org/en), similar to the setup for `wp-env`.

Getting started
---------------

[](#getting-started)

1. Install WordpressHMR package

```
composer require --dev aslamhus/wordpress-hmr
```

2. Run the installer and follow the prompts.

```
vendor/bin/whr install
```

3. Create a child theme

The default installation creates a wordpress site for you, but it is recommended that you create a child theme. To do so, simply run:

```
vendor/bin/whr create-child-theme
```

Follow the prompts to use a default theme or install your own. If you want to manually install a theme, simply add its directory to the `public/wp-content/themes` directory in your project.

4. Start your container!

```
# start your container
vendor/bin/whr start
# start your container with HMR
vendor/bin/whr start --hot
```

That's it! Try changing the css in your `resources/assets/screen.scss` file to see the changes immediately reflected in your browser. By default, the package adds two scripts to your theme, `screen.js` and `editor.js`. Each of these scripts is defined in the `whr.json` file with conditions to enqueue them in the public facing area of your wordpress site nd editor respectively. Each script imports a corresponding css file in your theme. Add more scripts or more css / scss files as you like. Try updating the css and see the changes reflected without a reload.

### Webpack HMR Entry points limitation

[](#webpack-hmr-entry-points-limitation)

This package supports multiple entry points, however HMR will only work with one entry point per page. This is a known issue with `Webpack HMR` when multiple entry points are loaded on the same page.

In development, I recommend having a single entry point per page so that you can take advantage of the fast paced development experience of HMR, and then use a separate config for your production environment. This can easily be facilitated with separate `whr.json` files, according to your environment.

Import a database by default
----------------------------

[](#import-a-database-by-default)

If you have a mysql dump of a database you'd like installed, place it in the `Docker/data/db` directory. To rebuild the container with this dump file imported, run `vendor/bin/whr start --reset`. Make sure your dump file uses the database `wp_database`.

***If you want to use a different database name, you'll need to change it in the `compose.yml` file, found in your project root. Search for all references to `wp_database` and replace it with the database name of your choice.***

Configuration (whr.json)
------------------------

[](#configuration-whrjson)

The `whr.json` file is where you define assets.

It is a simple JSON configuration file that contains the following properties:

- `config` - This is where you define the configuration for your assets.
- `assets` - This is where you define your scripts. Each property of the `assets` object is a hook where you want to enqueue your script. The value of each property is an array of script objects.

### Config

[](#config)

- `handlePrefix` - This is the prefix for your asset handles. This is useful if you want to avoid conflicts with other plugins or themes.
- `host` - This is the host of your local development server.
- `site` - The container url.
- `port` - This is the port of your local development server.
- `protocol` - This is the protocol of your local development server.
- `theme` - the name of your theme. Change your theme manually in whr.json or create it using the CLI tool, vendor/bin/whr create-child-theme. If you change it manually, the theme will be activated automatically when you start your container (as long as that theme is available in your themes directory)
- `src` - Points to the src directory where your webpack entry points live. Files here will be emitted to the theme you specify.
- `themePath` - This is the path to your themes directory.

### Assets

[](#assets)

An array of script objects. Each script object contains the following properties:

- `handle` - The handle of the script. This is the name that you will use to enqueue the script.
- `hooks` - An array of hooks where the script will be enqueued.
- `path` - The path to the script file.
- `ext` - The file extension of the script file.
- `dependencies` - An array of dependencies the script requires.
- `condition` - Set a condition to enqueue the asset. For more on conditional enqueuing, see the [Conditional enqueuing](#conditional-enqueuing) section below.

### Example

[](#example)

```
{
  "config": {
    "handlePrefix": "my-asset-handle",
    "host": "localhost",
    "site": "http://localhost:8889",
    "port": "8889",
    "protocol": "http",
    "theme": "twentytwentyfive",
    "src": "resources",
    "themePath": "public/wp-content/themes/"
  },
  "assets": {
    "enqueue_block_editor_assets": [
      {
        "handle": "editor-js",
        "path": "/js/editor",
        "ext": "js"
      }
    ],
    "wp_enqueue_scripts": [
      {
        "path": "/js/screen",
        "ext": "js",
        "condition": ["is_user_logged_in", null, false]
      }
    ]
  }
}
```

### Adding a new asset / HMR entry point

[](#adding-a-new-asset--hmr-entry-point)

To add a new asset, simply add a new object to the `assets` array in the `whr.json` file. You will have to run `vendor/bin/whr build` to generate the necessary asset files.

Let's add some styles to our theme which we only want to appear on our custom template, "My Custom Template".

1. Create a scss/css file in your `resources/assets/css` directory. For example, `my-custom-template.scss`.
2. Create a script in your `resources/js` directory that will import the scss file. For example, `my-custom-template.js`.

    ```
    import "../css/my-custom-template.scss";
    ```
3. Add the following to your `whr.json` file:

    ```
    {
      "assets": {
        "wp_enqueue_scripts": [
          {
            "path": "/js/my-custom-template",
            "ext": "js",
            "condition": ["is_page_template", "My Custom Template", true]
          }
        ]
      }
    }
    ```
4. Run `vendor/bin/whr build` to generate the necessary asset files.
5. Restart your development server with `vendor/bin/whr start`. The styles should now be enqueued on the "My Custom Template" page.

You're done! Try changing the styles in your `my-custom-template.scss` file and see the changes reflected in your browser only on pages that use the "My Custom Template" page template.

For a list of condition functions that you can use, see the [Wordpress Conditional Tags](https://developer.wordpress.org/themes/basics/conditional-tags/).

***A note on block theme hooks***

When enqueuing assets for block themes, you will need to use the following hooks:

`enqueue_block_editor_assets` - to load only in editor view `enqueue_block_assets` - loads both on frontend and editor view

### Conditional enqueuing

[](#conditional-enqueuing)

##### Example 1: Enqueue a script only on the block editor

[](#example-1-enqueue-a-script-only-on-the-block-editor)

In this example, we only want to enqueue a script in the block editor. We set the path to the script file as well as the extension. We don't need to specify a condition for this script, as it will be enqueued in the block editor by default.

```
{
  "assets": {
    "enqueue_block_editor_assets": [
      {
        "handle": "editor-js",
        "path": "/js/editor",
        "ext": "js"
      }
    ]
  }
}
```

##### Example 2: Enqueue a script only on the front end

[](#example-2-enqueue-a-script-only-on-the-front-end)

Same as above, but we want to enqueue a script only on the front end. We use the `wp_enqueue_scripts` hook to enqueue the script.

```
{
  "assets": {
    "wp_enqueue_scripts": [
      {
        "path": "/js/screen",
        "ext": "js"
      }
    ]
  }
}
```

##### Example 3: Enqueue a script only on a specific page template

[](#example-3-enqueue-a-script-only-on-a-specific-page-template)

In this example, we only want to enqueue a script on a specific page template. We use the `get_page_template_slug` function to get the page template slug and compare it to our custom template slug. You can find the page template slug by looking at the classname of the body tag of any page given the template.

```
// for custom template
{
  "assets": {
    "wp_enqueue_scripts": [
      {
        "path": "/js/screen",
        "ext": "js",
        "condition": ["get_page_template_slug", "", "my-custom-template"]
      }
    ]
  }
}
// for default page template
  {
        "handle": "pages-feature-image",
        "path": "/js/templates/pages-feature-image",
        "ext": "js",

        "condition": ["get_page_template_slug", null, "page-template-default"]
      }

// for a custom post type
{
    "handle": "instructor-js",
    "path": "/js/instructor-single",
    "ext": "js",
    "condition": ["get_post_type", ["function", "get_the_id"], "custom-instructors"]
  }

// get the id of the page
{
 "condition": ["get_the_id", null, "2039"]
}

// targeting block editor styles only
 "enqueue_block_assets": [
      {
        "handle": "editor-js",
        "path": "/js/editor",
        "ext": "js",
        "condition": ["is_admin", null, true]
      }
    ],
```

##### Example 4: Use a conditional argument that takes the result of a function as an argument

[](#example-4-use-a-conditional-argument-that-takes-the-result-of-a-function-as-an-argument)

You can use a function as an argument by declaring the type in an array, followed by the function name, and then a list of optional arguments.

In this example, we only want to enqueue an asset on the about page. We need the id value of the current page to use the `get_the_title` method. We can use the `get_the_id` function to get the id of the current page.

This will evaluate to `get_the_title(get_the_id()) === 'about'`.

```
{
  "assets": {
    "wp_enqueue_scripts": [
      {
        "path": "/js/screen",
        "ext": "js",
        "condition": ["get_the_title", ["function", "get_the_id"], "about"]
      }
    ]
  }
}
```

Build for production
--------------------

[](#build-for-production)

Building is a very simple process, and leverages `wp-scripts` to generate the necessary asset files. A little magic is performed by `Aslamhus\WordpressHMR\EnqueueAssets` to create a static script which enqueues the assets in your theme, avoiding the performance overhead of enqueuing assets dynamically. Run the build command and then check out your enqueue-assets.php file in your build folder `public/wp-content/themes/your-theme/inc/enqueue-assets.php`

```
vendor/bin/whr build
```

### Project directory structure

[](#project-directory-structure)

The installer will create the following folders/files:

- `Docker` directory, which serves as an entrypoint for files in your project root and the docker container.
- `public` directory, with the latest version of wordpress installed. The installer will also prompt you to create a custom theme directory with the name you specify.
- `resources` directory where all your theme files will reside. Webpack copies all the files from your resources folder to the current active theme.
- `resources/assets` directory where your css / scss files will reside.
- `resources/inc` directory where the `enqueue-assets.php` and `functions.php` file will reside.
- `resources/js` directory where your entry points will reside.
- `vendor` directory where your composer packages are stored. This directory is mounted in the docker container in the parent directory of your wordpress site (/var/www/).
- `compose.yml` Docker compose settings.
- `whr.json` where you define your assets and theme

Accessing Docker containers
---------------------------

[](#accessing-docker-containers)

### Commands

[](#commands)

`WordpressHMR` provides some convenient commands to access your container. To run `wp-cli` commands inside your container you can use:

```
vendor/bin/whr wp --info
```

To run commands inside your wordpress container you can use:

```
vendor/bin/whr exec
```

Note that not all common binaries exist in the wordpress container. Therefore, it's recommended to run commands in the wordpress container like so:

```
vendor/bin/whr exec bash -c 'cd ../ && ls -1'
```

### Container entry points

[](#container-entry-points)

Your containers can access files in your local project through `Docker/data/tmp`. You'll find any files you add to this directory at the container path `/tmp/Docker`. If you want to change this directory, please see `volumes` in your `compose.yml` file, located in your project root.

When is this useful? Say you want to import / export a database. Add your .sql dump file to Docker/data/tmp in your project. Then import it into your container's database by running `vendor/bin/whr wp db import /Docker/tmp`.

Likewise, you can export your database by running `vendor/bin/whr wp db export /tmp/Docker/dump.sql`

Container settings
------------------

[](#container-settings)

### Wordpress

[](#wordpress)

If you want to change values in the `wp-config.php` file, you can do so through your `compose.yml` file in your project root. In the services.wordpress.environment section, you'll find default values you can change. For example, if you'd like to display more error reporting with `ini_set`, you can add those values under `WORDPRESS_CONFIG_EXTRA`. Your container will evaluate that string value directly into the `wp-config.php` file.

**Note: For changes to take effect, you'll need to restart your container with `vendor/bin/whr start`**

```
services:
  wordpress:
    ...
    environment:
      WORDPRESS_DB_NAME: wp_database
      WORDPRESS_TABLE_PREFIX: wp_
      WORDPRESS_DB_HOST: db
      WORDPRESS_DB_USER: admin
      WORDPRESS_DB_PASSWORD: password
      WORDPRESS_DEBUG: true
      WORDPRESS_CONFIG_EXTRA: |
        /* Remove https for admin */
        define('FORCE_SSL_ADMIN', false );
        define('WP_DEBUG_LOG', true);
        define('WP_DEBUG_DISPLAY',true);

        /* add error reporting */
        ini_set('display_errors', true);
        ini_set('display_startup_errors', true);
        error_reporting(E_ALL);

```

### Adding files to your wordpress container

[](#adding-files-to-your-wordpress-container)

By default, only your `wp-content` directory is mounted in the container. It is the bridge between your local project and the container. If you'd like to mount another file in the container, you can add it in your `compose.yml` file.

For example, say you wanted to add your own `.htaccess` file. Add it to your public directory and then edit your `compose.yml` file to include it as a volume:

```
services:
  wordpress:
    volumes:
      # htaccess
      - ./public/.htaccess:/var/www/html/.htaccess
      # Note: docker splits the volume string by a the colon (:)
      # You specify the path to your file / directory you'd like to mount,
      # followed by location in the container.
      # /var/www/html is where your wordpress site lives.
```

***WARNING: Be careful adding a file like `wp-config.php` since the wordpress Docker image uses a specific `wp-config.php` with its own logic for setting environment variables.***

Troubleshooting
---------------

[](#troubleshooting)

### Webpack Errors

[](#webpack-errors)

If you encounter an error such as `Module parse failed: 'import' and 'export' may appear only with 'sourceType: module'`, please make sure your root directory of your project does not contain a package.json file where the type is set to "commonjs". While `aslamhus/wordpress-hmr` uses ES6 syntax for its webpack configuration, `wp-scripts` uses CommonJS and specifying a type can cause errors.

### HMR isn't working

[](#hmr-isnt-working)

If you aren't seeing changes in your wordpress reflected in your site, try:

1. You've started your container with the `--hot` option.
2. Checking if the following line is in your resources/functions.php file. It should be added automatically when you install or change your theme.

```
/** Enqueue Custom Theme Assets */
 require_once  get_stylesheet_directory() . '/inc/enqueue-assets.php';
```

### Wordpress Errors

[](#wordpress-errors)

#### Can't login to my admin

[](#cant-login-to-my-admin)

Here's where the `whr` command line tool comes in handy. Simply add a new user with administrator privileges to login:

```
vendor/bin/whr wp user create admin admin@example.com --role=administrator
# a password will be generated for you. Now you can log in!
```

### Modifying the Webpack configurations

[](#modifying-the-webpack-configurations)

You'll find all the webpack config files in the `vendor/aslamhus/wordpress-hmr/build`.

Contributing
------------

[](#contributing)

If you would like to contribute to this package, please feel free to submit a pull request. I would love to hear your feedback and suggestions for improvement.

---

**This package was proudly built without AI, just one single human clacking away at the keyboard.**

```

```

###  Health Score

44

—

FairBetter than 92% of packages

Maintenance91

Actively maintained with recent releases

Popularity15

Limited adoption so far

Community8

Small or concentrated contributor base

Maturity53

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~23 days

Recently: every ~0 days

Total

33

Last Release

50d ago

Major Versions

v1.2.3 → v2.1.02024-10-02

v2.3.0 → v3.0.12026-03-18

### Community

Maintainers

![](https://www.gravatar.com/avatar/cf398f3954c2155216c23f9573a1729d46cbe95f9fdf17fc3da7d0da55fe1903?d=identicon)[aslamhus](/maintainers/aslamhus)

---

Top Contributors

[![aslamhus](https://avatars.githubusercontent.com/u/76789892?v=4)](https://github.com/aslamhus "aslamhus (52 commits)")

---

Tags

hot-module-replacementwordpresswordpress-child-themewordpress-containerwordpress-developmentwordpress-docker-developmentwordpress-envwordpress-hmrwordpress-themewordpress-webpackwp-cliwp-env

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/aslamhus-wordpress-hmr/health.svg)

```
[![Health](https://phpackages.com/badges/aslamhus-wordpress-hmr/health.svg)](https://phpackages.com/packages/aslamhus-wordpress-hmr)
```

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
