Elixir + Phoenix

Configuring Webpack and React with Phoenix 1.3 (with Heroku deployment)

Jake Curreri
Sep 6, 2018 · 4 min read

If you are building an app with a heavy focus on the front-end. You may want to use Webpack. Especially if you also would like to add React to your project.

We can manually replace Brunch with Webpack. This will remove a lot of headaches during deployment of a Phoenix application (an extensive process in itself).

Starting from version 1.4, Phoenix will support it out of the box for asset management.

Credit to Vitaly Tatarintsev for the initial guidance.

Configure Webpack

We need to remove all the Brunch configuration we already have in our project.

As a first step, we should remove the following libraries from the development dependencies of the package.json file:

"devDependencies": {
"babel-brunch": "...",
"brunch": "...",
"clean-css-brunch": "...",
"uglify-js-brunch": "...",
...
}

(and any other Brunch dependencies you might have)

Install the newly required dependencies:

cd assets
npm install babel-core babel-loader@7 babel-preset-env --save-dev
npm install webpack webpack-cli --save-dev
npm install copy-webpack-plugin css-loader mini-css-extract-plugin optimize-css-assets-webpack-plugin uglifyjs-webpack-plugin --save-dev

Note: At the time of this writing, Babel 7 was just released. If you want to use the latest version of Babel, ignore babel-loader@7 and use babel-loader. You will have to debug quite a bit of preset naming.

Open package.json again and change:

"scripts": {
"deploy": "brunch build --production",
"watch": "brunch watch --stdin"
},

to

"scripts": {
"deploy": "webpack --mode production",
"watch": "webpack --mode development --watch"
},

Now, remove the brunch-config.js file.

rm brunch-config.js

Create a new file: assets/webpack.config.js with the following content:

const path = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');

module.exports = (env, options) => ({
optimization: {
minimizer: [
new UglifyJsPlugin({ cache: true, parallel: true, sourceMap: false }),
new OptimizeCSSAssetsPlugin({})
]
},
entry: './js/app.js',
output: {
filename: 'app.js',
path: path.resolve(__dirname, '../priv/static/js')
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader'
}
},
{
test: /\.css$/,
use: [MiniCssExtractPlugin.loader, 'css-loader']
}
]
},
plugins: [
new MiniCssExtractPlugin({ filename: '../css/app.css' }),
new CopyWebpackPlugin([{ from: 'static/', to: '../' }])
]
});

Add import css from "../css/app.css" on top of the assets/js/app.js file

In the config/dev.exs change

watchers: [node: ["node_modules/brunch/bin/brunch", "watch", "--stdin", 
cd: Path.expand("../assets", __DIR__)]]

to:

watchers: [node: ["node_modules/webpack/bin/webpack.js", "--mode", "development", "--watch-stdin",
cd: Path.expand("../assets", __DIR__)]]

Create a assets/.babelrc file with the following content:

{
"presets": [
"env"
]
}

If further configuration is needed, you can review the babel-preset-env docs.

Pause for a moment and start your server:

cd .. # If you are inside your /assets directory
mix phx.server

If you have any errors, follow the previous steps to debug.

On Default Phoenix CSS

We did not includeassets/css/phoenix.css into the assets/css/app.css file.

Phoenix 1.3 provides us by default glyphicons fonts. However, they are not imported. It was not a problem with brunch because we can’t see any errors related to missing fonts until we try to use them. This workflow changes with Webpack. When Webpack processes the file, it will try to build a dependency graph. If an error occurs, it will crash.

If you desire to use assets/css/phoenix.css you must either delete the line in the minified file or import glyphicons fonts.

Configure React

If you want to install React (or any other front-end framework), continue onward.

First, let’s install additional dependencies.

cd assets
npm install --save react react-dom
npm install --save-dev babel-preset-react
cd ..

Extend presets assets/.babelrc to have react as well.

{
"presets": [
"env",
"react"
]
}

Done! Import all JS files as normal within your app.js file.

Deploying on Heroku

If you are updating from Brunch to Webpack on a production application, you will need to do some deployment modifications.

At the time of writing, the Heroku buildpacks default to Brunch.

Customizing the Buildpack

By default, there is acompile shell script which gets run after building dependencies and just before finalizing the build. The compile file looks like this.

To customize your app’s compile hook, just add a compile file to your app's root directory. compile is just a shell script, so you can use any valid bash code. Keep in mind you'll have access to your node_modules and mix. This means that if you're using a Node build tool other than brunch, you can just do something like:

#./compile

cd $phoenix_dir
npm --prefix ./assets run build
mix "${phoenix_ex}.digest"

Recommended compile file for Webpack is:

./node_modules/.bin/webpack -p

Then, redeploy your application.

SmallWorld

We inspire world-changing ideas and craft cutting-edge technologies through the development of meaningful brands, creative strategies, and innovative software.

Jake Curreri

Written by

ceo @ smallworldus.com, cto @ fitnessoneclubs.com, lover of functional programming, crafter of elixir, ruby, and react applications.

SmallWorld

We inspire world-changing ideas and craft cutting-edge technologies through the development of meaningful brands, creative strategies, and innovative software.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade