I've been experimenting with Phoenix LiveView for a while now. I have a net-positive experience but can't bring myself to love it or use it for my production projects.
It's an exciting technology where things work, but they don't quite feel right.
JavaScript interop
The biggest issue for me is the interop with JavaScript.
At some point, no matter what your initial expectations were, you need to bridge this gap and use JavaScript. That's where things get hairy.
Initially, you try to make things work with JS hooks, but this quickly becomes difficult to work with. Here's an example from the LiveBeats repo. The markup can very easily get out of sync with JavaScript. Maintainability is also an issue.
Some people pull Alpine, or libraries like LiveSvelte to help with this. But this seems like a weird choice to me. You're niching down your stack even further, and the initial quest to reduce front-end complexity ends up increasing it. The no-js-needed premise is gone, and along with it, you're left with a weird hybrid stack.
You get a bit of styling here, a little bit of front-end logic there, and a little bit of front-end state management over there. All of this, mushed together in the same codebase with the backend. It works, but stepping away for some time, and returning for a new feature, doesn't feel right. You have to reorient yourself, and remember how all this plumbing works.
JavaScript interop should be a first-class citizen and not an afterthought.
Components
The ecosystem is obviously small. That's, of course, expected when you're picking Elixir.
But now, when your front-end uses LiveView and HEEx, you're closing the door to a lot of good stuff out there.
Authoring components in HEEx can be very awkward. Here's part of the input component from the Phoenix skeleton.
It's a god-component that accepts a lot of options, and then does pattern matching to render the appropriate input.
From an Elixir POV, it's fantastic, I love that we can do this. But from a front-end perspective, it doesn't excite me or give me confidence. I can't do a quick prototype, or commit to a long-term project when my re-usable components have to be written in this manner.
There are some third-party component libraries (mostly paid), but they are not as polished as the ones you get with React or Vue.
Code organization
No back-end framework, to my knowledge, provides a good solution for organizing your front-end code, and Phoenix is no exception.
When you start a new Phoenix project, you get all your re-usable components in a core-components.ex
file. How you organize, grow, and maintain this file is up to you.
I agree that this is something unique to each application, but I can't help but feel that the lack of a good solution is a missed opportunity.
When you add LiveComponents to the mix, things get more annoying. Moving stuff around also has the negative of having to update the module name if you want to follow Elixir's naming conventions.
live_session and on_mount
Let's move on. live_session
lets you share the state with multiple LiveViews. So let's say we have this in our router:
Before we render the DashboardLive
or SettingsLive
, we double-check that the user is authenticated in the ensure_authenticated
function.
This looks good on the surface but gets annoying when you have to do multiple checks.
This becomes a headache for me. I understand why you need to do things twice, but it feels awkward, and you can easily mess it up.
In fact, I'm certain that there might be a better way to do this, but if I have to think about it that much, then it's not a good API.
You still need controllers
When I was exploring LiveView, I was hoping that I could drop the controllers and go full-on with this new paradigm, similar to React Server Components.
Essentially, have a single file that does everything for that page.
Unfortunately, LiveView can't do it; you still need controllers. For example, you need to POST to a controller to set the session.
Here's an example from the authentication generator. You create the user in LiveView, but for the session, you need to hit a controller.
I understand the technical limitations, but it's a bummer.
I was hoping for a more holistic approach. It feels weird that I do a mutation in LiveView and then a side effect in the controller.
No matter how you slice it, it's suboptimal.
GenServer centric API
Finally, the API is very GenServer centric. I believe this is intended, as the maintainers want to show you that it's nothing more than a GenServer under the hood.
In my opinion, this is a mistake, and it hurts the adoption. Imagine trying to convince your front-end team to evaluate LiveView, and you show them this:
What is this :noreply
? Is this a side-effect?
Say we generate some boilerplate with mix phx.gen.live Things Thing things name:string description:text
and look at the form_component.ex
.
On version 1.7.18
I get the following:
What is this send(self(), {__MODULE__, msg})
and why is only one handle_event
having @impl: true
? Why do we need this ceremony for a simple form?
For what it matters the missing @impl true
is a mistake, but doesn't break anything.
If I hadn't read Elixir in Action before picking up Phoenix, I would have quit in the first 10 minutes. The API should be simpler, there's no need for the plumbing to be visible.
Conclusion
LiveView is a great tool for internal applications or applications without lots of interactions. You can do cool things and ship them quickly. It's honestly amazing that our solutions are not just React, Vue, or Angular. If it doesn't suit my needs, there are 100 developers who might adore it.
But it's not the silver bullet some people make it out to be.
I wanted to write this post to share my thoughts, and balance the discussion, as most of the posts I've read don't mention some of the issues I've faced.
For the past weeks, I've been toying with Inertia & its Phoenix adapter.
I have to say, it's the most fun I've had coding for a while. I get the things I love about Elixir, and then throw in some React. If you're looking for a way to get the best of both worlds, I highly recommend it. Feel free to reach out if you have any questions, I'll write about it soon as well.