Phoenix 1.6.0 LiveView + esbuild + Tailwind JIT + AlpineJS - A brief tutorial

not quite as easy as @thomas.fortes suggests…

but close if you are OK with a seperate css file for the font (face) and thus using esbuild for it…

  • download the font Inter font family
  • copy the Inter Web folder into say /assets/vendor/fonts and rename the folder to InterWeb (need to avoid spaces!)
  • change the esbuild args in config.exs to:
    ~w(js/app.js vendor/fonts/InterWeb/inter.css --bundle --loader:.woff2=file --loader:.woff=file --target=es2016 --outdir=../priv/static/assets),
  • notice that the app.js is now outputted inside a js folder
    go to your layout and change the js script src to Routes.static_path(@conn, "/assets/js/app.js")
  • above your app.css stylesheet do a:
    <link phx-track-static rel="stylesheet" href="<%= Routes.static_path(@conn, "/assets/vendor/fonts/InterWeb/inter.css") %>"/>

for bonus points:
add by doing:
npm install --save-dev tailwindcss-font-inter (in the assets folder)
and then add the plugin in the tailwind config:

  plugins: [require('tailwindcss-font-inter')({ 
    importFontFace: false,

now you can use the css class font-inter wherever you want…


will Tailwind be also default for Phoenix 1.6?

No, in this article you can see he is installing it from NPM:

npm install autoprefixer postcss postcss-import postcss-cli tailwindcss --save-dev

1 Like

woo nice! Quick question - how would I enable scss ?

1 Like

Going with dart-sass npm package is one alternative. But, if you only need sass - GitHub - CargoSense/dart_sass: An installer for sass is a very good alternative to consider.

1 Like

Great job @sergio! :clap:

Now that Phoenix 1.6.0-rc.0 is out, I tried to migrate my app and this other post also provided me some insights:

I ended up with something like this:


"scripts": {
    "deploy": "npm-run-all --parallel deploy-styles deploy-files",
    "deploy-styles": "NODE_ENV=production tailwindcss -i css/app.css -o ../priv/static/assets/app.css --minify",
    "deploy-files": "cpx 'static/**/*' ../priv/static",
    "watch": "npm-run-all watch-styles watch-files",
    "watch-styles": "tailwindcss --input=css/app.css --output=../priv/static/assets/app.css --postcss --watch",
    "watch-files": "cpx 'static/**/*' ../priv/static --watch"
"devDependencies": {
    "autoprefixer": "^10.2.6",
    "npm-run-all": "^4.1.5",
    "postcss-import": "^14.0.2",
    "tailwindcss": "^2.2.4"


watchers: [
    esbuild: {Esbuild, :install_and_run, [:default, ~w(--sourcemap=inline --watch)]},
    npm: ["run", "watch", cd: Path.expand("../assets", __DIR__)]

PS.: Even though there’s too much configuration for my taste still, the recompilations are fast, really really fast now with esbuild.

Update: BTW: It seems you don’t need to have postcss installed at all if you are using just Tailwind for your styles. You can replace the deploy script with: NODE_ENV=production tailwindcss -i css/app.css -o ../priv/static/assets/app.css --minify


This guide was super helpful; thank you! I noticed that it doesn’t minify CSS. If you want that, it’s very easy to add if you’re already using PostCSS:

  1. Install CSSNano: npm --prefix assets install --save-dev cssnano
  2. Add it to your postcss.config.js:
module.exports = {
  plugins: {
    "postcss-import": {},
    tailwindcss: {},
    autoprefixer: {},
    cssnano: {
      preset: "default",

esbuild also supports Typescript out of the box; just make sure to read the caveats if you’re going to use it.

1 Like

Just wanted to add a thank you for the guide—was very helpful.

1 Like

Just to offer an alternative solution how to add a font file, you can also include the font using the postcss-url plugin as follows. (cc @cvkmohan )

1. Place the font in assets/fonts

I only need the one variable font file in WOFF2 format so for me it’s at assets/fonts/Inter.var.woff2.

2. Add postcss-url dependency

npm install postcss-url --save-dev

3. Add the plugin to assets/postcss.config.js

Mine looks like this after adding it:

module.exports = {
  plugins: {
    'postcss-import': {},
    'postcss-url': { url: 'inline' },
    tailwindcss: {},
    autoprefixer: {},

4. Add the @font-face definition to your app.css

@font-face {
  font-family: 'Inter';
  font-weight: 1 999;
    url('../fonts/Inter.var.woff2') format('woff2-variations');


The above configuration will inline the font into your CSS like this:

@font-face { font-family: "Inter"; src: url("data:font/woff2;base64,d09G[...]d6eMC") format("woff2-variations"); font-weight: 1 999; }

If you prefer to keep the font(s) in separate files, use copy instead of inline.


i have successfully made new phoenix 1.6.0 app with esbuild+tailwind+alpinejs everything fine even tailwind but tailwind-extension in vscode is not working the auto suggestion’s for tailwind are not showing up, i think it’s not working because of heex file extension.

1 Like

Have you tried changing the extensions in your config? There’s a tailwindCSS.includeLanguages setting. If that doesn’t help you should probably open an issue on Tailwind’s extension repository.

1 Like

Thank you
I tried that setting but no luck it’s still not working,
I’ll create a GitHub issue in their repository

1 Like

If anyone has trouble getting this to work on Windows this did the trick for me.

watchers: [
    # Start the esbuild watcher by calling Esbuild.install_and_run(:default, args)
    esbuild: {Esbuild, :install_and_run, [:default, ~w(--sourcemap=inline --watch)]},
    node: [
      cd: Path.expand("../assets", __DIR__)

You can also do cmd: ["/c", "npx", ... ] to run npx and other things that are .cmd files, which Erlang doesn’t seem to be able to open a port to.


Never considered that. Very helpful.

Are there any tricks/methods for the additional css files you could use as an addition to tailwindcss. Like for example adding @import "./custom-base-styles.css"; to the assets/css/app.css file. Would this still work and where do I need to drop these additional files. When trying via the assets/css/ location doesn’t work.

Has anyone successfully made a Dockerfile integrating his deploy script? I tried generating a mix release.init but for some reason the start command does not seem to get called.


Put the file anywhere in the config directory and when add the path to the esbuild key in config.exs

~w(js/app.js css/custom.css --bundle --loader:.woff2=file --loader:.woff=file --target=es2016 --outdir=../priv/static/assets)

Sharing this here…tailwind with esbuild and phx1.6 WITHOUT the tailwind cli. Uses a build file for esbuild (and the esbuild api). I find this approach much cleaner than having a lot of npm commands everywhere. Also gives you more flexibility with bundling your static assets (the esbuild cli can get long and unwieldy if you are trying to load font files, etc)


I checked out the repo for a sec. Do you see what more there might be to do beyond adding “html-heex” and/or “HTML (HEEx)” into the list of languages?