markevans

markevans

Better interop with Javascript libraries (React, Svelte, etc.)

Hi!

I feel like Phoenix is slightly missing a trick when it comes to front-end Javascript libraries like React, Svelte, etc.

I feel that Javascript framework components could be treated as even more of a first-class citizen than they currently are.

I realise that most, if not all, of what you need to do can be done just with LiveView, but there are times when you want a little bit more interactivity, or it doesn’t make sense to have some browser-related state in the LiveView process memory.
In those cases rendering JS framework components (particularly just as leaf nodes) from LiveView is the perfect fit.

Yes, we can use hooks for that, and also yes, there are libraries that provide these hooks, but I believe it could be even easier if Phoenix did half the work for you, i.e. the non-framework-specific parts.

Currently, a library that helps to plug a JS framework component into LiveView has to:

  1. provide elixir code, e.g. a function component, to render the component from the liveview
  2. provide javascript hook code, to mount the JS framework component and update appropriately

If Phoenix did the non-framework-specific parts out of the box (1. and some of 2. above), then for a user to use components from any given JS framework they’d only need the adapter part of 2. above, which would likely live in a third-party npm package (i.e. no need for elixir code at all).

I’ve just released a simple library, Komodo (see Komodo - easily render React, Svelte, Vue, Angular, any JS framework component from Liveview) that does (1) and (2) above, but it would be great if the generic parts weren’t even needed because Phoenix already did it (and had a standard interface for a “js component” that each library would adapt to).

To be specific, the Komodo library currently requires the user to do this in app.js:

import { registerJsComponents } from "komodo";
import componentFromReact from "some-third-party-adapter-lib-for-react";
import SomeComponent from "path/to/some/react/component";

// ...

let liveSocket = new LiveSocket("/live", Socket, {
  hooks: {
   komodo: registerJsComponents({
     MyReactComponent: componentFromReact(SomeComponent)
   })
  }
  // ...
});

If Phoenix already did this, this would become, for example

import componentFromReact from "some-third-party-adapter-lib-for-react";
import SomeComponent from "path/to/some/react/component";

// ...

let liveSocket = new LiveSocket("/live", Socket, {
  jsComponents: {
    MyReactComponent: componentFromReact(SomeComponent)
  }
  // ...
});

instead.

Anyway just throwing around a few thoughts - I hope that made at least some sense!

At the very least it would be great if Phoenix (i.e. HEEX) had built-in support for custom elements (i.e. native web components) that went as far as:

  • allowing props that were more than just strings, e.g. arrays, objects
  • allowing listening to custom events

without requiring the user to write an extra hook for this.
Then at least people could always have the option of wrapping their framework component with a custom element if the above wasn’t feasible.

What do you think? Any thoughts welcome!

Most Liked

Sebb

Sebb

We are just building an app with LV and solidjs.
There is close to zero friction, working very nicely together.

markevans

markevans

nice - what are you using to integrate them - a custom hook? or some library?
I was going to write a solidjs adapter for komodo but didn’t want to have to support too many libraries myself (though would be super easy to do)

Jskalc

Jskalc

Some time ago I’ve created live_vue. Heavily inspired by LiveSvelte, it uses Vite instead of ESbuild for stateful hot reload & SSR bundling in development, also it’s easy to include all .vue files to app bundle by using glob imports. We cannot use esbuild package as it doesn’t support plugins, and we need them to compile vue / svelte files.

All in all, I had very similar thoughts to you, as posted in this twitter thread. You need:

  1. Server side function to render & sync props to your client side component (possibly with many performance optimizations like diffing props)
  2. Client side hook to initialize / update props / pass events from your frontend framework to live view
  3. Bundler configuration to support your framework of choice
  4. SSR configuration - might be not trivial with code splitting, preloading etc.
  5. Macro for creating shortcuts (eg. instead <.vue v-component="Counter"> you can use <.Counter>)
  6. Slots support

I’d say steps 2-4 are dependant on the frontend framework, but it’s not a “huge” difference. I’m considering creating a core library that would install individual frontend frameworks as plugins. So something very similar to what you’ve done with Komodo, just with more features provided. I don’t think it should be part of the LiveView itself, since using it is entirely optional and there are multiple opinions about framework of choice.

Btw, Inertia.js does it in the right way. I think doing something similar but for LiveView makes perfect sense.

Where Next?

Popular in Proposals: Ideas Top

beepboop
(re-post of: Feature idea: measure and expose socket latency · Issue #1890 · phoenixframework/phoenix_live_view · GitHub) This is relate...
New
beno
There appears to be no way to escape whitespace in word lists, so sigil_w (and sigil_W) can only be used for lists of single words. Of co...
New
marcandre
I notice that most events have bindings (e.g. phx-keyup) but not the input event. The input event is the preferred way to interact with ...
New
dibok
Hi, I’m trying to use phoenix.js in my Qt QML project which has it’s own buildin JavaScript engine. Problem is that (what I googled so f...
New
jpbecotte
phx-hook accepts as argument an object where its methods are the lifecycle of the callbacks (mounted, updated, etc.). This is awesome, b...
New
pelopo
Hi, Oficial docs got this wonderful feature to download the ePub version that we can chuck to our kindles and read it. Now we are in the...
New
andreamancuso
Hey folks, This might sound niche, but I think it’s worth bringing up - especially given Phoenix’s reputation for being lightweight, por...
New
mythicalprogrammer
Hello, since the 1.8rc0 was out, DaisyUI was noted to have the benefit of light and dark mode. From the elixir subreddit, it seem a few ...
New
nunobernardes99
On 1.8+, when we generate an authentication system with mix phx.gen.auth we can make use of magic link login which is amazing and a great...
New
moacirbishopcamata
Good morning to the Elixir team. Please excuse me if I’m commenting in the wrong place; please indicate where I should discuss this. I ...
New

Other popular topics Top

jononomo
I am trying to figure out how Mix knows whether the environment is test, dev, or prod – where is this set? Thanks.
New
Darmani72
If I have a post route which an argument: post /my_post_route/:my_param1, MyController.my_post_handler How would get the post params ...
New
gausby
I asked this very same question on twitter and got some interesting feedback, but I thought it would be a good question to ask here as we...
1207 39467 209
New
shahryarjb
Hello, I have map which I want to convert it to string like this: the map: %{last_name: "tavakkoli", name: "shahryar"} the string I ne...
New
AstonJ
Seen any cool LiveView demos, sample apps or examples? Please post them here! :003:
New
marius95
Hello everyone, I try to use an Javascript Event Handler in my root.html.leex file. Therefore I created a function in the app.js file: ...
New
Patoshizzle
After calling mix ecto.create I get this error: 17:00:32.162 [error] GenServer #PID&lt;0.412.0&gt; terminating ** (Postgrex.Error) FATAL...
New
svb
Hi! Currently I want to submit a form by pressing the Enter key. However, since my input field is of type “textarea” this is just adds a...
New
senggen
Erlang/OTP 25 [erts-13.2.2] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] 15:22:35.803 [error] gen_event {lager_file_backend...
New
jason.o
In the code below, if the create action is not set to accept “extra_key” as an input, it errors out with a message shown above. Is there ...
New

Latest on Elixir Forum

We're in Beta

About us Mission Statement