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

GregPhx
Greetings Everyone!!! A little bit of my background so it could be easier to understand where my comments are coming from, and to take t...
New
mikesax
On a Rails/Turbo site, the first page is typically loaded using http GET and then sockets are used navigate and replace HTML content for ...
New
pinetops
LiveView is by far my favorite web tech, but a few things have been nagging me. So with all the fancy and ill advised elixir tricks I cou...
New
ffloyd
The Problem Currently, if I define a struct in the following way: defmodule MyStruct do # Both x and y will have the FIXED values unti...
New
Astolfo
When generating a release with phx.gen.release --docker a debian version is used by default to avoid “DNS issues” on Alpine. It seems li...
New
pierrelegall
Problem Currently, List.first/2 and List.last/2 return a default value (or nil) when the list is empty. However, there are cases where an...
New
calebjosue
I am reading (Once more) the Phoenix web framework documentation, in the From endpoint to views section contained in the Request life-cyc...
New
spicychickensauce
I’ve previously explored what is possible today with hacks to implement view transitions in our apps: I have since created a fork to im...
New
altdsoy
Hi there :wave:, I hope everyone is doing well… Tl;Dr: Is there a use case to have Heroicons from an actual hex.pm package instead of G...
New
sodapopcan
So after complaining about this for the third or fourth time on this forum, I figured I should make a proposal. TL;DR with can be hard t...
New

Other popular topics Top

malloryerik
Hi, this is for people who, like me, have had some friction using .html.heex templates in VSCode. The solution seems to be, in a hyphena...
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
Nvim
Anybody knows a comprehensive comparison of Django and Phoenix, thanks for the help. Where are they similar? Where do they differ the m...
New
lessless
I believe there are people here who are dealing with CSV files import on the daily basis, and since Excel is a really popular tool there ...
New
electic
Hi, I am new to Elixir. I am trying to use the DateTime component to insert a date into MySQL however the there seems to be no way to fo...
New
fireproofsocks
Forgive me if this is obvious, but how does one delete a database record WITHOUT selecting it first? Ecto.Repo — Ecto v3.14.0 has exampl...
New
stefanchrobot
What’s the safe way to decode a JSON string into a struct? I want to avoid calling String.to_atom. Jason.decode can give me a map with st...
New
ashish173
I am using Ecto timestamps with postgres, I can see the timestamps() use the :naive_dateime but for my use case I wanted to store the ti...
New
jononomo
For some reason my phoenix channels are working for me in my local dev environment, but as soon as I deploy via Docker, I get a 403 error...
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

We're in Beta

About us Mission Statement