Help me choose between LiveView and Vue

We started out with Phoenix in the backend and Ember.JS with GraphQL in the front end. When LiveView came out we switched early to it and ripped out Ember completely and dropped all GraphQL endpoints. Our productivity increased a lot and we did never look back. We can only recommend it, but as others said, it depends on you application and probably much more on your architecture.

5 Likes

I understood the first part where you integrate and liveview with web components, however you lost me on the second part.

If you ditch liveview in favor of graphql and houdini, how is this different from having to maintain 2 separate applications?

3 Likes

My point is the complexity is about the same using a JS library vs Liveview with the approach I am using.

Design System, Houdini GraphQL on the client, Ash on the backend.

Using Svelte JS with Houdini GraphQL is less friction vs using LiveView because the web component wrappers exist vs having to create my own, but I still have some amount of effort to describe the GraphQL fragments on the client and server, and the counterpoint with LiveView is that I still have to think about routing on the server and mediating between my domain and LiveView, handling events and state.

There is not much separating the two options and it comes down to some other factors:

  • Two languages vs one
  • Can easily implement PWA with Svelte or React
  • LiveView connection handling on the browser is flakey. I have concerns with a jarring LiveView issue “finding the internet” after switching tabs in mobile browsers or open close the device. I’ve seen it on multiple occasions with my own prototypes and on other LiveView sites too. There are persistent GitHub issues that are closed but the issue is not fully resolved.
  • Higher availability of front end developers for svelte or react vs Elixir Pheonix developers.
  • Given the Carbon design system and web components I don’t need or want a front end developer “with flair and creativity”.
  • Avoiding a specific role of front end only developer whilst bootstrapping can allow my team to be more general / backend focused. Anyone with Elixir experience can be reasonably productive with UX guidelines.
  • A UX developer will consider usability and UX factors far more than a backend developer who naturally dislikes doing front end work.
  • If I separate the front end and backend then it is possible to develop features faster. This entails describing the wireframe/prototype UI using a prototyping tool (eg Storybook and UXPin Merge). Then front end and backend developers can work building out their respective sides, with the backend team focusing first on describing the domain model and GraphQL contract in Ash to get a documented API interface.

So its hard to say right this minute where the chips will land. I have a couple of experiments in progress to help me confirm the most productive way forward as it impacts hiring and planning. I have an each way bet in both approaches but right this moment Svelte+Houdini with Ash+Absinthe is looking to be the better choice than LiveView. It may end up that we just use Plug instead of Pheonix as there isn’t much need of Phoenix once it’s GraphQL and some static assets.

4 Likes

Some of the preceding messages sound a bit negative, so I feel like I want to even out the sentiment. We’re pretty happy and productive with LiveView, even without using any component libraries or CSS frameworks. Handling the whole feature from back-end to front-end helps us to keep the user’s needs and the end goal in mind throughout the development process. We avoid any back-and-forth between front-end and back-end developers, which makes it much easier to quickly experiment with different approaches and optimize the feature with our UX designers. We’re usually able to onboard new developers without or with little Elixir or LiveView experience quickly.

6 Likes

I also keep reading all over the forums how there’s a huge gap in X, Y and Z with LiveView. I find it hard to reason with it.

With 3 “full stack” developers we’ve built and maintained an app built entirely with LV since v0.1.0. Since then A LOT has happened with the ecosystem (for the greater good) and besides some upgrades being time-consuming, it’s been a great ride.

We own CoreComponents (and a bunch of other component modules) and I don’t see the problem in reading the diff and updating whatever is necessary in our app - it’s not like it requires constant updates as it’s just a headstart for new apps, eventually it’ll be custom enough for the app’s needs. The app went through own-rolled CSS to Bulma and now Tailwind, all went fine.

We have no complaints from clients about the “We’re trying to find the internet” issue (well, we had once for a client that blocked WebSocket connections).

It does have a lot of accessibility features and tweaks. Enough to grant us contacts with schools districts where it’s required (ed-tech field).
If we were to start a new project now, porting most of the components would be easy. So yeah starting a new project won’t give you all the bells and whistles one would want, but also for many apps and POCs you don’t need the full package. By the time you need everything you’d have sorted it out with your team.

There’s tools around that will get you very far beyond the starting skeleton, like PETAL and friends, and if they help throw them some money. Extend where they don’t.

I have past experience with Ember, Elm, React and Vue both with Rails and Phoenix, and I’m very sure we wouldn’t be able to build everything we’ve built with such a small team and keep shipping features while maintaining everything up to date if we were using anything else.

I can’t see the tooling (or the lack of it?) we have right now being a problem or getting in the way of building a successful business/app.

9 Likes

I’ve been following along and trying to refrain from saying anything but I can’t resist at this point. I think has gone semi-off topic into adoption vs “should I use LiveView or Vue?”

As far as adoption goes, I do think the pre-1.0 label is relevant as it is a marker used by the team for when it’s considered truly “ready”. The Elixir ecosystem in general seems to favour a slow and steady path to adoption. This is particularly evident in telling us that we may get types sometime within the next five years but there’s a chance we may not get them at all. This is in the face of not only a strong desire of a good chunk of the current community asking for it as well as many outsiders claiming this is the one blocker for them even trying the language. All that to say that while we are certainly missing a state-of-the-art headless UI lib, there is precedent in why one hasn’t been pumped as quickly as possible by the core team.

The other thing at play here is that LiveView is very much a “full stack” developer’s framework (as touched on by @shamanime). I’m not certain that it’s ever going to try and be anything else? I honestly don’t know. Personally, I have never used a truly headless UI system in my career, though I’ve been sympathetic to @adw632’s desires for one now that I’m working on my first contract since my spaghetti PHP days. But ya, this has been touched on already but with LiveView there is no “web api” layer between your front and backend (hopefully you have some hard boundary, though, like via contexts) so seeing that as a positive is a big factor in choosing LiveView.

I had a bunch of other stuff but quickly:

I build up my components from CoreComponents and feel no pain but I do think it might be a bit of a mistake for two reasons: The main one is that many people don’t seem to understand that it’s meant as a jumping-off point that you can edit all you want or throw away entirely. I don’t have any examples off-hand but it seems to be hard to convince some people of this. Of course, this means you can’t use the generators though that’s another issue, I’d say. Secondly, I don’t think it’s great that it so heavily integrates Tailwind and there is a bit more work involved in remove it than simply --no-tailwind. Now don’t get me wrong, I’m a Greying Beard who’s been enjoying writing CSS since 1998 and the moment I saw 100% ultility classes in action I was like, “I want that! NOW!” There is was no period of “you just need to use it to understand it” for this guy! Seeing was believing! But I respect choice as well as there are times where Tailwind doesn’t fit the bill.

@hubertlepicki I really like your post and it has a lot of good points. The one thing about it is that it’s very much from the perspective of someone who already knows JavaScript. This is somewhat fair since everyone in web development knows JavaScript to some degree, but there is an equally long laundry list of stuff to know in JS land if you want to become proficent that perhaps you take for granted. For instance I wouldn’t claim that the JS event loop is all that much easier to grasp than message passing when learning from scratch. “Async by default” is certainly more complicated. And when it comes errors, no matter how much they’ve improved, JS errors are still horrendous. No matter how many times I spend a few days working solely in JavaScript, as soon as I go back to any other language and get my first error I get this massive wave of excitement when it actually tells me exactly what’s wrong and exactly where. Finally, mentioning that you have access to things like GraphQL—but if your app fits LiveView, GraphQL won’t even be in the equation.

Ok whoops, that wasn’t “quickly” at all! / :soap: :package:

4 Likes

“Async by default” is certainly more complicated

Oh yes, that’s correct. I really dislike writing “business logic” in JavaScript for that reason. Anything that involves some sort of I/O becomes pain in the bottom, and unreadable at that as well. It’s been designed to handle clicks in the browser and not complicated business logic, and if you have a look at server code for Node.js or Next.js or otherwise JavaScript web servers it’s some scary stuff to see.

I wouldn’t want to rely on JavaScript with all its flaws not to create bugs, security issues by omission by developers mine or otherwise, and it Elixir is just way more stable language and environment to do so.

2 Likes

From my personal experience, I always found developing with liveview faster, I have been using it for most of the projects I worked at since liveivew 0.1. Even with the lack of a good component system, which for me personally is an annoyance because I hate creating designs like a cavemen in a technology of the past, I found it much more enjoyable and fast in the overall picture.

I think the biggest time killer of frontend applications is having to maintain the communication contract. It does seem that graphql is a popular choice nowadays and more efficient than writing custom http endpoints, however this is still the prominent place where a lot of development time is sunk.

I think that by the time you will adapt your application to be PWA, you would have long implemented the functionality natively. I would classify this as people claiming that writing JS on both frontend and backend requires a unified knowledge set, when in reality you have 2 separate runtimes with different constrains and capabilities.

I don’t think this should be a concern worth thinking over. The same could happen with a front-end, where you will timeout a request and place the application in an unknown state or crash it entirely, at least with liveview you have fault tolerance in this case.

Indeed, however the other reality is that finding a few great elixir developers will allow you to replace an army of JS developers. I never worked in teams with a big number of developers, however I think that small focused teams always outperform big teams in terms of performance and quality.

This is a great reason. Developers that are invested fully into the product, will also start to have insights and make correct abstraction and performance decisions, creating a great product is a team game.

I find this argument strange. Can’t you hire someone specializing in frontend development and have him build those things on server-side? I mean he has access to heex templates (that are not much different from what front-end frameworks use), css and js. Make him build everything as components with a good API and there you have it, he writes minimal elixir and you have your divison of responsibilities.

6 Likes

All your points resonate with me very much. It’s just at the end you threw out a pretty big “can’t you just” :wink: I think finding a good frontend developer who is willing to have a hole on their resume missing some front-end JS framework would be pretty tough. Not impossible, but pretty tough! I could be wrong (in fact I hope I’m wrong).

I will also say that if you want a PWA web app—ie, you know many of your users will have JS turned off and won’t have a native version—LiveView is certainly not the way. Rails’ Turbo is actually really good for this. Remix does this, I believe, but then as @hubertlepicki mentioned, you now have your business logic in JavaScript.

But ya, the whole reason I’ve taken interest in this thread is that I’ve been feeling the pain of not having a headless UI. I’m eager to bring Doggo into my next personal project. Unfortunately, I don’t have time to incorporate into my current one.

2 Likes

I really think Doggo will be the start of good things for the LiveView ecosystem.

Similar to @sodapopcan, if I hire a front end developer, it would be because they are experienced and efficient in the front end framework I would be using. It’s not my preferred option and as I stated previously a backend developer is far more useful. Liveview is the solution which avoids the front end JS tar pit and is why I have been experimenting with web components and design systems as a way forward.

Believe me when I say I agree whole heartedly, I’ve never wanted to be in JS or TS land. I chose Elixir because the paradigm is very safe and side effect free.

Yeah I already looked at the options and accessibility and keyboard navigation was not where it needed to be.

Yes this is the benefit of the server rendered approach and cutting out the middle layers.

Depending what the experiments are, prototyping tools can also save development resources whilst the designers play what if

I’ve also worked on larger projects and teams where you did need multiple front end teams and a backend team. This was because there were native clients in the mix as well as web so it made sense to expose an API on the server only. Fortunately that’s not my use case currently.

1 Like

I guess you are right in the current reality, which is sad, the resume matters more than the actual work that was done and the quality of it.

I agree, at least when starting the risk should be minimized. No matter the final decision, I hope that everything will work out for you, as technology choice is only a small part of what lies ahead when starting something from scratch, nonetheless very crucial.

Regarding the OP it’s really hard to answer. I have been there and ended up choosing LiveView for my current project (where it was only me as a web developer but right now we have another developer working with me). I will try to tell a brief story of my experience around the same decision I had to take.

My background for the last 10 years (well, till 2018) has been mostly SPA JavaScript applications (corporate oriented) with some sprinkles of Fullstack development (NodeJS and Django mostly) so when picking up the technologies for my new project (which I started in 2020) I was faced with this decision.
I had React and VueJS experience from past gigs, I had written in-house component libraries for React and also VueJS for the respective projects but then I found Elixir.

Being frustrated with the Node ecosystem always breaking for any random reason and tired of the Python tooling and all (even though Django is great) I was looking into another Language and ecosystem with more guarantees, I found Elixir and fell in love. :slight_smile:

So I had Elixir for the backend (having some previous Functional Languages experience I picked it up fairly quickly) but this was around the time LiveView initially released, so being very new I decided my first draft/PoC would be Elixir Phoenix as the Backend but on the FE I would use a REST service with React (NextJS) on the FE side. The story around headless UI libraries wasn’t as great as it is today in JS land so that was also a bit of a challenge to change the design to match the style we went with in my project, but I ended up re-styling an existing UI library (Chakra UI if I recall correctly).

For a project of one (in the web side, I had someone else working on the data science part) I grew frustrated with managing the complexity of backend development plus all the duplication of logic in the REST API + NextJS. Also, NextJS is a bit more complex that just picking some “regular” SPA library like VueJS. But React was my choice because there were a lot more quality options in terms of libraries for it than for VueJS.

Because of this I started to explore other options and by now LiveView was consolidating as a nice option and with a new developer with me (that lacked experience with React, VueJS, etc), it was time for taking the final decision in what would serve us best.

So I decided to recreate some screens using LiveView in a weekend as a Proof of Concept, to see how fast I could port some screens we already had (we spent most of the time around business logic, backend and other details so the front end wasn’t that far along). I really loved how fast I could go with LiveView and decided to port all the FE to LiveView, dropped the REST service, dropped NextJS and went with LiveView! Life was simpler! Long live the monolith!

Was it a good decision? Yes it was, don’t regret it at all. But what was the challenging parts? Well, this was before Core Components and around LiveView 0.10.

  • LiveView not being a 1.0 release and living on the edge has its drawbacks. There was quite a bit of refactoring when LEEX became HEEX, when the new Functional Components were released, etc, etc, etc. But it wasn’t that bad, maybe I spent 2-3 days tops working on re-writing code to port our user land code to the new versions of Phoenix and LiveView, still beats the complexity of maintaining a REST service and all the complexity of NextJS for me. :slight_smile:

  • Yes, the lack of UI libraries was a bit of a challenge but we managed. More time than I would like was spent working on components, (like inputs, etc,), specially the more complex ones like Autocomplete, etc, but that was to price to pay, again, for adopting bleeding edge tech.

  • Forms with LiveView were a bit complex for my brain initially when managing a multi-step form with lots of state and validations, but after it clicked it all made sense. :slight_smile:

So in the end, this is a complex decision but there isn’t one day I regret betting on LiveView. Also it is quite exciting to see it develop and mature.
While LiveView isn’t perfect (there are no silver bullet solutions), but taking into account complexity and speed of development, nowadays LiveView ranks pretty high on my list of frameworks I would default to whenever considering new product development.

I also started working on writing a UI component library for LiveView. I already re-started several times as I want to get the API right (taking several ideas from other libraries (from Elixir and JS land)).

This will for sure be an endeavour that will take several months, but I want to create something with quality, that hopefully will be a net positive in helping other people adopting Phoenix LiveView as a viable option for new projects.

My idea is to release a Headless UI component library and then a fully styled but customisable component library (probably using TailwindCSS).

Already have the name for the Headless UI one: flint_ui | Hex (ignore it for now, still mostly empty and WIP).
Flint, to help us start… fires! :stuck_out_tongue:

Sorry for the very long post. :confused:

15 Likes

One thing that came up a few times in this thread is the “we lost the internet” thing, ie, once you reopen your laptop (or unlock your phone) you’ll see that message and it will either take a very long time to reconnect of you just have to reload to get it to reconnect.

First off, I see this in development all the time. Furthermore, I have a hobby project that a friend and I use almost daily that is running on a Fly hobby tier instance and this happens every single time without fail. However, I recently deployed a project to a micro EC2 instance and it hasn’t been an issue at all. You do see a flash of it while it reconnects but that’s obviously no big deal. I’m thinking of putting a 500ms delay on showing it.

Anyway, I just wanted to throw that out there for anyone concerned about it, especially if you happen to be playing around with Fly’s hobby tier. I should say that I have no idea if this is a Fly thing or a me thing :person_shrugging:

5 Likes

I was trying to build a headless ui component library, because I really wish to have something like Radix UI for phoenix framework. You can check it out here: Sprout UI - unstyled and accessible components for Phoenix Framework (it’s still WIP though). The main problems I had when implementing those components are:
It’s not that easy to design a flexible and composible component API with heex template
I’d like UI interactions just happen on the client side without an extra request to the server. I’ve seen a few examples of using LiveView to implement something like an accordion. It’s unnecessary to request to the server for just toggling a panel, and the UI feels not responsive at all.
So a lot of user interactions and accessibility issues like aria attributes, focus management and keyboard navigation need to be addressed on the JS-side, but currently the LiveView JS command is still not enough for all of these, so it has to be achieved with JS, and involves client side DOM changes that does not play very well with LiveView DOM patch.
But the biggest problem to stop me from using LiveView is just as @sodapopcan mentioned:

I had a side project using LiveView deployed using Fly’s hobby tier. And I built a multi-step forms but users keeps complaining on connection lost and form state loss when switching apps on mobile, and it takes ages to reconnect.
The LiveView development experience is actually pretty good but the user experience is not that good especially when users are in a slow or unstable network condition. And finally I ended up rewriting it using React, and just use Phoenix for API server.
Currently, I’m quite interested in the concept of local first software and hope that it can be combined with LiveView to solve the offline usability issues and get a more responsive user experience.

2 Likes

That’s good signal that this is a Fly hobby tier problem. I should probably take a closer look at the docs to make sure this isn’t a known thing, but it seems strange. It’s unfortunate regardless since Fly is very Elixir-centric and there are probably a lot of people who evaluate LiveView this way.

UI interactions in LiveView are meant to be done on the client with JS hooks. This has been a feature since at least the second release (v0.1.1), so we were never really intended be using the server to store the state of accordions, menus, modals, etc.

4 Likes

I know that JS interop can be achieved through hooks and that’s what I’m using. The main problem is having to sync client side DOM changes with LiveView DOM patches.

Ideally, yes, we should use JS command or hooks to implement these interactions. But unfortunately I’ve seen some examples of using LiveView to do this, like the accordion in https://www.ash-hq.org , the modal example in Surface , and the expandable sidebar menu in Live Storybook - Welcome Page (no offense here, just trying to give a few examples I’ve seen). Due to my network conditions, it usually takes about a second for the UI to respond after I’ve clicked something :smiling_face_with_tear:

These are the many reasons I’ve decided that the solution has to be a design system and web components first.

Then whether you use a JS framework on the client or server rendered LiveView is second. Either way the UI experience doesn’t really change much.

Ah, ya, when there are examples out there that do this that makes it a bit harder for people to know what to do. It does depends on the content of these things—like if a request needs to be to grab the data anyway, then I can see storing state on the server, but it would still be nicer to show a modal with a loader then bring the content in. This kind of thing isn’t as prevalent in LiveView, though, though hopefully will become more-so with assign_async.

1 Like

Your exploration with SproutUI is a good one in terms of API design, I’ve seen your code and took some ideas, mixed with what I’ve done on my current project.
I have to agree with you. Most of the components I wrote after the LiveView.JS came out are a mix of that and Hooks and they became a bit of a mess. :confused:
For a component library I think the JS command should be basically ignored unless used for dispatching events of course.

The make it or break it for a components library is really to implement a typeahead / autocomplete / combobox component that performs well and that is for sure a challenge in LiveView if mixing Client and Server side for the reasons you mentioned.

I love LiveView but I agree that for the underlying base components the experience needs to be client first and server interactions should be limited to fetching data.

4 Likes

You could potentially use parts of Vue inside LiveView hooks…

I currently have a small alpine-esque utility system to bind text, attributes, classes, events, etc

Here is a small sample of syncing attributes from LiveView to the internal state.

So far this experiment is working quite well. If you don’t mind the extra 20ish kb for importing ref and watch.

import { ref, watch } from "vue";

const fromAttr = (vm, el, attr, type, def) => {
  const fn = () => {
    if (type == "string") return el.getAttribute(attr) || def;
    if (type == "boolean") return el.hasAttribute(attr) || def;
    if (type == "array") return JSON.parse(el.getAttribute(attr) || "[]");
  };
  const _ref = ref(fn());
  vm._updates.push(() => (_ref.value = fn()));
  return _ref;
};

export default {
  mounted() {
    this._updates = []

    const $open = ref(false);

    const $value = fromAttr(this, this.el, 'data-value', 'string', '')
    
    watch($open, () => {
      // Do something when open changes
    });

    watch($value, () => {
      // Do something when value changes
    });
  },

  updated() {
    this._updates.forEach((f) => f())
  },

  reconnected() {
    this._updates.forEach((f) => f())
  },
}
2 Likes