Cool feature in Nextjs, loading the data async, but also loading sync for SEO. Can we build this in liveview?

In NextJS, you can use Suspense to load the data asynchronously and show a loading state.

<Suspense fallback={<JobSkeleton />}>
  <JobList query={query} location={location} />
</Suspense>

What’s very interesting is that:

  1. The job page loads instantly since no data is being fetched.
  2. The job data loads asynchronously from the database.
  3. If you View Source or curl the page, the job data is there, ready for SEO.

I’m wondering, how could you accomplish the same in Liveview? Is it possible?

Here’s the code I’ve tried so far experimenting with this:

defmodule GamedropWeb.TrendingLive do
  use GamedropWeb, :live_view
  alias Gamedrop.Games

  @impl true
  def mount(_params, _session, socket) do
    if connected?(socket) do
      # For browser requests, start with loading state and load data asynchronously
      socket = assign(socket, loading: true, trending_games: [])
      send(self(), :load_trending_games)
      {:ok, socket}
    else
      # For SEO/curl requests, load data synchronously
      trending = Games.trending_games()
      {:ok, assign(socket, loading: false, trending_games: trending)}
    end
  end

  @impl true
  def handle_info(:load_trending_games, socket) do
    Process.sleep(1000)
    trending = Games.trending_games()
    {:noreply, assign(socket, trending_games: trending, loading: false)}
  end
end

This defeats the point of what I’m trying to accomplish because the curl to this URL will load the trending games right away. And in browser, when someone navigates to this liveview, it’ll load the data before transitioning to the url.

Appreciate any tips!

First of all, you may be interested in async assigns, which essentially do what you’ve done in your example - but they take care of some boilerplate for you.

However, for the “SEO” bit, it sounds like what you want is for the server to render the full page unless the client is a “real user”, is that correct?

In order to do that you would have to sniff the user-agent on the initial page load (via a Plug, probably), and then pass that information to the LiveView. Then you could have the LiveView do a synchronous assign (in the dead render) depending on whether the client identifies itself as a crawler.

2 Likes