SvelteKit SEO: Search Engine Optimisation Metadata
Tags: svelte, webdev, javascript, seo
Published: 2021-07-29 08:22:25

---
title: "SvelteKit SEO:  Search Engine Optimisation Metadata"
published: "true"
description: "SvelteKit SEO: how to use SvelteKit to add search engine optimisation metadata so that social images appear when visitors share posts."
tags: "svelte, webdev, javascript, seo"
canonical_url: "https://rodneylab.com/sveltekit-seo/"
cover_image: "https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mxw2uy2xacqmdi6206mp.png"
---

## 🧑🏽‍🎓 Why is SvelteKit SEO Important?

Why write a post on SvelteKit SEO? We'll start by answering that question, before looking at some different types of SEO and then see how you can add Twitter SEO metadata as well as other metadata to your blog posts and other website pages.  The aim will be to have our SvelteKit page shares on Twitter appear something like this:

![SvelteKit SEO:  Search Engine Optimisation Metadata: screen capture of twitter post generated from page share](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/001fn1z56symxf1ht2dv.png)

We specify to Twitter which image we want to use as well as the title and description which appear below it.


Search Engine Optimisation (SEO) is all about getting your website pages to appear at the top of search engine results pages.  Would you still write content if no-one would ever read it?  Probably not.  If your page does not appear in the top few search results, there is a risk that very few people will visit your page.  This is because <a aria-label="Read Forbes article on Search Results Rankings" href="https://www.forbes.com/sites/forbesagencycouncil/2017/10/30/the-value-of-search-results-rankings/">up to 90% of search engine users will not go beyond the first page of search results</a>.  Around a third of users will click the first result and 17%, the second.  In short, Google needs to rank your post well for users to click on it.

We will look at some optimisations you can easily work into you SvelteKit site you get your posts and pages ranking higher.  As well as that we look at how you can ensure your posts look good whenever visitors share them on social media and messaging apps.

## 🤖 Which Aspects of SvelteKit SEO are we focussing on?

Although it is a new discipline, SEO is already has many aspects.  All branches, however, boil down to providing a good user experience.  Google want to connect their users to what they are looking for.  A bad user experience translates into users leaving your page without interacting with it (bouncing).  If you have a high bounce rate, why would Google want to put your site at the top of a search result list?

Some factors important for good user experience and SEO are:

- **good content**: writing about what people are interested in (<a aria-label="Open Yoast post on keyword research" href="https://yoast.com/keyword-research/">good keyword research can help here</a>), 

- **good site structure**: essentially this is about sufficient and relevant internal links, be they in breadcrumbs, navigation menus or post tags and categories.  You need to optimise your site's structure so users can get to what they want in few clicks.  On top more contextual links (links in the main text of a blog post, for example), help search engines determine what the post is all about.  Knowing what a post is about gives the search engine more confidence in placing it higher in the results.  Linking to (internal and external) related posts is important here.

- **technical SEO**: fast loading pages (SvelteKit makes fast sites so you already have a head start here), security, accessibility are important for user experience and so improve search ranking.  Technical SEO also encompasses getting rich results &mdash; we'll look at these in a bit more detail.

It is worth stressing that all of these aspects are important for ranking well.  You should take a holistic approach for successful SEO.  We will focus on the the more technical aspects though in this post as this is what is more specific to SvelteKit.  In particular we'll look at what metadata you should include on your website's pages.

### Rich Results

On Google, rich results will make your page stand out from others on a search results page.  This might be through a HowTo or a Rich Snippet (including a thumbnail for a video as well as the usual result data).  For these the most important metadata to include is SchemaOrg.  Google and other search engines developed SchemaOrg.  By including JSON objects matching the official schema, the search engine has a better idea of what is on the page and can be more confident in producing a rich snippet.  Below is an example of a <a aria-label="Read more about featured snippets" href="https://support.google.com/websearch/answer/9351707?p=featured_snippets&hl=en-GB&visit_id=637601192032736842-664216132&rd=1">featured snippet</a>, it appears above all other search results and Google give it prominence, with larger text:

![SvelteKit SEO:  Example of a featured snippet from Google search results. Shows a Rodney Lab page in results given prominence over other search results in a featured snippet](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/el8a25yyd41v2gwrgquj.png)


We will return to SchemaOrg in more detail.  To start though, let's take a look at more general SEO metadata and then Twitter metadata.  If you are starting a new site <a aria-label="Open the SvelteKit M D Svex Starter Blog repo" href="https://github.com/rodneylab/sveltekit-blog-mdx">clone the SvelteKit MDsveX starter</a> and follow along.  Alternatively you can follow along and paste the code snippets into your own project.

## 🧱 General Metadata

As a minimum, you should aim to include these four pieces of SEO metadata on your sites pages.  Lighthouse will give you a warning if some of them are missing.

1. **Title**: If your post has a short on-page title, augment the SEO title to include extra search terms.  Although there is no character limit, <a aria-label="Read more about writing a good post title from Yoast" href="https://yoast.com/page-titles-seo/#h-width-vs-length">Google sets a maximum display width of 600&nbsp;px</a>.

2. **Meta Description**: This is a longer description of the post.  Google might not show it in search results (they might instead decide to show an extract from the page).  When they do show it, they typically <a aria-label="Read more about meta descriptions on Google from Yoast" href="https://yoast.com/shorter-meta-descriptions/">cut it to around 155 characters, though Google do not officially confirm this</a>.  Try to keep it within that limit.

4. **Robots Tag**: The robots tag tells search engines you are happy for them to index your page.  They need to index the page to include it in their search results.  A `follow` directive tells search engines they can follow links on the page to discover new content.  The `max-snippet` directive specifies the limit on the meta description length (in number of characters), a value of `-1` indicates no limit.

4. **Site Language**:  This is important for site visitors using screen readers.  It helps with pronunciation, especially when there are dialects or regional variations (Brazilian Portuguese vs. European Portuguese for example).

### SvelteKit SEO Component

The SvelteKit blog starter includes those four pieces of metadata, just mentioned, in the SEO component.  You can either use the <a aria-label="Open the Svelte Kit M D Svex blog starter repo on Git Hub" href="https://github.com/rodneylab/sveltekit-blog-mdx">SvelteKit MDsveX blog starter</a> or just copy the component from below and adapt it to your own project.  Here is the code for the component:

```html
<script>
  import { VERTICAL_LINE_ENTITY } from '$lib/constants/entities';
  import website from '$lib/config/website';
 
  const { siteLanguage, siteTitle } = website;
 
  export let metadescription;
  export let title;
 
  const pageTitle = \`\${siteTitle} \${VERTICAL_LINE_ENTITY} \${title}\`;
</script>
  
<svelte:head>
  <title>{pageTitle}</title>
  <meta name="description" content={metadescription} />
  <meta
    name="robots"
    content="index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1"
  />
  <html lang={siteLanguage} />
</svelte:head>
```

There are two more robots directives included here (not mentioned previously): `max-image-preview` and `max-video-preview`.  We set image preview to large and put no limit on video preview, which is the number of seconds of video to play as a preview in search results.

You will see we need to pass in a `metadescription` and `title` as props when we use the component.  Here is an example (from the `BlogPost` component):

```html
<script>
  const {
    postTitle: title,
    seoMetaDescription: metadescription,
    slug,
  } = post;
</script>
 
<SEO article={true} {slug} {title} {metadescription} {timeToRead} />
<BannerImage {...bannerImageProps} />
```

In this case when we create a new post and add `postTitle` and `seoMetaDescription` to the frontmatter, these will automatically feed through to the html head section and be included in the post's metadata.  We can check SvelteKit has included the metadata by cracking open the post in a browser and opening Developer Tools **Inspector** if you use Firefox or **Elements** in Chrome:

![SvelteKit SEO:  Search Engine Optimisation Metadata: General Metadata Inspector](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lr1kbc91i38hwbg16zk5.png)

In this screenshot we can see SvelteKit included the title and meta description as well as robots and language.  General metadata is important but it will only get you so far.  Next we take a look at Twitter metadata.

## 🧑🏽‍🎓 Twitter Metadata

You might not know that Slack uses some Twitter meta tags when users share your  pages on the app. It's not just Twitter that uses it. If you didn't know that keep reading to learn even more about Twitter metadata!  

We mentioned before it is important to tell social networks which image to use for each page.  Not only will you avoid random, unrelated images from your page being picked, but also by providing an image with the right dimensions, you can avoid a poor crop, which can reflect badly on your brand.

I should point out because we include metadata in the pages, it won't matter whether the post is shared from someone clicking a link on your site or just by pasting the link into one of their tweets. Either way, Twitter will use your chosen image and description.

### Twitter Cards

When someone shares your page, Twitter generates a Card to show it.  There are different types, but the Summary Card with Large Image serves many purposes so we will use that.  To discover the other <a aria-label="Learn more about Twitter Cards in the developer docs" href="https://developer.twitter.com/en/docs/twitter-for-websites/cards/overview/abouts-cards">types of Twitter sharing card, see the Twitter Developer Docs</a>.

For our chosen type of card we should provide an image which is 800&nbsp;px &times; 418&nbsp;px.  The specification changes with time, so if you are reading this some point well into my future, check the latest specs!

Anyway, here is a component which we can use to add the Twitter metadata to our page:

```html
<script>
  export let article = false;
  export let author;
  export let twitterUsername;
  export let image;
  export let metadescription;
  export let pageTitle;
  export let timeToRead = 0;
  export let url;
</script>
 
<svelte:head>
  <meta name="twitter:card" content="summary_large_image" />
  <meta name="twitter:title" content={pageTitle} />
  <meta name="twitter:description" content={metadescription} />
  <meta name="twitter:url" content={url} />
  {#if image}
    <meta name="twitter:image" content={image.url} />
    <meta name="twitter:image:alt" content={image.alt} />
  {/if}
  {#if twitterUsername}
    <meta name="twitter:creator" content={\`@\${twitterUsername}\`} />
    <meta name="twitter:site" content={\`@\${twitterUsername}\`} />
  {/if}
  <meta name="twitter:label1" content="Written by" />
  <meta name="twitter:data1" content={author} />
  {#if article && timeToRead > 0}
    <meta name="twitter:label2" content="Est. reading time" />
    <meta name="twitter:data2" content={timeToRead !== 1 ? \`\${timeToRead} minutes\` : '1 minute'} />
  {/if}
</svelte:head>
```

Twitter metadata is based on the OpenGraph standard though they don't follow the standard to the letter.  While in OpenGraph we would use:

```html
  {`
<meta property="og:title" content={pageTitle} />
  `}
</CodeFragment>

for Twitter we use:

<CodeFragment language="html">
  {`
<meta name="twitter:title" content={pageTitle} />
```

Twitter meta falls back to OpenGraph meta where the equivalent OpenGraph tag is present.  So this means if we want to use the same title for sharing on Twitter as other apps, we can drop the `twitter:title` meta and just include the OpenGraph equivalent (`og:title`), which Twitter will pick up.

It's the last two tags (lines `25`&ndash;`30`) which generate metadata that appears when your post is shared on Slack:

![SvelteKit SEO: Slack: Webpage Bot:  Slack conversation screenshot.  User has received a share of a web page.  The share shows author and reading time details in the social sharing card.](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jnflgx140jjlsggwzf3k.png)

So now you know how automatically to include a reading time whenever your post is shared on Slack.  The other tags as used mostly for Twitter.

We need to update the SEO component to include Twitter metadata now:

```html
<script>
  import { VERTICAL_LINE_ENTITY } from '$lib/constants/entities';
  import website from '$lib/config/website';
  import Twitter from './Twitter.svelte';
 
  const { author, siteLanguage, siteTitle, siteUrl } = website;
 
  export let article = false;
  export let metadescription;
  export let slug;
  export let timeToRead = 0;
  export let title;
  export let twitterImage = {
    url:
      'https://rodneylab-climate-starter.imgix.net/home-twitter.jpg?ixlib=js-3.2.0&w=800&h=418&s=1b08b7276d34486234a4e2c1ccb49a74',
    alt:
      'picture of a person with long, curly hair, wearing a red had taking a picture with an analogue camera',
  };
 
  const pageTitle = \`\${siteTitle} \${VERTICAL_LINE_ENTITY} \${title}\`;
  const twitterProps = {
    article,
    author,
    twitterUsername: import.meta.env.VITE_TWITTER_USERNAME,
    image: twitterImage,
    metadescription,
    pageTitle,
    timeToRead,
    url: \`\${siteUrl}/\${slug}\`,
  };
</script>
 
<svelte:head>
  <title>{pageTitle}</title>
  <meta name="description" content={metadescription} />
  <meta
    name="robots"
    content="index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1"
  />
  <html lang={siteLanguage} />
</svelte:head>
<Twitter {...twitterProps} />
```

We specify a fallback image, with alt text in lines `13`&ndash;`18`.  This is used if we don't specify the `twitterImage` prop when the component is consumed.  The next piece in the chain is making sure metadata needed for Twitter SEO is passed through from the blog posts:

```html
<script>
  import readingTime from 'reading-time';
  import BannerImage from '$lib/components/BannerImage.svelte';
  import SEO from '$lib/components/SEO/index.svelte';
 
  export let post;
 
  const timeToRead = Math.ceil(readingTime(post.body).minutes);
  const {
    featuredImage,
    featuredImageAlt,
    featuredImageSrc,
    featuredImageSrcset,
    postTitle: title,
    seoMetaDescription: metadescription,
    slug,
    twitterImage = null,
  } = post;
 
  const twitterImageObject = twitterImage
    ? {
        url: twitterImage,
        alt: featuredImageAlt,
      }
    : null;
  const bannerImageProps = {
    featuredImage,
    featuredImageAlt,
    featuredImageSrc,
    featuredImageSrcset,
  };
</script>
 
<SEO
  article={true}
  {slug}
  {title}
  {metadescription}
  {timeToRead}
  twitterImage={twitterImageObject}
/>
<BannerImage {...bannerImageProps} />
```

`twitterImage`  in line `17` will be a url for the image we want to be shared on Twitter (remember this should be the dimensions we mentioned above).  This value feeds from the individual post frontmatter.  We give it a default value here, for the case it is undefined.  We assume the Twitter shared image has similar appearance to the main featured image of the post, so we can recycle the alt text.  Remember that alt text should describe the image for the benefit of visually impaired users.

Finally we need to install the `reading-time` package:

```
pnpm i -D reading-time
```

## 💯 Checking Twitter Metadata

You can <a aria-label="Open the Twitter card validator" href="https://cards-dev.twitter.com/validator">test Twitter metadata using the Twitter Card Validator</a>.

The Twitter logo watermark is something I added to the image file itself, just so you could see Twitter is picking up the specified image (which doesn't even appear on the shared page). The Twitter meta is used by the Telegram messaging app too.  To request the Telegram server to cache the image for your page, just start a new conversation in Telegram with `@webpagebot` and paste, then send, up to 10 URLs at a time as a message:

![SvelteKit SEO: Telegram: Webpage Bot:  Telegram conversation screenshot for converation with the web page bot.  User has typed a U R L as a message and the message window displays social sharing card featuring images and text set by the site's meta](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/otocuaxy2wh2huhfemvi.png)


## 🙌🏽 OpenGraph and SchemaOrg Metadata

Is this post, we have looked at:

- why SEO is important and an introduction to the broader topic,
- general SEO metadata,
- Twitter metadata.

You might have guessed this is not the end of the story.  As mentioned earlier, SEO is a detailed topic and there is too much detail to be able to summarise in a single post.  In an upcoming post we will look at SchemaOrg metadata.  This can be used to help your page appear, for example, in HowTo in Google search results.

In this <a aria-label="Open post on Open Graph SEO in SvelteKit" href="https://rodneylab.com/open-graph-seo-sveltekit/">followup post, we look at Open Graph SEO in SvelteKit</a>.  Although this is used by Facebook, it is also used in other apps, especially messaging apps (like Telegram and Signal).  We will also see how the order in which the meta tags are arranged affects whether WhatsApp shows your Card whenever a link for your site is shared.

You can <a aria-label="Open the Rodney Lab Git Hub Repo" href="https://github.com/rodneylab/sveltekit-seo">see the code for the story so far on the Rodney Lab Git Hub repo</a>.

## 🙏🏽 SvelteKit SEO: Feedback

Please send me feedback! Have you found the post useful? Would you like to see posts on another topic instead? Get in touch with ideas for new posts. Also if you like my writing style, get in touch if I can write some posts for your company site on a consultancy basis. Read on to find ways to get in touch, further below. If you want to support posts similar to this one and can spare a couple of dollars, rupees, euros or pounds, please <a aria-label="Support Rodney Lab via Buy me a Coffee" href ="https://rodneylab.com/giving/">consider supporting me through Buy me a Coffee</a>.

Finally, feel free to share the post on your social media accounts for all your followers who will find it useful. As well as leaving a comment below, you can get in touch via <a aria-label="D M Rodney Lab on Twitter" href="https://twitter.com/messages/compose?recipient_id=1323579817258831875">@askRodney</TwitterMessageLink> on Twitter and also <a aria-label="Contact Rodney Lab via Telegram" href="https://t.me/askRodney">askRodney on Telegram</a>. Also, see <a aria-label="Get in touch with Rodney Lab" href="https://rodneylab.com/contact">further ways to get in touch with Rodney Lab</a>. I post regularly on <a aria-label="See posts on svelte kit" href="https://rodneylab.com/tags/sveltekit/">SvelteKit</a> as well as <a href="https://rodneylab.com/tags/gatsby" aria-label="See posts on gatsby">Gatsby JS</a> among other topics. Also <a aria-label="Subscribe to the Rodney Lab newsletter" href="https://rodneylab.com/about/#newsletter">subscribe to the newsletter to keep up-to-date</a> with our latest projects.