Phoenix LiveView pushState support – Change the URL without refreshing the page
Tags: phoenix, liveview, elixir, pushstate
Published: 2019-06-24 14:59:41

---
title: Phoenix LiveView pushState support – Change the URL without refreshing the page
published: true
tags: phoenix ,liveview, elixir, pushstate
canonical_url: https://www.poeticoding.com/phoenix-liveview-change-the-url-without-refreshing-the-page/
cover_image: https://1o6jjh24p1t92lcqci49qg8a-wpengine.netdna-ssl.com/wp-content/uploads/2019/06/liveview_animated_url.gif
---

{% youtube Dad1mKexvu8 %}

**[Subscribe to get FREE Tutorials by email](https://pages.convertkit.com/c021826e86/5ddcb20a27)**. Start learning Elixir and Phoenix to build features and apps!



It's now possible to try the *pushState* support available in Phoenix LiveView (which, remember, is still in beta)

![Add `live_link` and `live_redirect` for push state support](https://www.poeticoding.com/wp-content/uploads/2019/06/liveview_pushstate_github_commit.png)


**What is *pushState* and why can be useful?**

With Phoenix LiveView we can easily update parts of the page without having to change the location, refreshing the page or directly use JavaScript. 

But when the page's content changes, the URL remains the same, making it difficult for a user to bookmark or share the current page state.

The LiveView pushstate support solves exactly this problem and, while changing the page content, we can dynamically update the URL without a full page refresh.

The reason why it's called `pushState` is because it refers to the `history.pushState` function in the HTML5 History API, which gives us the ability to change the URL and push a new page in the browser's history.

![HTML5 history API - pushState](https://www.poeticoding.com/wp-content/uploads/2019/06/history_javascript_pushState.gif)

In this article we are going to test this functionality in Phoenix LiveView, with two different examples:

The first is a LiveView which shows picture thumbnails taken from [unsplash.com](https://unsplash.com). When we click on a thumbnail, the full picture is shown in the same page. We'll see how to use the LiveView pushState support to update the URL, making easy to share one specific picture.

![LiveView Pictures with pushState support](https://www.poeticoding.com/wp-content/uploads/2019/06/liveview_pictures_pushstate-1.gif)

In the second example we'll see something different, an animated URL with emojis. Maybe something we're not going to use in a real app, but something fun to build. 

![LiveView animated URL](https://www.poeticoding.com/wp-content/uploads/2019/06/liveview_animated_url.gif)

You can find these examples in the [poeticoding/phoenix_live_view_example](https://github.com/poeticoding/phoenix_live_view_example) GitHub repo, which is a fork of the original [chrismccord/phoenix_live_view_example](https://github.com/chrismccord/phoenix_live_view_example).

If you haven't tried LiveView yet, subscribe to the newsletter to be updated with new Elixir and Phoenix content. New LiveView introductory content is coming soon!


## LiveView Pictures page

In this example we build a simple LiveView page where we show a list of pictures thumbnails taken from Unsplash. When we click on the thumbnail, the full picture is shown in the page and the URL is updated to something that uniquely refers to that specific picture. 


First, we add the live route in [lib/demo_web/router.ex](https://github.com/poeticoding/phoenix_live_view_example/blob/6f1bc3ab4c96826e13aab538e032799a802df0a8/lib/demo_web/router.ex#L23)

```elixir
defmodule DemoWeb.Router do

  scope "/", DemoWeb do

    live "/pictures", PicturesLive

  end
end
```

and then we create the file [lib/demo_web/live/pictures_live.ex](https://github.com/poeticoding/phoenix_live_view_example/blob/6f1bc3ab4c96826e13aab538e032799a802df0a8/lib/demo_web/live/pictures_live.ex) file where we define the new `DemoWeb.PicturesLive` LiveView module.

```elixir
defmodule DemoWeb.PicturesLive do
  use Phoenix.LiveView
  alias DemoWeb.Router.Helpers, as: Routes
  
  @pictures %{
    "ySMOWp3oBZk" => %{
	    author: "Ludomił", 
	    img: "https://images.unsplash.com/photo-..."
	  },
    ...
  }

  def render(assigns) do
    pictures = @pictures
    ~L"""
    <div class="row">
    <%= for {id, pic} <- pictures do %>
      <div class="column" 
       phx-click="show" phx-value="<%= id %>">
        <%= pic.author %>
        <img src="<%= picture_url(pic.img, :thumb) %>">
      </div>
    <% end %>
    </div>

    <%= if @selected_picture do %>
      <hr>
      <center>
      <label><%= @selected_picture.author %></label>
      <img src="<%= picture_url(@selected_picture.img, :big) %>">
      </center>
    <% end %>
    """
  end

  def mount(_session, socket) do
    socket = assign(socket, :selected_picture, nil)
    {:ok, socket}
  end

  def handle_event("show", id, socket) do
    picture = @pictures[id]
    {:noreply, assign(socket, :selected_picture, picture)}
  end

  defp picture_url(img, :thumb),
    do: "#{img}?w=250fit=crop"
  defp picture_url(img, :big),
    do: "#{img}?w=800&h=500fit=crop"

end
```

For simplicity we use a Map `@pictures`, where the keys are the picture IDs and the values are a Maps with `:img` URL and `:author`  name.

At the bottom we find a multi-clause function `picture_url/2`, which we use to get the thumbnail and large image URL by appending `w`, `h` and `fit` parameters to the `img` URL string.

In the `render/1` function we loop through the `pictures` Map, showing the thumbnails and making them clickable.

```elixir
def render(assigns) do
  pictures = @pictures
  ~L"""
  <div class="row">
  <%= for {id, pic} <- pictures do %>
    <div class="column" 
    phx-click="show" phx-value="<%= id %>">
     <%= pic.author %>
     <img src="<%= picture_url(pic.img, :thumb) %>">
    </div>
  <% end %>
  ...
```

Since `pictures` is a map, in the *generator* we pattern match both the key and value `{key, value} <- map`.

For each picture we show its thumbnail using the `picture_url(pic.img, :thumb)` function, which returns the thumbnail URL.

With `phx-click="show"`  we make the `div` with author name and image clickable. It means that when the user clicks the element, a "show" event is sent to the LiveView process, along with the `phx-value` value.


This event is handled by the `handle_event` function. 


```elixir
def handle_event("show", id, socket) do
  picture = @pictures[id]
  {:noreply, assign(socket, :selected_picture, picture)}
end

```


The `id` argument is the value passes with `phx-value` HTML attribute. In this function we use the `id` to get the picture map and `assign` it to `:selected_picture`.

After clicking the picture thumbnail, LiveView re-renders the view. This time the `@selected_picture` is bound to a picture map (which initially is set to `nil` in the `mount` function), so LiveView renders the HTML with the `:big` image.

![LiveView pictures without pushState](https://www.poeticoding.com/wp-content/uploads/2019/06/liveview_pictures_without_pushstate.jpg)

It works, but as we saw before, it doesn't change the URL, making it difficult to share the page state.

## live_redirect to change the URL using pushState

Let's see now how to change the URL without refreshing the page.
We start by adding a new route in [lib/demo_web/router.ex](https://github.com/poeticoding/phoenix_live_view_example/blob/master/lib/demo_web/router.ex#L24)

```elixir
defmodule DemoWeb.Router do

  scope "/", DemoWeb do

    live "/pictures", PicturesLive
		live "/pictures/:id", PicturesLive

  end
end
```

The new route with `:id` is triggered by passing the picture id in the URL - the case where a picture is selected.

Back to our [PictureLive](https://github.com/poeticoding/phoenix_live_view_example/blob/master/lib/demo_web/live/pictures_live.ex) module, we now have to add the `handle_params/3` function.

```elixir
def handle_params(%{"id" => id}=_params, _uri, socket) do
  picture = @pictures[id]
  {:noreply, assign(socket, :selected_picture, picture)}
end
```

In this function we do exactly what we were doing when handling the *show* event. The `handle_params` is invoked just after `mount/2`, when the user loads the page, and when changing the URL with `live_link/2` and `live_redirect/2`.

To handle the `/pictures` route, we add below another `handle_params` clause, where we set the `:selected_picture` to `nil`.

```elixir
def handle_params(%{"id" => _uri, socket) do
 ...
end

# catchall
def handle_params(_, _uri, socket) do
  {:noreply, assign(socket, :selected_picture, nil)}
end
```

We now update the `handle_event/3` function
```elixir
def handle_event("show", id, socket) do
{:noreply, 
  live_redirect(
    socket, 
	  to: Routes.live_path(socket, DemoWeb.PicturesLive, id)
  )
}
end
```

Instead of assigning the picture (like we did before), we use  `live_redirect/2` which changes the URL to `/pictures/:id` (using *pushState*). 
`handle_params(%{"id" => id}, _uri, _socket)` is then called, using the `id` to assign the `selected_picture`.

![Pictures - LiveView pushstate](https://www.poeticoding.com/wp-content/uploads/2019/06/liveview_pictures_pushstate-1.gif)


## Animated URL 🌝

Let's see now how to create an animation in the address bar, with LiveView and emoji.

As we did for the previous example, we add the two routes [lib/demo_web/router.ex](https://github.com/poeticoding/phoenix_live_view_example/blob/master/lib/demo_web/router.ex#L26)

```elixir
defmodule DemoWeb.Router do

  scope "/", DemoWeb do
  
    live "/moon", MoonLive
    live "/moon/:moon", MoonLive

  end
end
```

And we define the `MoonLive` module in [lib/demo_web/live/moon_live.ex](lib/demo_web/live/moon_live.ex)

```elixir
defmodule DemoWeb.MoonLive do
  use Phoenix.LiveView
  alias DemoWeb.Router.Helpers, as: Routes

  @moons ["🌑", "🌒", "🌓", "🌔", "🌝", "🌖", "🌗", "🌘"]
  @moons_count Enum.count(@moons)

  def render(assigns) do
    ~L"""
    <button phx-click="start">start</button>
    <button phx-click="stop">stop</button>
    """
  end

  def mount(_session, socket) do
    {:ok, socket}
  end

  def handle_params(_, _uri, socket) do
    {:noreply, socket}
  end

  def handle_event("start", _, socket) do
    socket =
      socket
      |> assign(:moon_idx, 0)
      |> assign(:running, true)
    Process.send_after(self(), "next_moon", 100)
    {:noreply, socket}
  end

  def handle_event("stop", _, socket) do
    {:noreply, assign(socket, :running, false)}
  end

  def handle_info("next_moon", socket) do
    idx = rem(socket.assigns.moon_idx, @moons_count)
    moon = Enum.at(@moons, idx)
    
    socket = assign(socket, :moon_idx, idx + 1)
    
    if socket.assigns.running, 
    do: Process.send_after(self(), "next_moon", 100)
    
    {:noreply, 
    live_redirect(socket, 
    to: Routes.live_path(socket, DemoWeb.MoonLive, moon),
    replace: true)}
  end

end
```

`@moons` is a list with 8 frames `["🌑", "🌒", "🌓", "🌔", "🌝", "🌖", "🌗", "🌘"]` we are going to use in the animation, each one is an emoji.

[**render/1**](https://github.com/poeticoding/phoenix_live_view_example/blob/9df35e5b1eea9baa61dccd379b8a85364ab0d8d7/lib/demo_web/live/moon_live.ex#L8)
The `render` is pretty simple: we just have two buttons, *start* and *stop*. Each one sends and event to the LiveView process.

[**handle_event("start", _, socket)**](https://github.com/poeticoding/phoenix_live_view_example/blob/9df35e5b1eea9baa61dccd379b8a85364ab0d8d7/lib/demo_web/live/moon_live.ex#L23)
Clicking the *start button*, we send a *start* event to the LiveView process. This event is handled by `handle_event("start", _, socket)` which sets `:running` to `true` and initialize the `:moon_idx` to `0`. We will use this `:moon_idx` to know at which frame of the `@moons` list we are.

With `Process.send_after(self(), "next_moon", 100)` we send a delayed message (100ms) to the current LiveView process (`self()`), starting the animation.

[**handle_info("next_moon", socket)**](https://github.com/poeticoding/phoenix_live_view_example/blob/9df35e5b1eea9baa61dccd379b8a85364ab0d8d7/lib/demo_web/live/moon_live.ex#L36)
The *next_moon* message is processed by `handle_info("next_moon", socket)`. 

In this function we get the right frame
```elixir
idx = rem(socket.assigns.moon_idx, @moons_count)
moon = Enum.at(@moons, idx)
```

We increase the `:moon_idx`
```elixir
socket = assign(socket, :moon_idx, idx + 1)
```

We check if `:running` is still `true` and we continue the animation sending another delayed `"next_moon"` message, which will be handled by the same function
```elixir
if socket.assigns.running, 
  do: Process.send_after(self(), "next_moon", 100)
```

And like we did in the previous example, we use `live_redirect/2` to update the URL. We pass `moon` which is the string with the emoji we want to show on the address bar

```elixir
{:noreply, 
 live_redirect(socket, 
   to: Routes.live_path(socket, DemoWeb.MoonLive, moon), 
   replace: true)
}
```

With the `replace: true` option we change the current url without polluting the browser's history.


[**handle_event("stop", _, socket)**](https://github.com/poeticoding/phoenix_live_view_example/blob/9df35e5b1eea9baa61dccd379b8a85364ab0d8d7/lib/demo_web/live/moon_live.ex#L32)
Clicking the *Stop* button, we send a *stop* event and the `handle_event("stop", _, socket)` function sets `:running` to `false`, stopping the animation.

```elixir
def handle_event("stop", _, socket) do
  {:noreply, assign(socket, :running, false)}
end
```


![Animated URL with moon emoji](https://www.poeticoding.com/wp-content/uploads/2019/06/liveview_animated_url.gif)