Phoenix 1.6.0 is nearing release, and when it comes out it will ditch Webpack for esbuild. It's a smaller integration, more predictable and just more productive.
There's very little material out there describing how to set up a liveview, tailwind jit and alpine project using Phoenix 1.6.0
Here's a little guide I pieced together out of forum posts, tweets, github issues and IRC. I can't thank everyone who helped me enough, they did the work, I just compiled it here for posterity.
If you're impatient just look at the code commits, it's this article step by step.
Update #1, November 11, 2021:
Alpine ^3.5.0 now targets es2017 so make sure you update
config/config.exs
. Check the last commit in the repo for a quick fix.
At the time of writing, Phoenix 1.6.0-rc.0 is out! phoenixframework.org/blog/phoenix-1.6-relea..
Create your project.
mix phx.new golden --live
In the assets
folder, install dev dependencies, also install alpinejs as a prod dependency.
cd assets/
npm install autoprefixer postcss postcss-import postcss-cli tailwindcss --save-dev
npm install alpinejs
Configuring the Javascript pipeline.
We use esbuild to build our javascript payload.
Make sure you comment out line 3 of app.js
, if you don't esbuild will also compile your css and will thrash any CSS pipelines you set up for tailwind jit. We will use postcss for CSS building later in this article.
// We import the CSS which is extracted to its own file by esbuild.
// Remove this line if you add a your own CSS build pipeline (e.g postcss).
// import "../css/app.css"
Add alpinejs to your app.js
file and make sure you configure the livesocket to call alpine on dom updates.
// import Alpine
import Alpine from "alpinejs";
// Add this before your liveSocket call.
window.Alpine = Alpine;
Alpine.start();
---
// Add dom update support for Alpine.
// before:
let liveSocket = new LiveSocket("/live", Socket, {params: {_csrf_token: csrfToken}})
// after:
let hooks = {};
let liveSocket = new LiveSocket("/live", Socket, {
params: { _csrf_token: csrfToken },
hooks: hooks,
dom: {
onBeforeElUpdated(from, to) {
if (from._x_dataStack) {
window.Alpine.clone(from, to);
}
},
},
});
Configuring the CSS pipeline
This one is pretty complicated but bear with me.
Create postcss.config.js
file in assets
folder.
module.exports = {
plugins: {
'postcss-import': {},
tailwindcss: {},
autoprefixer: {},
}
}
Create tailwind.config.js
file in assets
folder.
module.exports = {
mode: "jit",
purge: ["./js/**/*.js", "../lib/*_web/**/*.*ex"],
theme: {
extend: {},
},
variants: {
extend: {},
},
plugins: [],
};
Add Tailwind's basic css imports to the top of your app.css
file.
@import "tailwindcss/base";
@import "tailwindcss/components";
@import "tailwindcss/utilities";
Finally, add a watcher to dev.exs
so your compiled app.css
is automatically reloaded as you work on your project.
# dev.exs should ultimately end up looking like this:
config :golden, GoldenWeb.Endpoint,
# Binding to loopback ipv4 address prevents access from other machines.
# Change to `ip: {0, 0, 0, 0}` to allow access from other machines.
http: [ip: {127, 0, 0, 1}, port: 4000],
debug_errors: true,
code_reloader: true,
check_origin: false,
watchers: [
# Start the esbuild watcher by calling Esbuild.install_and_run(:default, args)
esbuild: {Esbuild, :install_and_run, [:default, ~w(--sourcemap=inline --watch)]},
npx: [
"tailwindcss",
"--input=css/app.css",
"--output=../priv/static/assets/app.css",
"--postcss",
"--watch",
cd: Path.expand("../assets", __DIR__)
]
]
At this point you should have nice working development environment.
Just run mix phx.server
and visit localhost:4000
Let's figure out deployment to production next.
Create a deploy script in package.json
"scripts": {
"deploy": "NODE_ENV=production postcss css/app.css -o ../priv/static/assets/app.css"
},
Modify the assets.deploy
alias in mix.exs
defp aliases do
[
setup: ["deps.get", "ecto.setup", "cmd --cd assets npm install"],
"ecto.setup": ["ecto.create", "ecto.migrate", "run priv/repo/seeds.exs"],
"ecto.reset": ["ecto.drop", "ecto.setup"],
test: ["ecto.create --quiet", "ecto.migrate --quiet", "test"],
"assets.deploy": [
"cmd --cd assets npm run deploy",
"esbuild default --minify",
"phx.digest"
]
]
end
Finally let's build a build.sh
at root folder deploy script. This will build our app using Elixir releases in prod
MIX_ENV and digest our static js and css assets.
#!/usr/bin/env bash
# exit on error
set -o errexit
# Install deps
npm install --prefix ./assets
mix deps.get --only prod
# Initial setup
MIX_ENV=prod mix assets.deploy
MIX_ENV=prod mix compile
# Migrate the database
MIX_ENV=prod mix ecto.migrate
# Build the release and overwrite the existing release directory
MIX_ENV=prod mix release --overwrite
Give that script run permissions.
chmod a+x build.sh
Run the build script.
./build.sh
Note: This command WILL FAIL because you need to set up a database url environment variable for prod MIX_ENV. Set that up using your database of choosing.