Adding Webpack 3 to Phoenix
Whether or not your motivation for adding Webpack to Phoenix has to do with React, I’ve found Webpacker project template to be one of the easiest ways to get started. You can clone the repo as such or hand pick the interesting bits as we do in the following.
Step 1: New Phoenix Project
$ mix phx.new my_app
Step 2: Copy Webpacker Files
Remove brunch-config.js
and copy files from Webpacker GitHub repo. At the time of writing, the files and folders to copy are:
assets/js/components/
assets/stylus/
assets/.babelrc
assets/postcss.config.js
assets/webpack.config.js
Step 3: Edit package.json
Replace Brunch related scripts with these:
"scripts": {
"start": "npm run watch",
"watch": "MIX_ENV=dev webpack-dev-server --inline --hot --stdin --colors --public localhost:8080",
"deploy": "MIX_ENV=prod webpack -p"
},
Step 4: Install Libraries
$ cd assets
$ yarn remove babel-brunch brunch clean-css-brunch uglify-js-brunch
$ yarn add -D babel-cli babel-core babel-loader babel-preset-es2015
$ yarn add -D babel-preset-es2016 babel-preset-react babel-preset-react-hmre
$ yarn add -D copy-webpack-plugin css-loader extract-text-webpack-plugin
$ yarn add -D file-loader image-webpack-loader postcss postcss-loader
$ yarn add -D react react-dom react-hot-loader style-loader stylus
$ yarn add -D stylus-loader url-loader webpack webpack-dev-server
$ yarn deploy
Alternatively, you can use npm uninstall
, npm i -D
and npm run deploy
.
Step 5: Configure Phoenix
Edit watchers in dev.exs
:
watchers: [
node: [
"node_modules/.bin/webpack-dev-server", "--inline", "--colors", "--hot",
"--stdin", "--host", "localhost", "--port", "8080", "--public",
"localhost:8080",
cd: Path.expand("../assets", __DIR__)
]
]
Add helpers to layout_view.ex
:
def js_script_tag do
if Mix.env == :prod do
~s(<script src="/js/app.js"></script>)
else
~s(<script src="http://localhost:8080/js/app.js"></script>)
end
enddef css_link_tag do
if Mix.env == :prod do
~s(<link rel="stylesheet" type="text/css" href="/css/app.css" media="screen,projection" />)
else
""
end
end
Inside app.html.eex
, replace CSS link with <%= {:safe, css_link_tag() } %>
and JavaScript link with <%= {:safe, js_script_tag() } %>
.
If you like, you can add the example React component to index.html.eex
and verify rendering with mix phx.server
.
That’s it. You’re now officially using Webpack with Hot Module Replacement (HMR). The rest of the steps are purely optional.
Step 6: (Optional) Edit Path Helpers
If you need separate JS & CSS for admin panel, here’s how to remove hard-coded file names from your views.
Replace helpers in layout_view.ex
with these:
def webpack_js_path(conn, path) do
if Mix.env == :prod do
static_path(conn, path)
else
"//localhost:8080#{path}"
end
enddef webpack_css_path(conn, path) do
if Mix.env == :prod do
static_path(conn, path)
else
""
end
end
Inside app.html.eex
, replace CSS link with
<link rel="stylesheet" href="<%= webpack_css_path(@conn, "/css/app.css") %>">
and JavaScript link with
<script src="<%= webpack_js_path(@conn, "/js/app.js") %>"></script>
Once again, you can verify rendering with mix phx.server
.
Step 7: (Optional) Replace Stylus with Sass
$ cd assets
$ yarn remove stylus stylus-loader
$ yarn add -D node-sass sass-loader
Delete folder assets/stylus
, rename app.css
to app.scss
and finally replace Stylus entries inside webpack.config.js
as follows:
stylus/app.styl > css/app.scss
(css|styl) > (css|scss)
stylus-loader > sass-loader
.styl > .scss
Step 8: (Optional) Remove React
Delete folder assets/js/components
, remove React related lines from app.js
and type
$ cd assets
$ yarn remove react react-dom react-hot-loader