Updating metatags in Phoenix LiveView (rel canonical etc)

Hello,

I have an e-commerce site and I migrated the site’s search results pages to use LiveView. Users can paginate the search results, and the liveview uses push_patch to update the url with a page param like fakestore.com/search?page=1

I want to be able to canonicalize the search results pages such that fakestore.com/search?page=1 or fakestore.com/search?page=2 or fakestore.com/search?page=n… all point to the same canonical url.

I am having a difficult time figuring out how to add a rel=canonical link to the head of the page. The LiveView in question inherits from a root.html layout that stores all of the static metatags, but I can’t simply put the rel=canonical in root.html because that root layout is shared across many other pages, and I only want the rel=canonical to apply to the search results page.

What is the best method for passing information from a LiveView to the layout?

One idea I have is ditching the live "path" LiveView call in the router and instead use regular controllers/templates. Then, in the controller, I would be able to pass some information about rel=canonical to the conn, and pull that information off of the conn in the root layout. Then I could use live_render to render the liveview from within the controller. However, I would really prefer to avoid making changes like these just for this use case.

Thanks for the help!

3 Likes

Hi,

No good news: but the live title doc gives some advice here:

There is no need to update canonical on live patches. Canonical is used by search engines and search engines won’t rely on live navigation. Instead, they will render the whole page, which will always contain the proper canonical from the initial render.

2 Likes

Would it be possible to dynamically update meta description in a LiveView? To motivate the example, we can imagine a LiveView that accepts url params to display a certain category of products.

example.com/apples should show a list of apples
example.com/bananas should show a list of bananas

In mount of this liveview, the url param is consumed and used to fetch the list of fruit, and also queries for some general information (description about the type of fruit etc.)

What I would like to do is be able to keep this generalized LiveView that can accept arbitrary url params, but dynamically update the meta description so that way the SERP for the page example.com/bananas can show a description of bananas and the SERP for example.com/apples can show a description of apples.

I’ve been reading about updating the title here, but I’m not sure how it may be possible to dynamically add things like meta description to the root layout.

Thanks in advance!

1 Like

For the same reason that José said, is there a real need to have the meta description update? Crawlers will receive the proper meta tags on HTTP request, and as far as I’m aware, no a11y tools use the meta description and friends so I don’t think they need to be updated on live nav?

It’s likely I’m not describing or even understanding my own problem correctly, I appreciate the help.

It’s not so much that I’m trying to update meta description etc on live nav as I am trying to get different meta descriptions in the head of the page for different http requests to urls that are ultimately using the same liveview.

example.com/bananas and example.com/apples both use FruitLive
If I make a request to /bananas I want the banana meta description and if I make a request to /apples I want the apple meta description.

Besides my contrived example here I do feel like it would be useful to be able to conditionally update what is in the root layout depending on certain url params or what have you.

Thanks again, I’m fairly new to elixir so I appreciate the patience with what might be a rather naive set of questions :slight_smile:

If it helps, I’m trying to accomplish what this metatags library does, except for liveview. For regular non liveview routes I’m able to use this metatags library and in the controller I am able to consume the URL params and pass information along before it makes it to the layout. I’m not sure where to intercept the request the same way in the standard liveview lifecycle that doesnt use controllers. By the time I’m in mount it seems too late.

1 Like

From what I understood its not possible to update parts of the root layout, in our case the head element. I’ve read that recommended to not use the LiveView in case you need to update parts of root layout. This is truly sad because I have already built an app in LiveView and did not know about this limitation earlier. I have tried to insert the metatags with js by utilizing LiveView hooks, but it doesnt work unfortunately

We recommend performing regular redirects/anchor links for navigating between different root layouts because things like scripts and styles won’t be unloaded/cleaned up. You can use LiveView perfectly fine in these cases :slight_smile:

Agreed, you can use LiveView fine in the above mentioned cases. I’m appreciative of LiveView, and excited to keep using it. However, in order to properly place structured metadata in the HTML element for a blog post, I would prefer a more efficient path as opposed to querying a post once at the controller-level, to populate layout specific assigns, and again at the LiveView level during server and client-side mounts.

2 Likes

I agree that this is a big gap in the LiveView ecosystem currently. It’s really common to link to Youtube, Tiktok, Instagram, TripAdvisor links and Slack/Discord/Whatsapp will parse the metadata to show a picture and description of the link. We currently can’t do that with LiveView because we can’t touch anything in the <head> aside from title.

phoenix_live_favicon works around this by adding changes to the private fields of LiveView’s patch commands. This was recently broken in LiveView 0.19.0 (that’s fair, it’s a private field in a pre-1.0 release). I’d love to see something like react-helmet where you can simply add/replace head tags further down in the render tree.

I may be misunderstanding but this is not a concern for LV. Whoever is indexing your page is going to perform a complete render, which should have the right canonical metadata. You never need to worry about live updating metadata, because no engine whatsoever will load metadata from the updated page.

You can have a plug in your router or use the :private option to the live macro to define data that goes into the layout. There are likely ways to streamline it but it should be doable. :slight_smile:

So the recommendation would be to use a plug to load the appropriate data to render meta fields in the root layout? That would definitely work and wasn’t something I thought of. But like you said it could really benefit from streamlining:

  • you’re likely duplicating the same logic to load info from the params that is done in the liveview
  • there’s then coupling between the plug and the layouts- you need to make sure every possible meta/head field populated by the various plugs for each page can be interpreted and converted into tags in your layout

A simple example- I pasted a link from my site into WhatsApp today, and WhatsApp correctly showed the title of the page in the link preview. So they did load the LiveView to the point that the assign(socket, :page_title, page.title) had executed. If I could populate other meta tags similarly I think they would also be picked up without needing an additional plug and modifications to the root layouts.

I could also be missing something obvious- my searching hasn’t come up with anything other than this older thread which I resurrected. :grimacing:

1 Like

I think updating metadata should be simpler as well.
It’s not about search engine crawlers, it’s about opengraph metadata for sharing (like you mentioned) that should be live.

2 Likes

It would be great to have an option to dynamically update header tags other than <title>. I agree that one could use a plug for this, but it’s not always practical.

I have the use-case where I show a product in a LiveView and the user can navigate to the next product using a link with patch-navigation. To show a product, I get the id from the params and fetch and assign the product to my socket. I have some access logic here, where not all users can see all products and I show an error if they try to access a product they aren’t supposed to.

Now, product pages need to have proper open graph and twitter meta tags so that if I share a link to a product, the social platform shows the proper preview card. To implement this, I’d need to move my product fetching and access checking logic out of the LiveView and into a plug.

That’s okay if I have only one route where this is needed, but I have multiple routes where I want to change the meta tags dynamically. For example, I also have product overview pages where a user can filter by category. I want to dynamically update the metatags based on the selected category so that any shared link shows a proper preview title. Again, I could split the logic into plug and LiveView, but it just gets too complex at this point and I cannot use patch-navigation anymore.

As a suggestion, could we maybe replace the page_title assign with a dedicated “head” or “meta” map? That way, I could assign whatever I need to the map inside my LiveView which would update my meta tags in the root.html.heex layout if I want.


Update: Don’t tell this to @josevalim but I managed to assign a map to :page_title and now I can render metatags dynamically by simply iterating over assigns[:page_title] in my root.html.heex :grin:

You can do this with any assign!

As long as the assign is set during the dead render it will apply to the root template. Not sure they can be updated afterwards, but for the purpose of open graph meta tags, that is all you need

1 Like

Ha! That actually worked! Fascinating.

I assigned my metatags to the socket in the mount/3 callback on both the connected and disconnected mount and then fetched the metatags from the @conn assigns in my root.html.heex. That worked! Thank you for the tip :heart:

For those seeking some more advanced features for dynamically changing (meta)tags in head: GitHub - BartOtten/phoenix_live_head: HTML Head element manipulation for Phoenix Live View

Remember though that results for non JS-aware search engines will not be affected in any way.

1 Like