MatinDevs

MatinDevs

Proposal: Prefetching in Phoenix LiveView

Currently, there are ongoing discussions about enhancing Phoenix LiveView, particularly focusing on improving performance and user experience. One prevalent area of exploration is the introduction of prefetching capabilities. This feature would allow the application to preload content before it is requested by the user, leading to significantly quicker responses and a more seamless interaction with the interface.

While many Phoenix developers have outlined the potential benefits of prefetching, they often fall short in detailing the implementation process. To address this, my proposal emphasizes clarity and conciseness in articulating how prefetching can be integrated into LiveView.

Benefits:

  • Preload likely-to-be-needed content before user interaction.
  • Significantly reduce perceived latency in view transitions.
  • Maintain LiveView’s simplicity while adding powerful optimization options.

To streamline feedback and contributions, I have created a dedicated repository on GitHub. I invite you all to review the detailed proposal, provide your insights, and contribute to its development. You can find the repository here: LiveView Prefetching Proposal.

Although the proposal might not be completely ready yet, I welcome all contributions and updates from the community. We are committed to seeing this feature implemented soon.

Looking forward to your feedback and contributions!

Marked As Solved

steffend

steffend

Phoenix Core Team

Hey @MatinDevs,

thank you for the detailed proposal. Here are some comments:

  1. State Management
    • “When the server responds with a prefetched view, it stores all of the assigns related to that view inside the current assigns collection”
      • this cannot work because LiveViews are by definition their own process. Therefore prefetching must also use a separate process as usual to not break existing guarantees (imagine someone having a <%= send(self(), ...) %> in their template. This message must not escape to the LiveView that initiated prefetching.
    • “When the client sends a prefetch request, the server sends the view, which is cached by the client in the browser session storage”
      • just a comment: LiveView already has the concept of a Rendered structure that stores a view’s static and dynamic parts in memory in the browser. This is what could also be used when implementing something like prefetching. Using the browser’s session storage would most likely not be a good idea here.
  2. Rendering Logic for Prefetching
    • “During prefetching, the system renders the view with all elements and logic except for asynchronous functions […] (this decision ensures maximum compatibility with previous versions of LiveView)”
      • if the goal is to reduce latency, why should async tasks not be executed?
  3. “Prefetching logic can be bundled into a separate, optional file, allowing projects that do not require prefetching to exclude it entirely for simplicity and performance optimization.”
    • The LiveView JavaScript bundle always contains all the LV code. There is no tree-shaking or similar at the moment. I also don’t think that prefetching would justify adding it. So this part of the proposal can be removed because it’s not needed.
  4. Prefetch Cache Management
    • “To enhance control, a function will be introduced to clear the prefetch cache for the entire site. This is particularly useful in scenarios like user logout, ensuring sensitive data is not inadvertently cached.”
      • Logged in and logged out LiveViews should use separate LiveSessions and I’d say views across those must not be prefetched. A request to prefetch those should be denied by the server.
      • If implemented through in-memory Rendered objects, a full page refresh would clear the prefetch state automatically.

With all that said, a realistic implementation of prefetching would almost definitely require adoption of Liveview to be implemented first. As mentioned, separate LiveViews are always their own processes and this is not something we can change. But even if did have adoptable LiveViews - meaning we can spawn a new LiveView process beforehand for prefetching and “adopt” it when the real navigation happens, there is still one problem:

How would patch navigation work? By definition, a patch goes to the same LiveView and therefore the same process. This is a predicament since:

  • a prefetch should (I’d say must) not have side-effects on the current LiveView, but we cannot transparently calculate the new state in the same process
  • a patch navigation can depend on the current state of the LiveView, therefore just using a separate process also won’t just transparently work
  • → one solution would be to just not support prefetching for patch

Those are all my thoughts at the moment. While I don’t see a good way forward for this at the moment, I still want to thank you very much for the proposal and the thoughts you put into it!

Also Liked

garrison

garrison

I read through this and I think it’s an interesting proposal. It seems like you’ve thought about this a lot :slight_smile:

The main differentiator between LiveView and other server-rendered frameworks is that it’s stateful, and can therefore be used for building pretty complex interfaces. As a result, it’s important to consider the consistency guarantees of a UI with pre-fetching built in.

For example, imagine we had a “forum” UI, with tabs representing categories and a “new post” button and accompanying modal. Now imagine a user hovered over a tab (triggering a prefetch), did not click it, and then opened the modal and made a new post in that category.

Then, after selecting the category, the old prefetch assigns would clobber the updated state (the state which includes the new post), and the user would be confused: “I just opened this, why is it out of date?”.

Of course, this does not preclude the inclusion of this feature (a perfectly valid counter would be “don’t use pre-fetching for that”), but I think it’s important to keep in mind that LV apps are usually more interactive and therefore have different expectations with regards to consistency than simple “static” websites. This feature would have to be used carefully.

garrison

garrison

No, but when the prefetch is applied to the page it will be stale (a consistency violation).

To be clear, this is not a problem that has a solution. Prefetches will be stale by definition (it’s essentially a form of caching). I just think it’s worth highlighting that you would have to be careful using such a feature in an interactive app (just like you would have to be careful using caching).

codeanpeace

codeanpeace

And potentially even other users if there’s any soft real time collaboration on shared resources.

In scenarios where prefetching would likely becomes stale, it’d be nice to have a way for the client to render a prefetched/cached view with a “still syncing latest changes” indicator/signifier as it checks with the server to quietly refresh the view if needed. It’d require even more from the server, but users would get their first contentful/meaningful paint sooner with weak/eventual consistency.

Where Next?

Popular in Proposals: Ideas Top

dimitarvp
To @jonatanklosko and @the-mikedavis: I see that there is a Rust crate at crates.io: Rust Package Registry but it is pointing at https:/...
New
dkuku
This is a proposal to make the map key mismatch errors a bit better: Every time I have a typo It’s very challenging for me even when I u...
New
benkimpel
Background I work at a hedge fund and our traders need highly dynamic UIs (think splitters, tabbed panels, tree lists, enormous data grid...
New
superchris
Currently there is no out of the box way to support DOM Custom Events in phoenix. I’ve created a separate library to do this, but I’d lov...
New
pejrich
I propose adding compact_map/2 to the Enum module. What is it? Sometimes you want to map over a collection, but sometimes you want to ma...
New
dvartic
Would there be interest to implement a link/1 that gives to option to operate on other events? So I implemented a simple link/1 function...
New
manhvu
In a large repo, working with module need to add alias too much is quite annoyed and not good for organizing code. I think better add su...
New

Other popular topics Top

ashish173
I am using Ecto timestamps with postgres, I can see the timestamps() use the :naive_dateime but for my use case I wanted to store the ti...
New
sen
Hi All, I set a environment variables in dev.exs , like below code. when i start server, how can i set the ${enable} value? thanks. d...
New
shijith.k
I am trying to start a new phoenix project with elixir 1.9, but mix phx.new does not work. It says that ** (Mix) The task "phx.new" could...
New
msaraiva
Surface is an experimental library built on top of Phoenix LiveView and its new LiveComponent API that aims to provide a more declarative...
564 44139 214
New
Patoshizzle
After calling mix ecto.create I get this error: 17:00:32.162 [error] GenServer #PID&lt;0.412.0&gt; terminating ** (Postgrex.Error) FATAL...
New
Harrisonl
We have an ECS cluster with 4 services, where each task joins a single cluster, via discovery ECS discovery service. Currently when I de...
New

We're in Beta

About us Mission Statement