DEV Community

Caleb Weeks
Caleb Weeks

Posted on • Updated on

Phoenix 1.6 + Vue (esbuild)

Phoenix 1.6 dropped support for webpack in favor of esbuild. Since this is a relatively recent update, most tutorials about using a React or Vue with Phoenix require modifying a webpack config. I followed the instructions on this page to get esbuild working. Here is a brief tutorial on how to get Vue working with esbuild in Phoenix 1.6.

The final working code can be found here:
https://github.com/weeksseth/phoneix_vue_chat

Create a Phoenix project

Assuming you have installed Elixir, Hex, and Phoenix (v 1.6+), create a new Phoenix project using mix phx.new <project_name>. I added the --no-ecto flag since I am not using a database at the moment.

Configure esbuild

Change directory to the assets folder and install the required dependencies:

npm i esbuild esbuild-vue -D
npm i vue ../deps/phoenix ../deps/phoenix_html ../deps/phoenix_live_view
Enter fullscreen mode Exit fullscreen mode

Create a assets/build.js file and add the following code to it:

const esbuild = require('esbuild')
const vuePlugin = require("esbuild-vue")

const args = process.argv.slice(2)
const watch = args.includes('--watch')
const deploy = args.includes('--deploy')

const loader = {
  // Add loaders for images/fonts/etc, e.g. { '.svg': 'file' }
}

const plugins = [
  vuePlugin()
]

let opts = {
  entryPoints: ['js/app.js'],
  bundle: true,
  target: 'es2017',
  outdir: '../priv/static/assets',
  logLevel: 'info',
  loader,
  plugins
}

if (watch) {
  opts = {
    ...opts,
    watch,
    sourcemap: 'inline'
  }
}

if (deploy) {
  opts = {
    ...opts,
    minify: true
  }
}

const promise = esbuild.build(opts)

if (watch) {
  promise.then(_result => {
    process.stdin.on('close', () => {
      process.exit(0)
    })

    process.stdin.resume()
  })
}
Enter fullscreen mode Exit fullscreen mode

Modify the watcher in config/dev.exs to use node:

config :hello, HelloWeb.Endpoint,
  ...
  watchers: [
-     esbuild: {Esbuild, :install_and_run, [:default, ~w(--sourcemap=inline --watch)]}
+     node: ["build.js", "--watch", cd: Path.expand("../assets", __DIR__)]
  ],
  ...
Enter fullscreen mode Exit fullscreen mode

Modify the aliases in mix.exs to install npm packages during setup:

defp aliases do
    [
-     setup: ["deps.get", "ecto.setup"],
+     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": ["esbuild default --minify", "phx.digest"]
+     "assets.deploy": ["cmd --cd assets node build.js --deploy", "phx.digest"]
    ]
  end
Enter fullscreen mode Exit fullscreen mode

Remove the esbuild configuration from
config/config.exs:

- config :esbuild,
-   version: "0.14.0",
-   default: [
-     args:
-       ~w(js/app.js --bundle --target=es2017 --outdir=../priv/static/assets --external:/fonts/* --external:/images/*),
-     cd: Path.expand("../assets", __DIR__),
-     env: %{"NODE_PATH" => Path.expand("../deps", __DIR__)}
-   ]
Enter fullscreen mode Exit fullscreen mode

And finally remove the esbuild dependency from mix.exs:

  defp deps do
    [
      {:phoenix, "~> 1.6.6"},
      {:phoenix_html, "~> 3.0"},
      {:phoenix_live_reload, "~> 1.2", only: :dev},
      {:phoenix_live_view, "~> 0.17.5"},
      {:floki, ">= 0.30.0", only: :test},
      {:phoenix_live_dashboard, "~> 0.6"},
-     {:esbuild, "~> 0.3", runtime: Mix.env() == :dev},
      {:swoosh, "~> 1.3"},
      {:telemetry_metrics, "~> 0.6"},
      {:telemetry_poller, "~> 1.0"},
      {:gettext, "~> 0.18"},
      {:jason, "~> 1.2"},
      {:plug_cowboy, "~> 2.5"}
    ]
  end
Enter fullscreen mode Exit fullscreen mode

Add Vue

Create a new Vue component in assets/js/components/Component.vue with the following content:

<template>
  <h1>Hello world!</h1>
</template>
Enter fullscreen mode Exit fullscreen mode

Replace the code in assets/js/app.js with the following:

import Component from "./components/Component.vue";
import Vue from "vue";

new Vue({
  el: "#app",
  render: (h) => h(Component),
});
Enter fullscreen mode Exit fullscreen mode

Add the following code to the end of lib/<project_name>_web/templates/page/index.html.heex:

<section id="app">
</section>
Enter fullscreen mode Exit fullscreen mode

Finally, start up your Phoenix server with mix phx.server and you should see the default Phoenix app with a section at the end greeting the planet. If you modify the Vue component and save it, the page should automatically rerender with your changes.

What now?

This is the bare minimum required just to get Vue working with Phoenix. The components folder probably shouldn't be in the js folder since they are Vue components. Phoenix also comes with templates and layouts that you can choose to mix with Vue if you want. You'll probably want to come up with a better folder structure and change the entry point to the application. I don't know the best practices for doing this, so have fun!

Oldest comments (10)

Collapse
 
josempena profile image
JoseMPena

All good but the following error pops up:
ReferenceError: vuePlugin is not defined

And indeed, I cannot see where the vuePlugin function comes from.

Collapse
 
sethcalebweeks profile image
Caleb Weeks

Good catch. Add const vuePlugin = require("esbuild-vue"); to the top of the assets/build.js file.

Collapse
 
josempena profile image
JoseMPena

Hi. Any idea how to add Tailwind css to the pipeline?
I've been trying for the last 4 hours with no success.

Thanks.

Thread Thread
 
sethcalebweeks profile image
Caleb Weeks

I found these two articles which may help. I'm not planning on using Tailwind for this project at the moment, but if I end up figuring it out, I'll let you know!

Thread Thread
 
josempena profile image
JoseMPena

Hi, thanks for replying. I finally got it working using a custom pipeline based on the very articles you shared. If you ever need to take a look, just drop a message.
Thanks.

Thread Thread
 
sethcalebweeks profile image
Caleb Weeks

Awesome! Glad it worked out!

Collapse
 
egagodzy profile image
egagodzy

dev-to-uploads.s3.amazonaws.com/up...
This does not work. I guarantee committing according to the guide, getting an error. I downloaded your stack from github and it also gives an error.

Collapse
 
sethcalebweeks profile image
Caleb Weeks

Try running npm install in your assets directory.

Collapse
 
egagodzy profile image
egagodzy

im trying, same error

Collapse
 
egagodzy profile image
egagodzy

may be, it's only for version 1.6? not for 1.6.6?