DEV Community

Cover image for SvelteKit SEO: Search Engine Optimisation Metadata
Rodney Lab
Rodney Lab

Posted on • Originally published at rodneylab.com

SvelteKit SEO: Search Engine Optimisation Metadata

🧑🏽‍🎓 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

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 up to 90% of search engine users will not go beyond the first page of search results. 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 (good keyword research can help here),

  • 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 — 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 featured snippet, 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

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 clone the SvelteKit MDsveX starter 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, Google sets a maximum display width of 600 px.

  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 cut it to around 155 characters, though Google do not officially confirm this. Try to keep it within that limit.

  3. 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 SvelteKit MDsveX blog starter or just copy the component from below and adapt it to your own project. Here is the code for the component:

<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>
Enter fullscreen mode Exit fullscreen mode

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):

<script>
  const {
    postTitle: title,
    seoMetaDescription: metadescription,
    slug,
  } = post;
</script>

<SEO article={true} {slug} {title} {metadescription} {timeToRead} />
<BannerImage {...bannerImageProps} />
Enter fullscreen mode Exit fullscreen mode

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

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 types of Twitter sharing card, see the Twitter Developer Docs.

For our chosen type of card we should provide an image which is 800 px × 418 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:

<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>
Enter fullscreen mode Exit fullscreen mode

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

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

for Twitter we use:

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

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 2530) 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.

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:

<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} />
Enter fullscreen mode Exit fullscreen mode

We specify a fallback image, with alt text in lines 1318. 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:

<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} />
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

💯 Checking Twitter Metadata

You can test Twitter metadata using the Twitter Card Validator.

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

🙌🏽 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 followup post, we look at Open Graph SEO in SvelteKit. 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 see the code for the story so far on the Rodney Lab Git Hub repo.

🙏🏽 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 consider supporting me through Buy me a Coffee.

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 @askRodney on Twitter and also askRodney on Telegram. Also, see further ways to get in touch with Rodney Lab. I post regularly on SvelteKit as well as Gatsby JS among other topics. Also subscribe to the newsletter to keep up-to-date with our latest projects.

Latest comments (0)