Getting LiveView working with --no-assets

I’m trying to build a project using phx.new’s --no-assets flag. I don’t want a frontend build step - only static JS and CSS files. I don’t want to use Tailwind or Esbuild dependencies, however I do want to use LiveView.

LiveView does not work out of the box with --no-assets, because the required setup is not placed into app.js by the generator. Here is the regular generated app.js, and the one for --no-assets. You can see that the 2nd one doesn’t perform the necessary LiveView setup.

If I generate a new phoenix project without --no-assets, compile the assets, and copy the generated app.js to my --no-assets project, LiveView works. But I want to get LiveView working with just static compile-time JS files, without a build step for the JS.

I have attempted to rebuild the app.js file myself, by manually coping the relevant JS files from the dependencies to /priv/static and altering the import statements to things like:

import { Socket } from "./phoenix.js" // not "phoenix"

The browser does load this file, but it generates this error:

The requested module ‘http://localhost:4000/assets/js/phoenix.js’ doesn’t provide an export named: ‘Socket’

Is there a way to get LiveView working out of the box with the distributed Javascript using only the browser? Or do I need to have at least some kind of step to compile the required ‘runtime’ Javascript?

1 Like

When you use the --no-assets option, the generator will create a default js in priv/static/js/app.js

It tells you to manually add theLive View JS

// For Phoenix.HTML support, including form and button helpers
// copy the following scripts into your javascript bundle:
// * deps/phoenix_html/priv/static/phoenix_html.js

// For Phoenix.Channels support, copy the following scripts
// into your javascript bundle:
// * deps/phoenix/priv/static/phoenix.js

// For Phoenix.LiveView support, copy the following scripts
// into your javascript bundle:
// * deps/phoenix_live_view/priv/static/phoenix_live_view.js

It also warns you to use it this way in the mix phx.new documentation

--no-esbuild - do not include esbuild dependencies and assets. We do not recommend setting this option, unless for API only applications, as doing so requires you to manually add and track JavaScript dependencies

I believe your goal is to use Live View without JS bundler
You can absolutely do it, the most direct but not recommended is (for learning purposes) :

  1. Copy deps/phoenix/priv/static/phoenix.js to you priv/static/js

  2. Edit your priv/static/js/app.js to add the following (no import needed)

const csrfToken = document.querySelector("meta[name='csrf-token']").getAttribute("content")
const liveSocket = new LiveView.LiveSocket("/live", Phoenix.Socket, {
  longPollFallbackMs: 2500,
  params: {_csrf_token: csrfToken}
})
liveSocket.connect()
window.liveSocket = liveSocket
  1. Add the following to your root.heex.html :
<script defer phx-track-static type="text/javascript" src={~p"/assets/js/phoenix_live_view.js"}>

However a better way is to use Plug.Static to fetch an updated minified version from the Live View deps

  1. Add the following to your `endpoint.ex
  plug Plug.Static,
    at: "/vendor/phoenix_live_view",
    from: {:phoenix_live_view, "priv/static"},
    only: ~w(phoenix_live_view.min.js),
    gzip: not code_reloading?
  1. Edit your priv/static/js/app.js to add the following (no import needed)
const csrfToken = document.querySelector("meta[name='csrf-token']").getAttribute("content")
const liveSocket = new LiveView.LiveSocket("/live", Phoenix.Socket, {
  longPollFallbackMs: 2500,
  params: {_csrf_token: csrfToken}
})
liveSocket.connect()
window.liveSocket = liveSocket
  1. Add the following to your root.heex.html :
<script phx-track-static type="text/javascript" src="/vendor/phoenix_live_view/phoenix_live_view.min.js"></script>

But once again the recommended approach is to use the --asset option (by default) of mix phx.new

Hope this helps

1 Like

Thank you for the response!

The suggestion to use the fully-qualified module names worked, thank you!

The other requirement was that app.js needs to come after the includes to the phoenix scripts in root.html.heex:

<script defer phx-track-static type="text/javascript" src={~p"/assets/js/phoenix.js"} />
<script defer phx-track-static type="text/javascript" src={~p"/assets/js/phoenix_html.js"} />
<script defer phx-track-static type="text/javascript" src={~p"/assets/js/phoenix_live_view.js"} />
<script defer phx-track-static type="text/javascript" src={~p"/assets/js/app.js"} />

Do you have any idea why the import statement was not working, even though the browser could load the script? Perhaps that only works with esbuild, not when running directly in the browser?

“To import or not to import, this is the question” :laughing:
Javascript has different module formats and each require different type of setup and imports

Phoenix JS is exported in the following formats: ESM, CJS and IIFE.
https://github.com/phoenixframework/phoenix/blob/main/config/config.exs
You can use any depending on your needs

A possible reason the import statement is not working is that your script is not loaded as a module.
Try adding `type=“module” to your script import

You can read more about Javascript modules history and differences
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules

The general advise it to use ESM whenever possible as it offers the latest and more complete features