axelson

axelson

Scenic Core Team

One of my LiveView view’s has a very heavy javascript dependency so I want to avoid loading it for every page on the site, while still using live_redirect or similar between the two (avoiding a full-page load). However, I’m running into
https://github.com/patrick-steele-idem/morphdom/issues/178
This means that any <script> tag that I put into the page will not be executed. Of course there are a plethora of work-arounds: https://stackoverflow.com/questions/2592092/executing-script-elements-inserted-with-innerhtml But I figured that someone else has probably tried to solve this before so I am curious what other people have tried or are using.

Showing Posts 1 to 10

Bumppoman

Bumppoman

Any chance you might be able to load it with a hook?

dbern

dbern

I was able to do it via a LiveView hook and webpack’s dynamic imports.

Now, if you figure out <head> tags please let me know. I want LiveView-enabled pages but with opengraph+friends tags based on a resource loaded on mount. I don’t think I can use hooks/JS for that.

axelson

axelson OP

Scenic Core Team

I kind of can but it feels overly hacky. The approach that I’ve come to that partially works is to have a LiveComponent that receives the src href as an assign. Then it creates a <script src="<%= @src %>" onload="onloadfunc()"></script> where onloadfunc is window.loadedScripts = window.loadedScripts || {}; window.loadedScripts['<%= @src %>'] = true. Then in the hook it checks if the script with the current src has been loaded, and if not it uses document.createElement('script') along with appendChild to load the script. I’m not yet sure about pushing it to production.

Hmm, I’m just using separate bundles but not dynamic imports. Do you have an example you could share about how that worked for you? And are you actually using webpack 5.0 (that’s the version of the docs you linked to)?

dbern

dbern

I’m using Webpack 4.43.0. I didn’t notice the docs are for v5, but the dynamic imports are certainly working for me.

I’m going to open-source YouMeet when it launches, so I don’t have the full repo open to browse yet, but here are some snippets that I think covers it.

Here’s an example hook to load a fancy WYSIWYG editor that’s using Vue:

hooks.Editor = {
  mounted() {
    const phoenix = this;
    const target = this.el.getAttribute('phx-target');
    const Editor = () => import(/* webpackChunkName: "editor" */ "./components/Editor.vue");
    () => import(/* webpackChunkName: "highlight" */ "./syntax-highlighter");

    new Vue({
      el: this.el,
      provide: function() {
        return {
          publish: this.publish
        };
      },
      methods: {
        publish: payload => phoenix.pushEventTo(target, "editor-update", payload)
      },
      render: createElement =>
        createElement(Editor, {
          props: {
            initialValue: phoenix.el.dataset.initialValue,
            placeholder: phoenix.el.dataset.placeholder,
          }
        })
    });
  }
};

On the LiveView EEX to load it (actually a LiveComponent but should be about the same)

<div phx-update="ignore" id="Editor-<%= @id %>">
  <div data-placeholder="<%= html_escape(@description_placeholder) %>" 
       data-initial-value="<%= html_escape(input_value(f, :description)) %>" 
       phx-target="<%= @myself %>" 
       phx-hook="Editor" 
       class="container flex h-screen m-4">
  </div>
</div>

and in my Webpack config:

// all the configs plus this:
  output: {
    filename: "[name].js",
    chunkFilename: "[name].bundle.js",  // <-- this line
    path: path.resolve(__dirname, "../priv/static/js"),
    publicPath: "/js/"
  },
// ... no optimizations key
// this may imply that you need to split code yourself.

EDIT: There should be a GIF right here, but it doesn’t seem to load? Sorry, it’s just illustrating how navigating from one LiveView without the script, and then clicking a link to another liveview does trigger the JS fetch and import.
dynamic-import-via-hook

Hopefully I didn’t miss anything!

polypush135

polypush135

What I’ve learned is that if you are using webpack you should be able to dynamically load bundles per hook.
This is how I’m currently doing it.

By using this style in importing.

let Hooks = {
  PageIndex: {
    mounted() {
      import('@mojs/core').then(mojs => {})
...

It only works if you import this way.

idi527

idi527

Not sure if it’s relevant, but: Has anyone used lazy loading in a phoenix live view project?

And the example repo: https://github.com/syfgkjasdkn/lazy-live-view

axelson

axelson OP

Scenic Core Team

Thanks for all the examples. I should be able to get something working with them.

dbern

dbern

Here’s the gif I tried to post earlier: https://imgur.com/a/izmruiM

polypush135

polypush135

That’s a cool way of using async function for hooks.
Thanks for sharing that exmaple.

rhcarvalho

rhcarvalho

This is one of the two best threads that come up from search on the topic, so reviving it with an up-to-date solution for the benefit of all.

Just like the OP, I wanted to use a heavy JS library in a single page without compromising the bundle size for the rest of the app.

Nowadays Phoenix projects use esbuild by default, and modern browsers support ESM modules and dynamic import.

To my surprise, asking Gemini 3 produced a reasonable output, hinting at the right things. In a nutshell:

  • Update the esbuild config in config/config.exs to output the bundle as an ESM module and enable code-splitting:
      --format=esm
      --splitting
  • Update the root template to load the app.js as a module:
- <script defer phx-track-static type="text/javascript" src={~p"/assets/js/app.js"}>
+ <script phx-track-static type="module" src={~p"/assets/js/app.js"}>
  • Within a JS hook, import the dependency in the mount() callback.
Hooks.MyHook = {
  async mounted() {
    await import("...")
  }
}
  • Alternatively, import the dependency within an event handler.
window.addEventListener("myapp:action-with-heavy-dependency", event => {
  import("...").then(...)
})

With that setup, esbuild should produce a small bundle for app.js, loaded on all pages, and additional files that are only imported/downloaded/executed when needed.

Resources

— All posts loaded —

Where Next? Top

Trending in Questions Top

katta
I having some trouble figuring out if I have set myself too strict of standards for my production server. Currently I can handle 75% of r...
New
achenet
Hello, I’m trying to build a basic Phoenix web-app, and I’d like to use Tailwind. However, when I launch mix phx.server, I get an error...
New
Cxx-mlr
I’m working on a small exercise involving update_in/3, and I came up with this solution: data = %{ name: "Periodic Table", category:...
New
ChrisAmelia
I’ve got trouble wrapping my head around the order in which functions are called in this snippet (from Phoenix’s authentication): toke...
New
unaware8150
Hello folks! So at work, we are seeing some situations where we have to define some “fixed” strings that are used across the codebase in...
New
dillonoconnor
Is there any way to avoid the Hologram compiler running when using iex? It seems like the front-end code could potentially be disregarded...
New
thiagogsr
** (ArgumentError) expected :max_attempts to be a positive integer, got: {:@, [line: 10, column: 19], [{:max_attempts, [line: 10, column:...
New

Other Trending Topics Top

GenericJam
Edit: 2026 May 15 - This post is archived. Mob is alive!! Main docs: mob v0.7.11 — Documentation A bit of explanation for the slightly c...
New
garrison
Hobbes is a low-level distributed database for the Elixir programming language. Hobbes provides a simple, safe, and scalable storage lay...
New
budgie
A little off-topic, but I feel like people here have a good head on their shoulders. I used to be quite good at making software. Was luc...
New
KristerV
Hey. Is there anyone here who creates agents in their apps? Not talking about using agents, but creating them. I’m finding it pretty diff...
New
mcass19
ExRatatui lets you cook up rich terminal UIs in Elixir, powered by Rust’s ratatui via Rustler NIFs. Build interactive terminal applicatio...
New
georgeguimaraes
Just published claude-code-elixir, a plugin marketplace for Claude Code with Elixir support. These are the plugins I’ve been using for my...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews