Hosting a production React app built with Wepback on Heroku

Every time I push to heroku it simply says "Not Found". I am assuming it is because webpack doesn't run?

I've tried all sorts of scripts:

"scripts": {
  "clean": "rimraf dist",
  "build": "npm run clean && NODE_ENV=production && webpack -p --progress",
  "postinstall": "npm run builds",
  "serve": "webpack-dev-server"
}

and another, just without the postinstall.

The one with the postinstall will give me an error, saying webpack wasn't installed (saved under my devDependencies). In the second I get a successful build on the Heroku log but "Not Found" when I load the page.


Solution 1:

You need to put webpack (as well as any other dependencies that you need on Heroku) under "dependencies" in your package.json, not under "devDependencies".

Solution 2:

I think a better way to handle this is to keep your package.json clean, and since Heroku makes it harder to build things, just handle that specific case.

We use the NPM postinstall hook to perform build tasks, load dev dependencies for those tasks, cleanup, etc.

From the scripts section of your package.json:

"scripts": { 
  "postinstall": "node ./ops/heroku-build.js" 
}

And heroku-build.js:

'use strict';

if ('HEROKU' in process.env || ('DYNO' in process.env && process.env.HOME === '/app')){

  const pkg = require('../package.json');
  const ChildProcess = require('child_process');


  let deps = pkg.devDependencies;
  let packages = "";

  Object.keys(deps).forEach((key) => {
    packages += `${key}@${deps[key]} `; // note space at end to separate entries
  });

  try {
    console.time("install");
    console.log("starting npm install of dev dependencies");
    ChildProcess.execSync(`npm install ${packages}`);
    console.timeEnd("install");

    console.time("build");
    console.log("starting npm build");
    ChildProcess.execSync(`npm run build:all`);
    console.timeEnd("build");

    console.time("uninstall");
    console.log("starting npm uninstall of dev dependencies");
    ChildProcess.execSync(`npm uninstall ${packages}`);
    console.timeEnd("uninstall");
  }
  catch (err) {
    console.error(err.message);
  }
} else {
  console.log("Not Heroku, skipping postinstall build");
}

That way if we decide to deploy to AWS or somewhere else in the future, we can handle the idiosyncrasies of their environment separately.

Heroku documentation on how to customize the build process.