How to restore scroll position in live view app?

What’s the recommended way to restore scroll position in live view apps?

Right now in the default installation, when navigating forward and then back, the scroll position is lost.

Are there any plans to handle it by default?

scroll position should be maintained when clicking back and forward, the same way browser’s handle it because we use push state and let the browser handle the scroll. The only time we scroll to the top of the back is when you click a live_redirect link, which is the same behavior of following a real anchor. Are you seeing differently? What steps and which browser?


Clicking a live_redirect and then going back scrolls to the top in Chrome Version 81.0.4044.138 (Official Build) (64-bit) and Safari Version 13.1 (15609. I’ll try and create a repo on github.

Hm, I’m a bit confused, so clicking a live_redirect link and going back isn’t supposed to restore the previous scroll position?


Going back on link restores the position, but going back on live_redirect doesn’t.


yeah use live_patch…

now you might find yourself with the issue of the page not scrolling to top when navigation… eg scroll to bottom on first page and then navigate(live_patch) and find yourself on middle/bottom of new page (I did)
my app has in live.html.leex:

where the wrapper then acts as kind of a router and renders the different pages…

first I just did a hook on the uri_path with a scroll to top - that worked when clicking links (live_patch) - but going back it be at the top - and forward at the top…

so I ended up putting a hook on all my live_patch links, that sets window.ready_to_scroll = true;:

<%= live_patch to: "#{@locale_prepend}/about", as: :about, replace: false, phx_hook: "ScrollToTop", class: "block sm:mr-4 sm:w-1/4" do %>
  link span/svg
    <% end %>
Hooks.ScrollToTop = {
  mounted() {
    this.el.addEventListener("click", e => {
      if (window.location.href == this.el.href) {
        top: 0, 
        left: 0, 
        behavior: 'smooth'
      } else{
        window.ready_to_scroll = true;


nb: that if it doesn’t navigate (eg I’m on /products and click on /products in the footer or top nav - I assume the user wants scroll to top)

then the uri_path hook (or in my case “active_page” since I don’t navigate on locale change - which changes the uri_path)

<span class="hidden" id="active_page" phx-hook="ChangeActivePage"><%= assigns[:active_page] %><%= assigns[:navigate_counter] %></span>

and the hook, notice the setTimeout… honestly can’t remember if it’s important :man_facepalming: :

Hooks. ChangeActivePage = {
  updated() {
    if (window.ready_to_scroll == true) {
      window.ready_to_scroll = false;
          top: 0, 
          left: 0, 
          //behavior: 'smooth'
       }, 30);


that way clicking links with live_patch scrolls to top - but going back/forward in browser doesn’t…

shoot me a DM and I can send you the link to the prod site for you to check out…


I don’t think I can use live_patch since I’m redirecting to a different live view. I hope there is a simpler solution …

I’ve opened an issue on github:

1 Like

Nice one.

Yeah this seems like expected behavior based on how the internet works in practice.

For example:

  1. Goto
  2. Click the mix.exs file
  3. Scroll to the bottom of the file
  4. Hit back (to go back to the readme file)
  5. Hit forward (to go back to the mix exs file)

At this point your browser will be scrolled to the bottom of the mix.exs file, in other words your scroll state is saved during a page transition. This also works out of the box with Turbolinks too.


I can’t get the expected behavior for the following situation:

  • LiveView
  • Scroll within LiveView
  • Navigate to different page (non LiveView)
  • Navigate back via back button to LiveView

I expect to have the same scroll position, but currently I’m always landing at the top of the page…

This is the same problem I’m working on right now. If I find a solution, I’ll let you know.

That would be wonderful.
I didn’t got the time to investigate the problem more in detail myself.

My tip for investigation is this PR →

But of course I could assist a new PR.

I think you might be right. Before that PR merged it worked.

I’ll see if I can fix it up.

It actually may be this commit:

It set history.scrollRestoration = "manual"

Commenting that line out fixes our issue, but might cause other issues.

PR submitted:


great find… my carefully crafted solution posted earlier stopped working… will give this a test

any updates on this?