Phoenix LiveView vs SPA

Theres a recent js library called uce-template, it’s more or less like that but without bundling.

At that point, wouldnt that mean completely dropping liveview and just using channels? You subscribe to a channel with an observable/stream and thats all. Maybe I’m missing something on your idea.

The most painful point in my experiencie is the need to share templates between js components and phoenix, dom diffing is already a solved problem

2 Likes

I had an ideia a while back to generate js templates based on a EEx file but on the couple days I worked on it I couldnt get it working.
My plan was to keep using my server side rendered application as it was and “hydrate” it via websockets when needed.

There are a few being worked on (mostly in proof of concept / toy stages). I think Surface is one that is somewhat influenced by Vue. The bundling aspect is very important though. Without that, it’s a step back in time. Modern UIs are loading assets only as needed, sometimes even using Google Analytics and some simple data science to predict the user’s next page and loading those resources.

One thing that I personally feel is a mistake (one that React makes) is the whole “CSS in JS” concept. The respective languages should be as “pure” as possible. The LAST thing I want to do, is write some kind of Elixir / CSS / JS / HTML Frankenstein syntax.

No, definitely not. In-fact, the communication / protocol layer would actually need to be built up a bit to essentially expose an API (not just hooks). As I mentioned, you could even add a model layer (eg. using JS’s new proxy logic for reactivity) and / or a caching layer (eg. Apollo). But as I mentioned, it’d be important to be completely unopinionated / decoupled from the presentation layer. The current presentation layer could (should) be broken out as the “default” (but easily replaceable).

Per above, if these two things were done, I think the community would solve a lot (if not all) of the other issues. Currently, LiveView feels mutually exclusive. With some key tweaks, LiveView can be “incrementally adoptable” in ways that cater to the needs of the project and team’s comfort level with various technologies.

For me personally, I often work in Vue (Nuxt) for the UI. With a LiveVue™ adapter, we could make the UI even more “dumb”, but leverage routing, hydration, and complex UI components (eg. the dropdown example from above).

3 Likes

It doesn’t really make sense to decouple liveview from the presentation. 90% of liveview is the template compilation and being able to send minimal diffs to the client, so morphdom can do it’s job. It’s a feature of liveview to not need to send raw data to the client. The “protocol layer” you mentioned are exactly those diffs for morphdom. I doubt anything else could use that.

I can however see, that something akin to liveview processes, where a channel connection spawns a single process on the server for communication between client and server is useful. Before liveview I tried to build a svelte store around a channel connection, which sounds like a quite awesome thing. But as mentioned, this has hardly anything in common with liveview anymore.

You might want to try out alpine.js. It works flawlessly on top of liveview and can easily handle pure client side interactions like dropdowns for markup rendered live by liveview.

1 Like

I disagree. I’ve spent a good time reverse-engineering LiveView. Quantity != quality. Just because a lot of the logic is presentation layer oriented, does not mean that the remaining 10% (or whatever it is) isn’t powerful in itself. In-fact, that’s the part I think is most valuable and should be expanded upon to expose a front-end API (not just hooks).

But to be clear, I’m not suggesting LiveView send more data from the server to the client. Rather, you can take the data already being sent (probably even less), and decouple how it is presented to allow for more powerful UI layers (eg. shadowdom / reactive implementations that tie into advanced UI libraries like Material).

Even if morphdom was the best thing since sliced bread, the evolution of JS libraries over the past decade has shown adoption correlates to the library’s ability to be incremental. If you have a shadowdom / reactive app, it’s quite messy to throw in some morphdom into that. It’d be much better to allow UI developers the option to build their own presentation layer on the LiveView protocol.

That’s all just my opinion, but my prediction is a lot of unnecessary friction / resistance to LiveView until there’s a way to make it less mutually exclusive. Even the title of this thread “LiveView vs SPA” exemplifies the problem; which could be rewritten as “How much LiveView in my SPA?”.

I’m not sure how this should be possible any more that it is right now. The minimal diffs are possible only because liveview knows what the client has rendered (and morphdom irons over any inconsistencies). Take that fact away and you’re back to no optimizations. Liveview also can’t do the diffing job if the client should be able to retain client only changes the server doesn’t know of. Alpine.js only works with liveview in a comparable manner, because it basically reapplies its own changes to the dom after each and every update of liveview to alpine managed dom nodes. This would work with different client side tools as well, but alpine just has a one liner to do that.

Sounds like you and I have similar concerns, or thoughts on how LiveView fits in with the last decade of engineering efforts made towards the JS ecosystem…

Wanted to add how an addition of a LiveData, or an API that allows a more incremental adoption of Elixir in a teams’ stack could mean wonders for Elixir adoption in the Node/React/Vue communities.

A team that has heavily invested in React (vue,angular) which, lets be honest, is a lot of teams; will not drop all of it for LiveView, no matter how amazing it is, imho.

Now if you could simplify their frontend code base with a really nice API that allow for them to move all that frontend state to the server, keeping the UI as simple and dumb as possible…now they might actually consider Elixir/Phoenix/OTP.

One thing I am experimenting with at the moment, inspired by @justinmcp efforts with taper and @methyl s LiveData - is an Elixir Redux implementation - that would be as close to the JS API as possible in Elixir.

My thoughts here is that a team of javascript developers could quite easily migrate their frontend Redux logic to Elixir without having to learn too many new paradigms like OTP. Just as webdevs can get going with phoenix without much knowledge of OTP.

Am curious of others thoughts on this.
Maybe a redux like API is a bad idea or wouldn’t work well in elixir?
I believe its’ not about how good the redux API is or how elixiry it is, if it makes it easier for JS devs to use Elixir on the backend, and thus creates some converts out of 'em - I think it is worth it!

Maybe we should break this discussion into another thread…

Its not LiveView vs SPA so much as LiveView <—??—> SPA. There is room for an abstraction that lies somewhere in the middle, allowing state in your SPA to actually be backend state. The API abstracts away 'where the state came from` and includes the ability of pushing the updates from Server -> UI in a reactive fashion…

4 Likes

I totally forgot about Surface! It’s created by @msaraiva so I’m positive it’s going to be excellent! Hopefully it’ll mean I won’t have to use one of the JS frameworks after all (I do like what I’ve seen of Svelte tho).

I will also admit I was feeling a little depressed after reading these threads and looking at how far SPAs have come - it seems as though in the last 18 months or so they’ve really catapulted forward, and I was beginning to worry LiveView may not be enough for us to compete. Many of the comments (and the reminder about Surface) has definitely left me feeling more optimistic.

I think it might be a nice idea to have a number of threads dedicated to individual ideas (and projects like the one you posted). For anyone who wants to start a new branch or related thread, you just need to click on the timestamp shown with each post and then the + New Topic link :023:

1 Like

Hi,
I have been reading this discussion with great interest and have a suggestion to reduce the gap between the current LiveView and a SPA (I did not look at LiveData so I apologize in advance if this what you are already doing).

Currently, LiveView is:

On the server- side: (1) changing assigns following messages received from the client (or from the server), (2) evaluating which assigns have changed, (3) evaluating what are the changes in the templates following the changes in the assigns and computing a diff in terms of HTML, (4) sending the diff over the wire.

On the client side: (1) reading the diff, (2) passing the changes to morphdom to update the Dom, (3) reacting to user actions by transferring messages to the server side in order to change the assigns

What if:

  • The diff was not in terms of html but a of data
  • This diff would be used to update a framework agnostic reactive client store (e.g. Mobx)?
  • All client-server interactions were accessible using js functions rather than bindings?

=> This would enable to have a client store which is reacting to changes of the state on the server side. It would also be possible to connect any js framework (react, vue, etc.) to this store and dynamically update the UI state when the store changes. It would also be possible to bind the framework events to phoenix events.

=> This would be, in a way, simpler than LiveView for many aspects on the client side (no need for phx-update = ignore/append, automatic addition of insert and delete, nice UI components and transitions) while keeping all the power of LiveView (abstraction over channels, state on the server, small diffs (including the idea of temporary assigns) and a simple client-server communication without controllers, etc.).

Is that feasible? I am not an Elixir nor a JS specialist but I went through the LiveView code and I don’t see why it would not be possible. I have the impression that all building blocks are already here and that an efficient diff (i.e splitting static and dynamic parts) of a tree of data could be done in the same way as a tree of html (diffing an ordered map and having the keys as static parts?)

What do you think?
Would that be an interesting option?

2 Likes

UI library adoption has proven to be correlated to its ability to be incrementally adoptable / interoperable. To an extent, Elixir being able to run Erlang code is an example of that. Typically though, that paradigm doesn’t exist in the backend world, and is accomplished through service architectures where you can “incrementally” phase out legacy services with replacements built in new languages. That’s way harder than simply having jQuery and Vue dependencies running on the same page.

I’m not sure the juice would be worth the squeeze. You’d still be writing a lot of JS. Redux is essentially a uniform API that abstracts server and client state via exposing getters and setters (eg. actions / mutations). LiveView provides more value by going further than this by tying into user interaction in a more plug-n-play implementation. Unfortunately, it’s too opinionated and too rigid in its current form (IMO).

1 Like

I wouldn’t get depressed. JS really hasn’t made any major strides recently. Rather masochistically, they create all new libraries that require a lot of effort for very little value, trading an improvement in one area, for a decrement in another.

That said, the overall state of JS has matured over the last decade, and we’re not doing ourselves any favors by comparing LiveView to basic JS implementations while ignoring the incentive/constraint economics.

Per above though, spending a great deal of time on both sides of the equation, breaking the front-end of LiveView into two parts (API and UI) would be hugely impactful due to its ability to be incrementally adoptable via non-morphdom UI. I’ve done prototypes using Vue’s real-time vdom compiler to convert LiveView responses into reactive Vue templates, so I know it can be done.

4 Likes

Would you mind sharing those prototypes? I think they can be a first step to materializing what you’re proposing here. The further we push this discussion forward the higher the probability of someone getting curious enough to elaborate and come out with a proper ‘bridge’ solution.

I completely agree that incremental steps are best so the easier we make it for existing projects to leverage LiveView with more confidence, the better.

@chrismccord what is your take on this?

Not sure how much is ‘alot’ of JS…

Lots of UI challenges people have with LiveView for example require writing JS.

It’s a bit of a false promise live view eliminates the need to understand and write JS… To some extent it is true, but at which point is it too much?

If all you have are a bunch of mostly stateless React/Vue components, is that alot of JS?

I think what quantifies as a lot of JS is all the code in your front end that isn’t just a stateless view…

Think we both are on the same page that reducing the amount of JS required to make a compelling app is what we are after.

The time I have spent looking at LiveView code it appears to me is highly coupled to the concept of a DOM, and the DOM hierarchy. It’s pretty much a virtual DOM in the BEAM. Not sure how the current implementation could be broken into API vs UI bits. That is the motivation to Redex as it is just a composable state graph/tree. Doesn’t know or care anything about DOM.

EDIT:

This sounds pretty cool - I have experimented with server rendered React from phoenix - works well. Was not all that efficient in terms of diffs or anything just plain old server rendered React. I am not all that keen on having to run Node as part of the backend to make this kind of thing work though…

I wasn’t counting “library” code towards the code count (in this context). I was referring to the amount of code needed by the developer to interact with the library code.

If I understand what you’re proposing though, you’re saying that this “server side redux” is the first half of what I am suggesting LiveView be broken out into; with the acknowledgement that you’d either need a UI library that talked to it (eg. something morphdom driven), or build your own. Yes?

If so, I think that has merits (even outside of LiveView), but it feels like a pretty different approach to the LiveView problem/solution. I wasn’t personally suggesting an entirely different approach, simply breaking the current approach up into more modular libraries.

Yeah, I think that might a fair way to describe it (as far as I can see), but I’d disagree in the feasibility to decouple them. Even in cases where markup is sent from Elixir, a real-time compiler can convert this markup into framework code (eg. Vue, React, etc.), allowing your Elixir code to use Vue/React/WebComponent tags (eg. Material components).

Further, by separating communication from presentation, you can create components out of each “view”. These components can have their own markup, styling, scripting, and even JS dependencies. This opens up the ability to do a lot more than just hook in, but rather swap out entire render logic. If there were “single file components” that represented each view, some of the component would execute on the server, and some would execute in the browser. You don’t really get that with the current LiveView implementation.

For simple / static sites, it might feel overkill (warranting a “basic” UI using something like morphdom). But it would solve for many (if not all) of the SPA requirements.

I used Nuxt, so the rendering is done as part of the “UI” code (but technically a separate “server”). Essentially, some of the work is done on page request (UI server to Elixir server), giving you the SEO benefit and hydration performance. Subsequent diffs are done in the browser. This isn’t required though. You could send static markup from Elixir. The browser might re-render since it doesn’t know if it can trust hydration. With a little work though, you probably could do that. Not sure if that all makes sense.

Yes exactly!

It is only different from LiveView imo in that it is not coupled to the DOM or any UI layer, be it HTML, React, Scenic, or some other VDOM - instead of phx-X events that are synonymous with(coupled too?) the DOM - with (arguably) difficult to reason about bubbling characteristics - the only event Redex cares about is an action with a type and optional payload… your reducers are responsible for translating that action into a state change (like a state machine) - this change can be broadcasted as a diff to wherever, instructing your UI to update! All reducers receive the action - they do not need to act on it though - this is done via pattern matching in elixir as apposed to a switch statement in JS…

Yea I’m using this for React - GitHub - revelrylabs/elixir_react_render: React SSR Framework for Elixir - it runs some node processes in a pool on the same server as phoenix for invoking the necessary JS to render the HTML over stdio - its pretty rough - getting it all wired up and everything…and doesn’t help much reducing JS bloat, it has just about the same bloat, now on the server AND the Client :confused: And dealing with the initial render is a nightmare - like now need my API on localhost to access from within these local node processes to make the initial render worthwhile…

Interesting that using something like Clojurescript allows one to use all this ecosystem. There are even wrappers for all these React Based UI toolkits.

I am OK with modern JS, typescript is also not bad.

The idea of transpiling to JS is interesting, especially if it allows a nice full stack experience without any context switching like closure/closure script.

I am of the opinion JavaScript is well suited for what it needs to do in the browser. Just like Erlang was built to solve a specific problem, so was JavaScript.

1 Like

Right and I find that most theme authors or toolkits like Tailwind UI are catered towards Vue, React or AlpineJS.

There’s a bunch of stuff that’s best suited towards client side JS like menus, drop down menus, sidebar menus, tooltips, popovers, copying to clipboard, the visual element of drag / drop, accordions, toggling visibility on simple elements without state and a ton of other little things like maybe wanting to show the character count of a field that’s being typed into or showing the preview color of a hex value where you might not necessarily want to make a server round trip.

And even if you follow the pattern of server rendered templates with sprinkles of javascript, even those sprinkles add up to be enough JS where you want something besides a jumbled mess of jquery and bare bones ES6 features. I think even with LV you’ll end in this same spot.

2 Likes

I’m doing a Phoenix + Custom Elements frontend right now. It’s not messy, really. Except for the part of having templates split between phoenix and js. That disconnect can be hard to tackle.
To be more concrete, say you have a blog article:

<article class="blog-article" data-article-id="<%= @article.id %>">
  <header>
    <!-- some user data -->
    <some-custom-menu></some-custom-menu>
  </header>
  <section class="article-body>
    <%= @article.body %>
  </section>
</article>

Now imagine that some-custom-menu has an option that modifies the structure of the article to a variant that is also specified in phoenix templates(it adds tags, it changes some setting(like “sensible content” on a tweet), you get the idea). Even if you use a builtin extend(like article is="my-article-component">, both component and phoenix need to know about the template, which doesn’t seem like a big deal, but when you have a good bunch of them and you need to update them in two different places, it’s really easy to mess up.

With an SPA you don’t have this kind of issues(your page just works HORRIBLY in a low end device or a pc with outdated hardware, althought this also varies notably not only with complexity, but with the ammount of content you need to update -ie infinite scrolling-), but then you need to run an additional node backend to render it serverside or you lose SEO benefits(googlebot is not the only crawler ot there). And that’s hacky.

The “data diffs in the backend and spa or whatever on the frontend” sounds interesting, but the point would be to close that disconnect while letting the server render stuff, which LiveView achieves in many cases.

Personally, I wouldn’t have “pre-made components like material ui” as a metric, because when I started with VueJS the complain was that there was a lack of component uis and it wasn’t wise to pick it for a new project. Same for svelte until a few months ago. First comes the solution, only then one would care about component libraries if they’re so important.

Most of that comes down do the problems of distributed state. If article is modified by both the server and the client you need some way to consolidate state. Ultimately you either need CRDTs or some other means of getting rid of conflicts between concurrent changes. While there’s e.g. automerge on the js side of things I don’t know of any CRDT implemented in elixir and js besides maybe phoenix presence.

That’s also the reason why I personally don’t see the problems with dropdowns and other similar UI components. Their state doesn’t overlap the state on the server. Say I have a dropdown, where the list of links is handled by liveview. The state for list items is completely separate from if the dropdown is open. The open/close state should imo never be handled by liveview, while the list of items needs the server for getting updated. That’s why the alpine.js integration with liveview works so well – whenever liveview updates alpine does apply it’s local changes on top. One likely could do the same with react or vue or whatever, but there’s just not many people using those libraries like that.

1 Like