Skip to content

Instantly share code, notes, and snippets.

@esamattis
Last active May 19, 2019 00:36
Show Gist options
  • Star 21 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save esamattis/28c2dbcbe8109a0c5edb to your computer and use it in GitHub Desktop.
Save esamattis/28c2dbcbe8109a0c5edb to your computer and use it in GitHub Desktop.
React hot reloading with Webpack for Ruby on Rails
<script charset="utf-8">
<% if ENV["RAILS_ENV"] == "production" %>
var script = "/react-app-bundle.js";
<% else %>
console.warn("Development mode. Make sure to start 'node devServer.js'");
var script = "http://" + (location.host || 'localhost').split(':')[0] + ":4000/react-app-bundle.js"
<% end %>
document.write('<script src="' + script + '"></' + 'script>');
</script>
var path = require("path");
var express = require("express");
var webpack = require("webpack");
var config = require("./webpack.config");
var app = express();
var compiler = webpack(config);
app.use(require("webpack-dev-middleware")(compiler, {
noInfo: true,
publicPath: config.output.publicPath,
}));
app.use(require("webpack-hot-middleware")(compiler));
app.listen(4000, "0.0.0.0", function(err) {
if (err) {
console.log(err);
return;
}
console.log("Listening at http://0.0.0.0:4000");
});
var webpack = require("webpack");
// This must be the public address where the hot reload bundle is loaded in the
// browser. Yeah it sucks to hard code it here. Let's hope for the better
// future
var PUBLIC_DEV_SERVER = "http://my-dev-server:4000/";
var ENTRY = "./index.js";
var NODE_ENV_PLUGIN = new webpack.DefinePlugin({
"process.env.NODE_ENV": JSON.stringify(process.env.NODE_ENV)
});
var config = {
entry: [
"webpack-hot-middleware/client?path=" + PUBLIC_DEV_SERVER + "__webpack_hmr",
ENTRY
],
output: {
path: __dirname + "/public",
filename: "react-app-bundle.js",
publicPath: PUBLIC_DEV_SERVER
},
devtool: "cheap-module-eval-source-map",
module: {
loaders: [
{
test: /\.jsx?$/,
exclude: /node_modules/,
loader: "babel",
query: {
"env": {
// Not active when NODE_ENV=production
"development": {
"plugins": ["react-transform"],
"extra": {
"react-transform": [{
"target": "react-transform-hmr",
"imports": ["react"],
"locals": ["module"]
}]
}
}
}
}
}
]
},
plugins: [
NODE_ENV_PLUGIN,
new webpack.HotModuleReplacementPlugin(),
new webpack.NoErrorsPlugin(),
]
};
// Drop all hot stuff for production!
if (process.env.NODE_ENV === "production") {
config.devtool = "source-map";
config.entry = ENTRY;
delete config.output.publicPath;
config.plugins = [NODE_ENV_PLUGIN];
}
module.exports = config;
@gugl
Copy link

gugl commented Oct 19, 2015

It helped me so much! Thanks for your gist.

Also: The config structure changed a bit with babel-plugin-react-transform version 1.1.0. so it's

                        "development": {
                            "plugins": ["react-transform"],
                            "extra": {
                                "react-transform": {
                                  transforms: [{
                                    "transform": "react-transform-hmr",
                                    "imports": ["react"],
                                    "locals": ["module"]
                                  }]
                                }
                            }
                        }

now. see https://github.com/gaearon/babel-plugin-react-transform/releases for more info on this change.

@dcurletti
Copy link

This works great! The one problem that I was experiencing was I didn't specify the path.

@ericdfields
Copy link

This helped me too. To get it to work, I had to make sure my config.output.path was absolute (path.join(__dirname,'relative/path/to/app.js')), and my config structure was even a little different than @gugl's update above:

    {
      loader: 'babel-loader',
      test: /\.(jsx?$)/,
      exclude: /node_modules/,
      query: {
        "presets": ["es2015", "react"],
        "plugins": [
          'transform-class-properties',
          'transform-decorators-legacy',
          'transform-object-rest-spread',
          'transform-object-assign',
          'transform-async-to-generator'
        ],
        "env": {
          // this plugin will be included only in development mode, e.g.
          // if NODE_ENV (or BABEL_ENV) environment variable is not set
          // or is equal to "development"
          "development": {
            "plugins": [
              // must be an array with options object as second item
              ["react-transform", {
                // must be an array of objects
                "transforms": [{
                  // can be an NPM module name or a local path
                  "transform": "react-transform-hmr",
                  // see transform docs for "imports" and "locals" dependencies
                  "imports": ["react"],
                  "locals": ["module"]
                }]
              }]
            ]
          }
        }
      }
    },

Last, to get hot module reload working with css while using postcss, I had to add postcss-import to my css config in webpack using the code at the bottom of this thread.

@nadbm
Copy link

nadbm commented Aug 10, 2016

This worked perfectly for me. I changed this line however:

   var PUBLIC_DEV_SERVER = "http://my-dev-server:4000/";

Replaced it with so that it is agnostic of the devserver:

   const os = require("os");
   const PUBLIC_DEV_SERVER = `http://${os.hostname()}:4000/`;

@praweb
Copy link

praweb commented Apr 21, 2017

Tried the same setup.. but end up with "EventSource's response has a MIME type ("text/html") that is not "text/event-stream". Aborting the connection." error. Any idea about this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment