Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

svelte:head tags outside __layout not included in SSR output #2142

Closed
disbelief opened this issue Aug 9, 2021 · 7 comments
Closed

svelte:head tags outside __layout not included in SSR output #2142

disbelief opened this issue Aug 9, 2021 · 7 comments

Comments

@disbelief
Copy link

disbelief commented Aug 9, 2021

Describe the bug

The contents of <svelte:head> are only server-side rendered if they are defined inside __layout.svelte

For many very common reasons, I would like to define custom meta tags on a per-route basis. For example I'd like OpenGraph/twitter meta tags that correspond to the content on the page.

However, when specifying these tags inside <svelte:head> in a specific route, they are only injected into <head> of the document on the client-side. This defeats the entire purpose of these tags which are fetched by social networks and search engine spiders which typically won't execute javascript to read the content of the meta tags to generate previews.

This is happening on my production build on Netlify using the standard adapter-netlify adapter.

Reproduction

  1. Start with a typical SvelteKit application.
  2. Add some global tags with svelte:head in src/routes/__layout.svelte
    <svelte:head>
     <title>Default site title</title>
     <meta name="description" content="A default site description" />
    </svelte:head>
    
    <slot />
  3. Add some custom meta tags with svelte:head in a specific route src/routes/some-route.svelte:
    <svelte:head>
      <title>Custom route title</title>
      <meta name="description" content="Custom route description" />
      <meta name="twitter:card" content="summary" />
      <meta name="twitter:title" content="Custom title for this route on twitter" />
    </svelte:head>
    
    <!-- page content -->
  4. Deploy to netlify
  5. Open /some-route in browser
  6. View source of the page, notice the custom meta tags are not present in <head>
  7. Inspect the live DOM via devtools, the custom meta tags are present in the <head>

Logs

No response

System Info

System:
  OS: macOS 11.4
  CPU: (12) x64 Intel(R) Core(TM) i9-8950HK CPU @ 2.90GHz
  Memory: 81.50 MB / 32.00 GB
  Shell: 3.2.57 - /bin/bash
Binaries:
  Node: 14.16.0 - ~/.nvm/versions/node/v14.16.0/bin/node
  Yarn: 1.22.0 - /usr/local/bin/yarn
  npm: 6.14.11 - ~/.nvm/versions/node/v14.16.0/bin/npm
Browsers:
  Brave Browser: 92.1.27.108
  Chrome: 92.0.4515.131
  Firefox: 89.0.2
  Safari: 14.1.1
npmPackages:
  @sveltejs/adapter-netlify: ^1.0.0-next.27 => 1.0.0-next.27 
  @sveltejs/kit: ^1.0.0-next.144 => 1.0.0-next.144 
  svelte: ^3.42.1 => 3.42.1 


### Severity

serious, but I can work around it

### Additional Information

I marked this as serious because this is a pretty huge problem for common web use-cases like SEO and social sharing previews. Would go so far to say that it makes SvelteKit not production ready.
@vwkd
Copy link

vwkd commented Aug 9, 2021

I can't reproduce.

All header tags (see potential new issue below) seem to be included in SSR as well as CSR. The order is correct as well, since you have the <svelte:head> in the layout component before the <slot>. If you want the page header tags before the layout header tags, simply move the <svelte:head> in the layout component after the <slot>.

Also, Svelte doesn't filter out duplicate tags (EDIT: except <title>, see below). In fact, most of HTML is duplicate tags. The problem you see may be because some tags are expected to occur only once, like the <title> and the <meta> tags. Which of those duplicate-but-expected-once tags are used seems to not be well defined. I'm guessing here, but the easiest thing would be to use the first such matching header, not needing to scrape the document any further. Since in your example the layout headers come first, they would be used. Again, you can change the order as described above.


One issue I do see is that the <title> tag from the page component is not included. I don't know if there is some filtering going on in SvelteKit to make sure there is only ever one <title>?

Edit: Yes, according to @Conduitry the <title> tag is handled specially and deduplicated. See #2009 (comment)

I don't know if this is a good thing. Now it's not so clear which title is included. Handling the title like any other tag would be better IMO. And there is more chances for bugs.

Edit: And indeed, here is the bug #2146

@disbelief
Copy link
Author

@vwkd I don't think so. I'm not worried about duplicate tags as I'm aware of that issue, but the twitter:card and twitter:title tags are not included in the SSR markup at all.

Here's an example page: https://deploy-preview-32--keen-murdock-f65a9a.netlify.app/articles/coca-cola-vs-craft-soda-revolution

View the raw source and search for "twitter:title". It's not present. Then view the DOM with web inspector and you'll see it there.

raw source
Screen Shot 2021-08-09 at 15 56 21

live DOM
Screen Shot 2021-08-09 at 15 56 36

@disbelief
Copy link
Author

(by the way I tried putting the <slot> above the <svelte:head> in __layout.svelte but it didn't make any difference)

@vwkd
Copy link

vwkd commented Aug 9, 2021

I'm still failing to reproduce any lost tags (except a <title> tag). I put a simple project up here, maybe you can report if it works.

What I do see is that the SSR uses the page component title instead of the layout component title, and then client-side hydration changes it to the layout component title. I'm not sure why.

I found this note on <svelte:head>. I don't know if this is related

This element makes it possible to insert elements into document.head. During server-side rendering, head content [sic!] exposed separately to the main html content.

Here is a screencast:

title_change_on_hydration.mov

@disbelief
Copy link
Author

@vwkd thanks for the repro project. I cloned it locally and I can see that the head is being set correctly from the custom "About" route component.

It's very strange because this is the same structure as my real app. I'm trying to add things into the repro app now (eg. a load module function for the route) to see if something else on the page is breaking things but so far it still works.

(Aside: yes the <title> tag behaviour is very strange with SvelteKit. I've been confused by it in the past. Apparently there is some logic in svelte to deduplicate any <title> tags in the svelte:head but I'm not sure the ordering always makes sense.)

@disbelief
Copy link
Author

During server-side rendering, head content [sic!] exposed separately to the main html content.

@vwkd I also noticed this line from the svelte docs. I have no idea what it means though...

@disbelief
Copy link
Author

I found the problem. It was an error in my code where I was wrapping a <svelte:head> block within another <svelte:head> block.

This was because I have a component called PageMeta which renders the meta tags using <svelte:head> and I was mistakenly wrapping that component in <svelte:head> as well:

// src/components/PageMeta.svelte

<script>
  let title;
  let description;
</script>

<svelte:head>
  <title>{title}</title>
  <meta name="description" content={description} />
  <meta name="twitter:card" content="summary" />
  <meta name="twitter:title" content={title} />
  <meta name="twitter:description" content={description} />
</svelte:head>

// src/routes/some-route.svelte

<script>
  import PageMeta from '../components/PageMeta.svelte';
</script>

// this is the problem:
<svelte:head>
  <PageMeta title="Custom title" description="Some custom description" />
</svelte:head>

<!-- page content -->

Removing the <svelte:head> wrapper in some-route.svelte resolves the issue and the tags are rendered server-side.

It's actually somewhat surprising that the client-side gracefully handles this situation and injects the tags at all! 😆

Many thanks for the quick repro app @vwkd! It was very helpful for me to realize my mistake ❤️

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants