laravel-mix: Hot reloading not working with laravel/sail [laravel 8]

  • Laravel Mix Version: 5.0.9
  • Node Version (node -v): v15.5.0
  • NPM Version (npm -v): 7.3.0
  • OS: Windows with Ubuntu 20.04 WSL2

Description:

Hot reloading is not working using laravel/sail.

Steps To Reproduce:

I have tried with sail npm run watch sail npm run hot and nothing seems to work.

Also when I run npm run hot it output this:

ℹ 「wds」: Project is running at http://localhost:8080/
ℹ 「wds」: webpack output is served from http://localhost:8080/
ℹ 「wds」: Content not from webpack is served from /var/www/html/public
ℹ 「wds」: 404s will fallback to /index.html

but clicking on http://localhost:8080/ actually opens http://localhost:14252/ which is so weird.

I also tried to add EXPOSE 8080 to the Dockerfile and 8080:8080 to the docker-compose.yml then I rebuilt the image and but still not working and I can’t see any side effect.

Any blade template I made changes on and save it, there is no autoreload on any browser. I have to activelly reload the page to check every change I made.

About this issue

  • Original URL
  • State: open
  • Created 4 years ago
  • Reactions: 15
  • Comments: 31 (3 by maintainers)

Most upvoted comments

Hi @moracabanas, I have gotten one step closer to getting this working by:

  • Adding a port binding from 8080:8080 to the docker docker-compose.yml as you mentioned (EXPOSE 8080 isn’t required)
  • Adding the following config to webpack.mix.js:
mix.webpackConfig({
    devServer: {
        host: '0.0.0.0',
        port: 8080,
    },
});

Now when I run sail npm run hot I can see that it picks up changes as I make them, and my javascript is now loading the first time in the browser. But currently my browser isn’t picking up and replacing the new ‘hot’ javascript parts.

I will report back if I figure out how to solve this next issue.

Edit: sail npm run watch is a work around for me at the moment which works okay. Yet to figure out how to get HMR working though.

What finally, simply worked for me was the following:

  1. Add the 8080 port binding to docker-compose.yml:
laravel.test:
        ...
        ports:
            - '${APP_PORT:-80}:80'
            - 8080:8080
        ...
  1. Add the devServer setting to webpack.config.js:
module.exports = {
    ...
    devServer: {
        host: '0.0.0.0',
        port: 8080,
    },
};

Note that the hmrOptions stuff in webpack.mix.js isn’t necessary, because it already defaults to using localhost and 8080.

Update! I read the Laravel Mix code a bit better… It looks like the Webpack Config options can override the options set by hmrOptions… Which means we can simply do this:

webpack.mix.js (or you can replace localhost by something else if you have a custom domain set up in your Hosts file; but it doesn’t really matter and localhost should probably always work)

mix.options({
    ...
    hmrOptions: {
        host: 'localhost',
        port: 8080,
    },
});

webpack.config.js (or put this in the webpackConfig() function which might be the default for Mix 5.x) (you can also put laravel.test instead of 0.0.0.0 this will also work)

module.exports = {
    ...
    devServer: {
        host: '0.0.0.0',
        port: 8080,
    },
};

Those 2 combined, along with the 8080 port binding in docker-compose.yml is now happily working for me!

Do keep in mind that you just need to visit the normal URL without :8080 appended. I didn’t know this at first.

Here’s what works on mine and what I found out about the difference between hmrOptions on mix.options and devServer on mix.webpackConfig.

My environment is: Laravel v8.34.0 laravel-mix v6.0.6 npm: v7.7.6 node: v15.14.0

docker-compose.yml

services:
    laravel.test:
        ...
        ports:
            - '${APP_PORT:-80}:80'  // APP_PORT=8085
            - '8585:8080'           // want to change 8585? Update hmrOptions like below
                                    // want to change 8080? Update devServer like below

webpack.mix.js

mix.options({
    hmrOptions: {
        host: 'laravel.test',       // Requires adding entry on /etc/hosts file if I want
        port: 8585                  // to use laravel.test but localhost, 127.0.0.1 
    }                               // or 0.0.0.0 would also work.
})

mix.webpackConfig({
    ...
    devServer: {
        host: '0.0.0.0',
        port: 8080
    }
});

Accessing http://laravel.test:8085 would result in replacing

<script type="text/javascript" src="{{ mix('js/app.js') }}"></script>
to
<script type="text/javascript" src="http://laravel.test:8585/js/app.js"></script>

Looks like the devServer configuration is for configuring the container side on where to expect the hot files while the hmrOptions is what will mix use on the html side.

My setup is exactly the same as @moracabanas, except that I am using Laravel Mix 6.0.6. I am also running Laravel 8 on Sail with Node 15.5.0 and NPM 7.3.0. And I am also running a Ubuntu 20.04 WSL2 backend op Windows 10.

I finally got this to work (more or less) after tinkering around a lot.

I don’t get any output regarding HMR when I run sail npm run hot. But perhaps that is not needed (anymore)?

What I do see is that webpack picks up my changes because I see a fresh Compiled successfully... message when I edit a Vue file.

Initially I couldn’t get anything to load when visiting http://localhost:8080. After adding a port binding for 8080 into docker-compose.yml and restarting the containers I could theoretically connect to port 8080 but I wasn’t able to access it from my local machine.

I also tried adding the devServer code to webpackConfig(), but that didn’t fully work for me. I believe it has to do with the fact that Laravel Mix expects hmrOptions to be set because it will do some things based on that, like create the hot file (it will also automatically set the devServer options behind the scenes if I understand the source correctly).

So I added the following code into mix.options() in webpack.mix.js:

mix.options({
    hmrOptions: {
        host: 'laravel.test',
        port: 8080,
    },
});

laravel.test is the service name in docker-compose.yml. It also seemed to work if I just put 0.0.0.0 as the host there, but then 0.0.0.0 will be put into the hot file and will be used as the JS src in the HTML, which I cannot access.

By doing the above and after running sail npm run hot I could then visit http://localhost:8080 but it would only return the following: Cannot GET / However… If I request http://localhost:8080/js/app.js I do get a response with compiled JS back.

After that, I found out that I can just visit http://localhost (without port 8080) and if the file hot exists in the public directory (which gets added and contains http://laravel.test:8080) then I can visit the site as usual and I will see http://laravel.test:8080/js/app.js being requested (because of the hot file and the mix() function generating the URL to the JS asset).

So I added laravel.test to my Hosts file (resolve to 127.0.0.1) and then I reloaded the page and I finally saw this message in my JS console: [webpack-dev-server] Hot Module Replacement enabled.

And indeed, after changing something in a Vue template, the change is reflected instantly in the browser!

Hopefully this can help you out too?

It looks like it is currently not possible to easily change the URL to something else besides laravel.test because Laravel Mix seems to use the host and port values from hmrOptions to (1) set the values for webpack-dev-server but also (2) create the hot file which will be used to create a link to the “HMR assets” when the mix() function is called. I think this should probably be configurable.

PS: if you edit the hot file manually and remove the http or https from the URL, then the mix() function will fallback to http://localhost:8080 instead. Found this out by reading the ´mix()´ source code…

Hello,

for everybody still having problems with the solution above (for a reason it didn’t work for me). In my case I am using inertia.js, which seems to cause problems when not running the sail npm run watch-poll command. Either hot, nor watch were picking the changes done in the .vue files.

For me watch-poll and browserSync did the trick for 100% hot reload experience, by calling localhost:3000. For more information refer to this https://laravel-mix.com/docs/6.0/browsersync and this thread.

here my webpack.mix.js

const mix = require('laravel-mix');




mix
  .js('resources/js/app.js', 'public/js')
  .vue()
  .alias({ '@': 'resources/js' })
  .postCss('resources/css/app.css', 'public/css', [
    // prettier-ignore
  ]);



mix.browserSync({
	host: '127.0.0.1',
	proxy: 'localhost',
	open: false,
	files: [
            'app/**/*.php',
            'resources/views/**/*.php',
            'packages/mixdinternet/frontend/src/**/*.php',
            'public/js/**/*.js',
            'public/css/**/*.css'
	],
	watchOptions: {
            usePolling: true,
            interval: 500
  }
});

and my docker compose:

  laravel.test:
    ...
       ports:
           - '${APP_PORT:-80}:80'
           - 3000:3000

Maybe this has something to do with the way I embade js script in a blade template: <script src="/js/app.js"></script>

It is very important that you use Laravel’s global mix function. This will load hashed/versioned assets on production, and when running HMR in development it will pull in the correct JS instead. So try this: <script src="{{ mix('/js/app.js') }}"></script>

This is referenced (for production purposes) here: https://laravel.com/docs/8.x/mix#versioning-and-cache-busting

Does that work for you?

I got lost as well. After setting up everything as @synio-wesley described above, I can see that changes are detected:

✔ Compiled Successfully in 219ms
┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┬──────────┐
│                                                                                                                                                                                                                                                                                                            File │ Size     │
├─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┼──────────┤
│                                                                                                                                                                                                                                                                      /js/app.eea2b40d3912bde50a95.hot-update.js │ 5.51 KiB │
│                                                                                                                                                                                                                                                                                                      /js/app.js │ 1.31 MiB │
│                                                                                                                                                                                                                                                                    _js_app.eea2b40d3912bde50a95.hot-update.json │ 31 bytes │
│                                                                                                                                                                                                                                                                                                     css/app.css │ 62 bytes

I can see the newest version of js file under http://localhost:8080/js/app.js but when I go to localhost:80 there is still old version of app.js. Also, in public/js/app.js there is an old version of compiled file. I need to run npm run watch to refresh public/js/app.js.

Maybe this has something to do with the way I embade js script in a blade template: <script src="/js/app.js"></script>

I’ve been pulling my hairs out for hours trying to figure out where webpack is puts this app.js file. It must be somewhere if it’s available throught http://localhost:8080/js/app.js, right?

Any help would be greatly appreciate 🙏


webpack.mix.js

mix.js("resources/js/app.js", "public/js")
    .react()
    .sass("resources/sass/app.scss", "public/css");

mix.options({
    hmrOptions: {
        host: "localhost",
        port: 8080,
    },
});

mix.webpackConfig({
    devServer: {
        host: "0.0.0.0",
        port: 8080,
    },
});

With laravel-mix 6.0.16 running Windows WSL2 with Ubuntu and sail @alexpcoleman solution worked.

mix.webpackConfig({
    ...
    devServer: {
        host: "0.0.0.0"
    }
});

I did have to expose 8080 in docker-compose.yml.

I didn’t need to specify the port in devServer config as 8080 seems to be the default.

@synio-wesley comment worked for me.

In case anyone else got the server.sockWrite is not a function error, here’s a modified version of the blade reload trick:

onAfterSetupMiddleware(server) {
   chokidar.watch([
       './resources/views/**/*.blade.php'
   ], { ignoreInitial: true }).on('all', function() {
       server.sendMessage(server.webSocketServer.clients, 'content-changed');
   })
},

The last part to get this setup for me so it simply runs with sail up

was to run command sail artisan sail:publish then to docker/8.0/supervisord.conf add

[program:hot]
command=npm run hot
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0

everyone in this thread thank you for your help

@cthom-dev try this:

sail npm run hot

Btw, thanks @synio-wesley so much! The use of chokidar is more than what I had expected, combining with @cthom-dev solution, they all work perfectly!

This is not working for me. I’m testing it in a new project:

But HMR does work for you? (For example for Vue files and/or for CSS changes?) And you also set up the 8080 port binding in Docker?

It does look very similar to what I have here. The only difference is that I import the webpackConfig from a separate file, but I don’t think that this is an issue:

webpack.mix.js

mix
    .webpackConfig(require('./webpack.config'))
    .options({
        postCss: [
            require('postcss-import'),
            require('tailwindcss'),
            require('autoprefixer'),
        ],
        hmrOptions: {
            host: 'localhost',
            port: 8080,
        },
    });

webpack.config.js

const path = require('path');
const chokidar = require('chokidar');

module.exports = {
    module: {
        rules: [
            {
                resourceQuery: /blockType=i18n/,
                type: 'javascript/auto',
                loader: '@intlify/vue-i18n-loader',
            }
        ],
    },
    resolve: {
        alias: {
            '@': path.resolve('resources/js'),
        },
    },
    devServer: {
        host: '0.0.0.0',
        port: 8080,
        onBeforeSetupMiddleware(server) {
            chokidar.watch([
              './resources/views/**/*.blade.php'
            ]).on('all', function() {
              server.sockWrite(server.sockets, 'content-changed');
            })
        },
    },
};

There’s a few extra rules in there, but they are just for my specific project (postCss, vue-i18n)

Can you tell me what OS and Laravel versions you are running? And/or could you perhaps share the test project with me? I’m happy to take a look to see if I can find out why it’s behaving differently.

PS: In case you are only working with CSS and Blade files (not Vue or anything) you could probably also use something like BrowserSync instead

  • Laravel Mix Version: 5.0.1
  • Node Version (node -v): v15.4.0
  • NPM Version (npm -v): 7.0.15
  • OS: Manjaro Linux
  • Docker Version: 20.10.1

The solution in @jameshulse comment solved the problem for me. But when running sail npm run hot the command takes a lot of time compared to running it outside sail

image