Using Tailwind CSS with Phoenix

Published on: 22 December 2020
I wrote this little guide to help anyone try out Tailwind in their Elixir/Phoenix project. It works not just for local development, but also for production. I will be using Tailwind 2.0
and Phoenix 1.5.7
. I wrote it for brand new projects, but you can skip the first section, and follow the remaining steps to add it to an existing project. Let’s get started.
Setting up a new project
Let’s scaffold a new Phoenix project, we’re going to call it tailwind
:
$ mix phx.new tailwind --live
Notice that I’ve also included the --live
argument, which will include Phoenix LiveView. You can skip this step, but if you haven’t tried LiveView, I’d definitely recommend you give it a shot.
You can answer with Y
to install all dependencies, and then go into the project directory:
$ cd tailwind
Phoenix projects include a version of the Milligram CSS framework, which we don’t need. Let’s remove it:
$ rm assets/css/phoenix.css
Then edit assets/css/app.scss
and remove this line:
@import "./phoenix.css";
Finally, rename assets/css/app.scss
to app.css
, since we’re no longer going to use Sass. We’ll replace it with PostCSS in just a moment.
$ mv assets/css/app.scss assets/css/app.css
Now we’re ready to add Tailwind.
Installing Tailwind and dependencies
The recommded way to use Tailwind is as a PostCSS plugin. The latest version of PostCSS at the time of writing is 8.0
. The official guide also recommends adding autoprefixer
, so we will include this, too. Finally, we will add postcss-loader
so we can plug PostCSS into Webpack. This line will install everything in one go:
$ npm install --prefix assets --save-dev tailwindcss postcss postcss-loader autoprefixer
Next, we have to add some basic configuration files for both PostCSS and Tailwind. The following config for PostCSS is the bare minimum you need. Create a file assets/postcss.config.js
and add the following:
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
}
}
Next, create assets/tailwind.config.js
and paste this:
module.exports = {
purge: [
"../**/*.html.eex",
"../**/*.html.leex",
"../**/views/**/*.ex",
"../**/live/**/*.ex",
"./js/**/*.js",
],
darkMode: false, // or 'media' or 'class'
theme: {
extend: {},
},
variants: {
extend: {},
},
plugins: [],
};
Note that we included a few items in the purge
field. This is strongly recommended for production and will significantly reduce your final build size. You can add or remove paths here to make it work for your project setup. Other than this, this file is identical to what you’ll get from running npx tailwindcss init
.
Configuring Webpack
Now that both Tailwind and PostCSS are configured, let’s add them to the build. Modify the module
section of you webpack.config.js
to replace it with this:
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: "babel-loader",
},
},
{
test: /\.[s]?css$/,
use: [MiniCssExtractPlugin.loader, "css-loader", "postcss-loader"],
},
],
}
I’ve also hard-coded NODE_ENV
to production
in the deploy
script found in package.json
. You can skip this if you’re already setting NODE_ENV
in your build environment:
"scripts": {
"deploy": "NODE_ENV=production webpack --mode production",
"watch": "webpack --mode development --watch"
},
Our final step is to include the Tailwind styles in our project.
Including Tailwind
To make all Tailwind classes available, you have to import everything in app.css
. We can use the special @tailwind
macro here:
@tailwind base;
@tailwind components;
@tailwind utilities;
Styles are imported in app.js
and extracted out using Webpack. We need one small change in app.js
since we renamed the file from app.scss
to app.css
:
// We need to import the CSS so that webpack will load it.
// The MiniCssExtractPlugin is used to separate it out into
// its own CSS file.
import "../css/app.css"
Notice that we updated the extension of the import statement.
That’s it, we’re done. Now you can start using Tailwind’s CSS classes in your templates.
Running in production
Thanks to specifying which templates to purge
and setting NODE_ENV
variable, this setup should work in production, too. This is size of the app.css
bundle when you run it in development:
Version: webpack 4.41.5
Time: 5276ms
Built at: 12/17/2020 21:13:49
Asset Size Chunks Chunk Names
../css/app.css 3.71 MiB app [emitted] app
../favicon.ico 1.23 KiB [emitted]
../images/phoenix.png 13.6 KiB [emitted]
../robots.txt 202 bytes [emitted]
app.js 360 KiB app [emitted] app
It contains all classes generated by Taiwind, which is whopping 3.71MB.
Now let’s run the deploy script:
Version: webpack 4.41.5
Time: 12084ms
Built at: 12/17/2020 21:16:34
Asset Size Chunks Chunk Names
../css/app.css 6.03 KiB 0 [emitted] app
../favicon.ico 1.23 KiB [emitted]
../images/phoenix.png 13.6 KiB [emitted]
../robots.txt 202 bytes [emitted]
app.js 101 KiB 0 [emitted] app
app.js.LICENSE.txt 98 bytes [emitted]
Since we haven’t started using any classes, and all unused CSS was removed, the production build ended being just 6.03KB. It is not zero KB because we get some CSS to reset styling across browsers.
This work is licensed under the Creative Commons BY-SA 4.0 International License