What's the possibility of creating a “drop-in component” (React-like) using LiveView?

I’m exploring the possibility of creating a “drop-in component” – something like what React would provide – using LiveView. Obviously, it would still depend on a Phoenix app on the other end of the websocket, but the goal is to provide some markup, js, css for any site to embed with.

Looking closer, I see the wrapping div has attributes: id and data-phx-session with socket ids and session data which I have removed (anonymous is fine for now). I tried setting up a flat html with the needed markup/js, but without the initial page-load happening in the Phoenix app, I see warnings: [warn] Ignoring unmatched topic "lv:" in Phoenix.LiveView.Socket

Should something like this be possible? I imagine it’s super early days for experiments like this, but I’m curious what the thoughts are for the future, at least. This could be a sweet capability to have, allowing portable, “white-label” components to be written in LiveView!

1 Like

This should work just fine. It sounds like you forgot to include the socket "/live", Phoenix.LiveSocket.socket, websocket: true call in the endpoint?

2 Likes

Thank you so much for chiming in, Chris!

If you meant Phoenix.LiveView.socket, then yep, I do have that in my endpoint. I’m actually using the phoenix_live_view_example app, virtually unmodified.

Good to know this should be possible, but it seems that the initial page-load is important for setting up the PubSub? … so without that step, there is no topic when the LiveView connects?

So I’ve been playing around with this some more, and I think I’ve gotten a bit further. This time, I used wget -m to scrape the html and assets for the thermostat page in the examples repo.

Then, in the app.js file, I replaced “/live” in the connection string to “ws://localhost:4000/live”.

In thermostat.html, I experimented to see what I could get away with removing in the div attributes where the liveviews are mounted. In this example, the outer liveview is DemoWeb.ThermostatLive and it mounts the inner liveview, DemoWeb.WeatherLive. Each mounting div has id, phx-view, and phx-session; the inner div has phx-parent-id.

My flat html instance seems to work properly as it connects back to the Phoenix server if I remove the two id attributes and the one phx-session from the inner div.

It seems the outer div’s phx-session is still needed or I get in my JS console: Uncaught TypeError: Cannot read property 'getSession' of undefined. Also, interestingly, I need the inner div’s phx-parent-id, which seems fine empty. If the attribute is missing, I get the same error and a couple others.

The goal here is to try and streamline as best I can the html needed to embed a liveview from outside the Phoenix app. The big lingering question I have is why we need the phx-session on the outer div? It’s long, and we don’t need to hydrate with any data – an empty session is fine for now. Should LiveView be able to init with an empty session if this is missing?

As I originally mentioned, I may be too early with this experiment and all these questions :slight_smile: I’m just hoping that this type of thing will remain an option for folks who want to use LiveView where the initial HTML did not come from Phoenix.

With Phoenix/Elixir generating the template and session, you can request the initial rendered page with jQuery or similar, then connect to the remote socket with a callback. Something like the following on another web server:

import { LiveSocket } from "phoenix_live_view"

let liveSocket = new LiveSocket("ws://localhost:4000/live");

$(document).ready(() => {
  $("#thermostat").load("http://localhost:4000/thermostat", liveSocket.connect);
});

There may be a better way to go about this, but this works if you just need to embed a LiveView component once onto an existing page.

3 Likes