Liam_or

Liam_or

I’m having difficulty understanding where JS can belong in a liveview app. I understand that JS hooks belong in app.js, but there is only one app.js which means every hook you write, will be bundled and sent to the client. For universal hooks that would be ok, but for component specific js, if a client never navigates to a view which uses it, it would lead to unused JS being sent to the client. It would be lovely to be able to write JS in the heex, and it appears to be possible, but is it safe to write component specific JS within tags in heex?

Showing Posts 1 to 10

frankdugan3

frankdugan3

You certainly could create components that have in JS them, but it does come with a can of worms:

  • Need to remember to load script components when needed by some component on a page
  • If JS is put in the component itself, it will be duplicated
  • A lot of JS needs to be a hook, and as far as I know, there’s not a way to load hooks dynamically

I think it’s important to note that, aside from hooks, there’s no component-level scope for JS in LiveView, so whatever scripts you are doing inside a heex template is going to need to be assigned to some globally accessible variables.

What I have done for really big JS deps (like GraphiQL) is to create a separate bundle and create a specific root layout for the pages that needed it.

I have also done a few “page-level” components for things like dark mode JS and some other little things like that, and those are loaded only once in the applicable root layouts.

frankdugan3

frankdugan3

I forget to say this, but Chris mentioned at the end of the 1.0 blog that collocated JS hooks are an upcoming planned feature for LiveView. So, this could soon be a resolved issue.

D4no0

D4no0

Very nice!

I always advised people to avoid using hooks unnecessarily because of this global nature. This might be the feature that will open the gates for UI component libraries wide open.

LostKobrakai

LostKobrakai

I don’t think colocation will change anything about that. When LV encounters a phx-hook attribute it needs to know what to do with it. That’s a completely separate concern to how you organize source files.

For particularly heavy dependencies I’d probably do a hook, which pulls in the dependency as an async import at the moment. That way the hook definition still works fine and can stay slim and you can actually model the fact that the dependency has some loading associated.

D4no0

D4no0

Since this is a internal implementation detail, it might as well be possible that the core team will find a better way to achieve the same result.

I am more interested in hooks as interaction with UI parts that cannot work without JS, for example rendering a map with maplibre.js, without the JS initialization part this is not possible.

LostKobrakai

LostKobrakai

The most I seem them doing is hoist the “async portion” to the hook level instead of it being part of a hooks implementation. But once they encounter a phx-hook they need to do something and I don’t see much alternative to either “start loading a hook” or “start using a hook”. Eventually that attribute needs to result in code being available to execute. In either way somewhere there needs to be the knowledge of which hooks exist and if they’re to be loaded where to do so. JS already has means for doing that.

I’m not sure I understand what you mean. You want to use maplibre, which seems to be a given. So you have three options.

  1. Bundle maplibre with all your other code.
    single, but large, app.js
  2. Do not bundle maplibre, but have static knowledge about which page to include the js in.
    additional dynamic js linked in head
    caches independently to app.js
    requires full page – including root layout – navigation for LV though
  3. Load the js dynamically when encountering the need for it to be included at runtime.
    async import of additional js file
Liam_or

Liam_or OP

Thanks everyone for chiming in : )
I have some questions if you don’t mind.

  1. @LostKobrakai your point regarding:

Blockquote 3. Load the js dynamically when encountering the need for it to be included at runtime.
async import of additional js file.

Would you mind giving an example of how you’d implement this?

  1. @frankdugan3 Is it possible to load a different ‘app.js’ from the root depending on the session? I.e user and user type?
LostKobrakai

LostKobrakai

For now you’d have something like this

{
  mounted() {
    this.loading = true;
    import("heavy_dep")
      .then(dep => {
        this.loading = false;
        # start using dep
        # assign stuff to this for use in other hook callbacks
        # …
      })
  },
  …
}

Depending on the bundler used this should generate additional js files, which are only loaded once that import function is called.

frankdugan3

frankdugan3

Proabably, but at the very least you’d need to ensure whenever the session changed, you did a full page refresh. TBH, I think @LostKobrakai has the proper suggestion. Dynamically loading the heavy deps of hooks is the most straightforward way to go, instead of splitting up the hooks into top-level bundles.

You could even do some preloading of those deps so there would be no loading latency when the hook is mounted, but the page would still load without waiting for the heavy modules:

<script rel="modulepreload" type="module" src="dynamic-module.js"></script>
Lucassifoni

Lucassifoni

I’m using “dynamic hooks” in an app where liveviews are not part of the main build but loaded at runtime and where I would not want those scripts to be part of the main JS bundle.

def hooks() do
   %{
     act_on_coordinates: ~JS"""
      h.el.addEventListener("mouseenter", (e) => {
         // handle your event
      });
     """
    }
end

...

~H"""
<.register_hook :for={{key, content} <- @hooks} bind="h" name={key} script={content} />
"""

The JS sigil has formatting-on-save built in, I’m working to add syntax coloring. But I allowed myself to do this in my project because I’ve read that co-located hooks will drop at some point. So when it’s the case, I’ll remove this and migrate to the new standard way.
Note that I’m not importing dependencies in those dynamic hooks, they are mostly vanilla JS 1 to 10-liners.

Phoenix and Liveview are quite permissive and provide a lot of foundations to build on - use this to your advantage but know the pitfalls if you deviate too much.

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
brecabral
Documentation While reading the Scoped Routes section, I noticed that the documentation currently refers to a problem without explainin...
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
kpanic
Hi everyone, I am toying with the idea of building a “match maker” for giving personal help to people that wants to start coding. I sta...
New
asweet-confluent
I recently noticed that Elixir’s Logger defaults its primary log level to :debug when no :logger, :level application configuration is pre...
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

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
JesseHerrick
Hey, I’m Jesse and I’m the main contributor behind Dexter, a full-featured, lightning-fast Elixir LSP optimized for large codebases. It s...
New
mudasobwa
I am happy to introduce the very α version of the new programming language compiled to BEAM. Welcome Cure. It has literally three kille...
New
garrison
Hobbes is a low-level distributed database for the Elixir programming language. Hobbes provides a simple, safe, and scalable storage lay...
New
mhanberg
Hi everyone! The first release candidate for the Expert language server project is now available! We’ve published a press release detai...
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

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews