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

Define default meta tags, title etc. and have the option to override it #1540

Closed
Zerotask opened this issue May 24, 2021 · 13 comments
Closed

Comments

@Zerotask
Copy link
Contributor

Zerotask commented May 24, 2021

I want to define default meta tags, title etc. and have the option to override it per page.
For example: I have 10 generic pages where I just want to use the default ones and then I have 5 pages where I want to set individual meta keywords, description, title etc.

In the app.html I can define those default tags, but if I set new tags via svelte:head it just appends those new tags and doesn't replace the existing ones.

I also tried playing around with __layout.svelte (since this is somehow a root component at the moment) and Context API etc. but this didn't work for me.

At the moment, the only solution I can find is creating a root component (e. g. App.svelte) and every page will import and call it. This root component will then export those meta tags and title and will have default values. Then I remove those tags from the app.html and pass it 1x from the root component with svelte:head

<App keywords="test1, test2, test3" description="this is a test" title="test title">
    The actual content of the site
</App>

But this seems to be cumbersome and would mean that in my case, I have to update more than 100 pages.

Is there a better way of doing that?

the easiest way of doing it would be to have an "override" mode for svelte:head:

<svelte:head|override>
    // override existing content
</svelte:head>

edit: there is also a realted Svelte issue: sveltejs/svelte#4990

It would also be a nice solution to have some sort of named slots here (in the app.html, the __layout.svelte or somewhere else).

Using a store (https://github.com/svelte-society/sveltesociety-2021/blob/main/src/lib/stores/metatags.ts) can also be a solution, but the "override" solution seems to be easier to read and write.

@7antra
Copy link

7antra commented Jul 1, 2021

If think it's a good idea, because, event if we could create an SEO component, there is a little bug, sometimes hydratation generate two meta title with __layout and index

@ralyodio

This comment has been minimized.

@Zerotask Zerotask changed the title Define default meta tags, title etc. and have the option to override it optionally Define default meta tags, title etc. and have the option to override it Aug 6, 2021
@benmccann
Copy link
Member

#269 would help quite a bit with making this easier as each page could pass its title to the layout and you would only need the layout to set the title

@e0
Copy link

e0 commented Feb 18, 2022

With the recent addition of stuff, should this be closed now?

Following is a TypeScript example.

In src/app.d.ts:

declare namespace App {
  interface Locals {}

  interface Platform {}

  interface Session {}

  interface Stuff {
    description: string;
  }
}

In src/routes/my-page.svelte:

<script context="module" lang="ts">
  export const load = () => ({
    stuff: {
      description: 'Description from page'
    }
  });
</script>

In src/routes/__layout.svelte:

<script context="module" lang="ts">
  export const load = () => ({
    stuff: {
      description: 'Default description from layout'
    }
  });
</script>

<script lang="ts">
  import { page } from '$app/stores';
</script>

<svelte:head>
  <meta name="description" content={$page.stuff.description} />
</svelte:head>

Edit: Updated code based on #1540 (comment)

@benmccann
Copy link
Member

Yes, I think we can for now and see how well that solves the problem for people. We've also documented it here: https://kit.svelte.dev/docs/seo

@eurolife
Copy link

eurolife commented Mar 15, 2022

could not follow this example and gave up. Documentation sadly lacking in any detail.

@benmccann
Copy link
Member

The example just above seems pretty fleshed out. I'm not sure why you'd be having trouble with it and you haven't given any details as to what you're having difficulty with. I'd suggest that a closed issue is not the correct place to ask for help though and you should ask on Discord, GitHub Discussions, or StackOverflow.

The one thing I'd note is that doing description: stuff.description || 'Default description from layout' in the layout probably doesn't make sense because I'd expect stuff.description to never be defined at that point, so it could simply be description: 'Default description from layout'

@Therealskythe
Copy link

With load and stuff reworked / gone, is it still not possible not declare svelte:head in pages? This seems extremely complicated for a simple task.

@ayyash
Copy link

ayyash commented Oct 10, 2022

The question was about an option to "override" but the solution provided here #1540 (comment) (the above comment that people don't seem to like), is the other way around, it's watching a variable and making the layout respond to changes, which is, I guess, the pretty standard way, just thought in SvelteKit things would be done differently, that the child would override its parent. Nope, not today.

@7antra
Copy link

7antra commented Oct 10, 2022

You could just use $page.data like $page.data.metadata for examples, it will replace stuff, and use it in your +layout.svelte, you could overwrite this in any load function, if i'm not wrong ?

@Ben-Kincaid
Copy link

Apologies for resurrecting this closed issue, but is returning some arbitrary object properties in the load function & handling these head elements directly in the base layout the recommended approach moving forward? Having a hard time finding any further discussion on this other than this inactive issue.

Just my 2c, but this seems like an overly complicated (and frankly, unique) solution to an issue that has been solved time and time again in most of the other (non-svelte) frameworks that i'm sure many incoming svelte developers are familiar with. Having our layout(s) rely on specific object properties to be defined in the response of various load functions across the app seems like a really hamfisted way to support default meta tags, and feels like a large compromise to make considering it forgoes the contextual simplicity of using svelte:head in my pages & layouts as the source of truth for the metadata that is displayed across my site.

Additionally, i think it should be made more clear in the docs that defining ''default'' meta tags in your layout will not work as expected when defining those same tags in your pages. The small blurb about using $page.data in your layout doesn't provide much context as to why this should or needs to be done, and this issue contains the only examples that i've been able to find of this approach.

@blerrgh
Copy link

blerrgh commented Jun 11, 2023

it should be made more clear in the docs that defining ''default'' meta tags in your layout will not work as expected when defining those same tags in your pages. The small blurb about using $page.data in your layout doesn't provide much context as to why this should or needs to be done

+1 Agreed. As a newbie to SvelteKit I went through all the tutorials and generally accepted advice and it really seemed like I was supposed to put, for example

<svelte:head>
	<title>Website</title>
</svelte:head>

into my root +layout.svelte

and then

<svelte:head>
	<title>Specific Subroute - Website</title>
</svelte:head>

Into +layout.svelte or +page.svelte in any routes that might need more specific titles.

However when doing it this way, the correct "subroute" title loads for a second, then changes back to the base title after page loading is complete.

I'm still not clear on how it's supposed to work -- I guess you should only use <svelte:head> in pages but not in a layout?

this issue contains the only examples that i've been able to find of this approach.

Yes, and the example uses the stuff concept that I guess has been deprecated so they're not even up to date examples. Title management seems like such a core concept, it would really help to have some documentation on best practices for this.

@thnee
Copy link

thnee commented Apr 1, 2024

The docs could definitely be improved. Doing SEO feels more straight forward with other frameworks.
This seems to be a common issue, people are still getting tripped up by this, as seen in #10089 and sveltejs/svelte#10223.

It seems like the key is to learn how to work with page data, load functions, and the page store.
Then it actually becomes relatively easy to make any part of <svelte:head> dynamic.
The following code pattern seems to work, and it handles default values, and optional tags.
There is probably a cleaner way of coding this, but at least it seems to work, and maybe it can be of help to someone.
(This is using Svelte 5 runes, but my gut feeling is that it would work the same way in Svelte 4 too).

routes/some/path/+page.js

export function load() {
  return {
    pageMeta: {
      "title": "Some Page",
      "description": "Description of Some Page",
    },
  };
}

routes/+layout.server.js

import { ENABLE_ANALYTICS } from "$env/static/private";

export function load() {
  return { ENABLE_ANALYTICS };
}

routes/+layout.svelte

<script>
  import { page } from "$app/stores";
  let { data } = $props();
  let head = $state({});
  let siteName = "My Blog";
  let author = "My Name";
  let defaultKeywords = ["Foo", "Bar"];

  let setHead = () => {
    if ($page.data.pageMeta.title) {
      head.title = $page.data.pageMeta.title + " - " + siteName;
    } else {
      head.title = siteName;
    }
    if ($page.data.pageMeta.description) {
      head.description = $page.data.pageMeta.description;
    } else {
      head.description = undefined;
    }
    if ($page.data.pageMeta.keywords) {
      head.keywords = [...defaultKeywords, ...$page.data.pageMeta.keywords || []];
    } else {
      head.keywords = defaultKeywords;
    }
    head.author = author;
  };

  setHead();
  $effect(setHead);
</script>

<svelte:head>
  <title>{head.title}</title>
  {#if head.description}
    <meta name="description" content={head.description}>
  {/if}
  {#if head.keywords}
    <meta name="keywords" content={head.keywords.join(" ")}>
  {/if}
  {#if head.author}
    <meta name="author" content={head.author}>
  {/if}
  {#if data.ENABLE_ANALYTICS == "true"}
    <script>do_analytics();</script>
  {/if}
</svelte:head>

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