I am new to designing applications in LiveView. I have learned how to implement features using most if not all the provided features of LiveView. However, I cannot find much information on architectures that work well when using LiveView.
For instance, suppose I am implementing pretty much the CRUD features for the people in an application. I want an index that provides a search form and shows > 1 people as a table and equal to 1 person as a detail view. I will also need a show feature without the search form and an edit feature.
Is it better to create a Live module to handle each of these features or a single Live module that renders an appropriate live component for each of the features based on the route information? Are there different requirements/situations that makes each of these preferred? If so, what are they and what are my trade-offs?
That is an interesting question, and I was asked about this during interviews as well, curious to see other people’s answers to that. Honestly, I don’t think there is an established pattern to handle this.
You can handle it into a single module and take advantage of the handle_params(%{"id" => id}, ...) when you need to navigate to a details page for a single record for example.
In my current job we split into different modules, we have one for Index, where we are the list, pagination, filters, search, and a different module to show details. That helps in case we need to add more features to both pages, so keep the modules handling a particular piece of the feature.
If you have a details page that is very simple and just shows information, I see no problems using the first approach I mentioned instead of using a different module just for that.
I’d probably create a component for: table, detail view, edit, and possibly the search. Then you can combine those in whatever arrangement you like.
On the search page you can display either the table or the detail view, depending on the number of results (use pattern matching rather than Enum.count or length to check). On the show feature you show the detail view and the edit can replace the detail view, be in a side bar, modal or on its own page.
This is very much a matter personal preference and context and there is no right answer.
I’ve experimented with toy apps that do each CRUD action as a stand alone LiveView as well as one LiveView per resource, switching on :live_action. I’ve also worked on a production app with real-life users that was one giant LiveView for… all resources! They all have their merits and, from my experience none of them is “better,” it’s what what you are used to.
Having said that, I’ve settled on having one LiveView per CRUD action. I don’t have an abstracted form live component because I started making my new forms only take what is absolutely necessary to create a record, then on success I immediately redirect to the edit action to fill out the rest (I feel like this has become a pretty normal pattern).
Furthermore, as someone who often preaches “For the love of God please let’s follow what the guides suggest unless we have a provably good reason not to,” I really dislike what the Phoenix generators do when it comes to LiveViews. In the docs it’s recommended to not reach for LiveComponents unless absolutely necessary—and yet the built in generators give you a form as a LiveComponent which adds conditionals () to switch between create vs update. The only thing this is DRYing up (vs just using a stateless function component) is the validation event, and yet, it’s not uncommon to have different validation logic based on these two events. To further add to this confusion, the phx.gen.auth generators follow a completely different pattern that is more in line with what I’m suggesting is “better.”
Anyway, a bit off track there, but in short there is no right or better way. Compared to something like React, LiveView has generally better abstractions over state (personal opinion) but when it comes to code organization, it’s really very much up to your personal preference.
use nested Phoenix.LiveView to compartmentalize state, markup, and events (with error isolation)
AFAIK the above is the basis.
For each, there are more considerations we can dive into. Examples:
LiveComponents help reduce payload size when rendering and diffing multiple similar items (like a list or table), and can be used as an optimization (which may or not be necessary)
LiveView streams also help with rendering lists, even infinite lists
It is not possible to set HTTP-only secure cookies over the LiveView WebSocket connection, requiring regular HTTP request/response (this is used in phx.gen.auth)
Also not possible to change the (cookie-based) session to store app state
I really dislike what the Phoenix generators do when it comes to LiveViews. In the docs it’s recommended to not reach for LiveComponents unless absolutely necessary—and yet the built in generators give you a form as a LiveComponent which adds conditionals () to switch between create vs update.
I saw some conversations in GitHub about this. As of Phoenix 1.8, the generators are basically transitioning from showing off features to simple starting points. If you look at the live generator in main now, the live component form is gone.
Oh great! I suspected that it was the case that it was more about showing off features. it made for some annoying convos, though, as I usually point to guides in debates, but when it came to people using LiveComponents for forms they’d say “Well that’s what the generators do” and what could I say?
Personally I lean heavily toward the “one big LiveView” side of things and then use a large number of LiveComponents (e.g. sidebar, list view, item view, forms, modals, etc) to compartmentalize state and event handling.
But this is because my flagship application (wip) is essentially SPA-level interactive (if not beyond) and everything is very tightly coupled.
If your app more closely resembles a traditional form-powered SaaS then multiple LiveViews are probably what you want.
Honestly, I do wonder if we should tone down the docs warning against using LiveComponents. I understand why they were written that way (people can be tempted to use them when normal components are fine), but I feel like there’s a new post on here every week asking “is it okay to use LiveComponents”, all citing the same scary warning even though their use-case is perfectly acceptable.