Add Structured Data To Your Svelte App

| Published: March 02, 2024

| Updated: March 02, 2024

Structured data is a massively underutilized weapon for improving search engine rankings and helping them understand your content better. And for most Svelte applications, developers overlook this opportunity.

Improve your SEO and get rich search results with Structured Data.

Understanding the “when”, “where” and “how” of structured data when it comes to your website isn’t super straightforward, but if you do it right the results will pay dividends.

Google themselves have even written case studies on how businesses have massively increased organic traffic since adopting structured data.

This article will walk you through the process of adding relevant structured data to your Svelte website, and provide code snippets to get you going quickly.

Skip to FAQ’s

What does structured data look like in the search results

If you’ve ever used Google, you’ve seen rich results pulling from structured data - they are the results that are more than the standard “title and description”:

Standard Search Result Just a standard old search result.

✔️ Rich Search Result with Structured Data A rich search result with structured data. This is what you want.

As you can see, the rich result is more engaging and takes up more real-estate on the page.

Whilst this is just an example of one rich result (a Question-Answer schema), there are 35 schemas recognized by Google which may be more relevant to your app.

So let’s find schemas related to your site and integrate the structured data into your app.

Find Relevant Schemas

The first step of integrating structured data into your app is which type/s to use. You should peruse Google’s official Structured Markup Page to find options you believe would help search engines understand your content better.

via GIPHY

You may select more than one type of structured data to add to your pages, so long as they are all relevant.

Don’t use structured markup that isn’t relevant - Google may apply manual actions if you misuse this feature or violate their content or spam policies.

Seeing as there are so many different schema types, we’ll focus on the process of adding a schema to your page, rather than just copy-pasting the code. This is so that you can fundamentally understand schemas and apply the steps to any structured data you choose.

via GIPHY

Read the schema specification

Once you’ve selected the structured data type from the official supported schemas page, click on it to observe the specification.

In this example, we’re going to add the FAQ structured data to our page. If all goes to plan and Google recognizes it as valid, our page may be eligible to show a rich result that looks like this:

A rich search result with an FAQPage schema

Go to the examples section, and select the JSON-LD code - this is what we’ll be using for all of our structured data code moving forward.

Copy the <script> tag part of the JSON-LD example, which should look like this:

<script type="application/ld+json">
    {
      "@context": "https://schema.org",
      "@type": "FAQPage",
      "mainEntity": [{
        "@type": "Question",
        "name": "How to find an apprenticeship?",
        "acceptedAnswer": {
          "@type": "Answer",
          "text": "<p>We provide an official service to search through available apprenticeships. To get started, create an account here, specify the desired region, and your preferences. You will be able to search through all officially registered open apprenticeships.</p>"
        }
      }, {
        "@type": "Question",
        "name": "Whom to contact?",
        "acceptedAnswer": {
          "@type": "Answer",
          "text": "You can contact the apprenticeship office through our official phone hotline above, or with the web-form below. We generally respond to written requests within 7-10 days."
        }
      }]
    }
</script>

In Svelte, we can’t just paste this directly into our code and be done with it - we’ll have to make a few changes to have it work on our page.

Adding structured data to your Svelte App

In the step above, we were able to locate the shape of the structured data we chose (in our case, an FAQ).

To add this to our Svelte page, you must wrap the JSON-LD <script> and its contents inside a template literal and within a {@html} special tag and place it in the page’s markup.

Which will look like this:

+page.svelte

<script>
	// script contents...
</script>

{@html `<script type="application/ld+json">
    {
      "@context": "https://schema.org",
      "@type": "FAQPage",
      "mainEntity": [{
        "@type": "Question",
        "name": "How to find an apprenticeship?",
        "acceptedAnswer": {
          "@type": "Answer",
          "text": "<p>We provide an official service to search through available apprenticeships. To get started, create an account here, specify the desired region, and your preferences. You will be able to search through all officially registered open apprenticeships.</p>"
        }
      }, {
        "@type": "Question",
        "name": "Whom to contact?",
        "acceptedAnswer": {
          "@type": "Answer",
          "text": "You can contact the apprenticeship office through our official phone hotline above, or with the web-form below. We generally respond to written requests within 7-10 days."
        }
      }]
    }
</script>`}

<!-- Rest of page markup... -->

Remember, you can’t put this within your page <script> tag - it must be within the Svelte markup.

via GIPHY

Obviously you will have to change the Question and Answer values to reflect your page, however you can also include more properties to what’s been used in the example. Let’s now look at how to add more valid properties to our structured data.

For the FAQ schema in particular, the guidelines require us to clearly display the FAQ’s on the page as well. So let’s add this to our page:

<script>
	// script contents...
</script>

{@html `<script type="application/ld+json">
    {
      "@context": "https://schema.org",
      "@type": "FAQPage",
      "mainEntity": [{
        "@type": "Question",
        "name": "How to find an apprenticeship?",
        "acceptedAnswer": {
          "@type": "Answer",
          "text": "<p>We provide an official service to search through available apprenticeships. To get started, create an account here, specify the desired region, and your preferences. You will be able to search through all officially registered open apprenticeships.</p>"
        }
      }, {
        "@type": "Question",
        "name": "Whom to contact?",
        "acceptedAnswer": {
          "@type": "Answer",
          "text": "You can contact the apprenticeship office through our official phone hotline above, or with the web-form below. We generally respond to written requests within 7-10 days."
        }
      }]
    }
</script>`}

<div>
	<h2>Frequently Asked Questions(FAQ)</h2>
	<div>
		<h3>How to find an apprenticeship?</h3>
		<div>
			<div>
				We provide an official service to search through available apprenticeships. To get started,
				create an account here, specify the desired region, and your preferences. You will be able
				to search through all officially registered open apprenticeships.
			</div>
		</div>
	</div>
	<div>
		<h3>Whom to contact?</h3>
		<div>
			<div>
				You can contact the apprenticeship office through our official phone hotline above, or with
				the web-form below. We generally respond to written requests within 7-10 days.
			</div>
		</div>
	</div>
</div>

Adding more properties to the schema

The examples shown on the Structured markup page are simply baseline examples. You can add more properties to your structured data object, so long as it falls within the type definitions. How do we find the type definition for our schema though? Let’s take a look.

If you’re happy with the content in the snippet above, skip to the next section where we componentize our snippets.

Finding Schema Type Definitions

For each schema type you find on the Google markup page, there will be a section called “Structured data type definitions”.

You should be able to append #structured-data-type-definitions to the end of your URL and it will take you to this section

This section will give you information about where you can find the full type definition on Schema.org.

For example, in our FAQ example, we can find the type definitions section here. In the text, you’ll see the sentence:

The full definition of FAQPage is provided on schema.org.

The link will take you to the specific FAQPage on Schema.org. If you go to the page, you’ll be shown a list of all potential properties you can add to your FAQ schema. The list is exhaustive and mostly unnecessary, so we’ll leave it for now.

via GIPHY

So long as you provide the stated “required properties” (which in our case is mainEntity), your schema will be valid - anything else is optional.

Once you’ve changed the values and tested the validity of your schema, you have successfully added this FAQ structured markup to your page. 🥳

But if you run a website with many pages, it’s better to implement an automated system for adding structured data to your pages.

Let’s look at how to do this now.

Convert structured data to Svelte components

Converting the structured data code block we used to a component means you don’t have to add this to every page/post you make, and your site will be more robust. You’ll save time, and run into less issues.

via GIPHY

To do this, we have to convert the object values, and turn them into types, so we know which data to feed into the component.

For example, if we take the FAQ structured data we used above, we can convert the object type to be this:

// Remember this is just a type, not code to add to a component

{
    "@context": "https://schema.org",
    "@type": "FAQPage",
    "mainEntity": {
        "@type": "Question",
        "name": "string",
        "acceptedAnswer": {
            "@type": "Answer",
            "text": "string"
        }
    }[]
}

In this process, we made the following observations:

  • @context: "https://schema.org" is constant, so we can leave this,
  • @type: "FAQPage" is constant, so we can leave this,
  • mainEntity contains 2 objects that contain the same shape of data, so we take the object type and make it an array ([]),
  • Within mainEntity
    • @type: "Question" is constant, so we can leave this,
    • name is the question string, which will be dynamic - this must be a prop,
    • Within acceptedAnswer:
      • @type: "Answer" is a constant, so we can leave this,
      • text is the answer string, which will be dynamic - this must be a prop.

So with this information, we just need to look at the dynamic data to understand what to accepts as props into the component, which is just an object array containing a question string and answer string (makes sense, huh 😆).

type faqs = {
	question: string;
	answer: string;
}[];

via GIPHY

So, let’s create our FAQStructuredDataTag.svelte component.

Building the component

First, we’ll define the expected props:

FAQStructuredDataTag.svelte

<script lang="ts">
	export let faqs: {
		question: string;
		answer: string;
	}[];
</script>

Since we’re using a template literal to define the structured data, we don’t have access to the Svelte {#each} blocks to create the mainEntity array. So we’ll have to build the full mainEntity array in our <script> tag before passing it to the structured data tag:

<script lang="ts">
	export let faqs: {
		question: string;
		answer: string;
	}[];

	let mappedFaqs = faqs.map((faq, index) => {
		return `{
            "@type": "Question",
            "name": "${faq.question}",
            "acceptedAnswer": {
                "@type": "Answer",
                "text": "${faq.answer}"
            }
        }`;
	});
</script>

And then we can simply place the mappedFaqs as the value for mainEntity in our structured data tag:

{@html `<script type="application/ld+json">
    {
      "@context": "https://schema.org",
      "@type": "FAQPage",
      "mainEntity": [${mappedFaqs}]
    }
</script>`}

Don’t forget to wrap mappedFaqs in array brackets [] - as it’s an array, these must be present or else the schema will be invalid.

So all together, this is the full component for the FAQ schema:

FAQStructuredDataTag.svelte

<script lang="ts">
	export let faqs: {
		question: string;
		answer: string;
	}[];

	let mappedFaqs = faqs.map((faq, index) => {
		return `{
            "@type": "Question",
            "name": "${faq.question}",
            "acceptedAnswer": {
                "@type": "Answer",
                "text": "${faq.answer}"
            }
        }`;
	});
</script>

{@html `<script type="application/ld+json">
    {
      "@context": "https://schema.org",
      "@type": "FAQPage",
      "mainEntity": [${mappedFaqs}]
    }
</script>`}

So now, if you want valid structured FAQ schema in your page, simply place it in the markup in your +page.svelte, like this:

<script lang="ts">
	// other script content...

	export let data; // In your load function, you can build the faqs object array
</script>

<FaqSchema faqs={data.faqs} />

And the component will also be type-safe, so if there’s an error when passing data to the component, the language server/compiler will warn you.

via GIPHY

So now you’ve successfully componentized your structured data snippets for Svelte.

Now we want to use these components to automate the generation of structured data through our entire Svelte app. We’ll explain this by walking through a real-world example of this in action.

But first, we need to quickly touch on using multiple schemas for a page.

Stacking Schemas

You’ve probably perused the Google Structured Data Types Page and thought, “hmm 🤔, I could apply a few of these to my pages”. And you would be correct.

Remember the general rule: if it helps search engines understand the page better, it’s worth providing the structured data.

For example, if you test the schema used on this very page, you’ll notice 4 separate schemas:

This page has 4 schemas for structured data.

For every blog post, there will be 4 schemas:

  • Organization (hardcoded site-wide),
  • BreadcrumbList (programmatically generated with post data),
  • Article (programmatically generated with post data),
  • FAQ (questions/answers written for each post, with JSON-LD generated from data)

via GIPHY

Let’s look at how we apply these multiple schemas to this website.

How SvelteKit.io applies structured data to blog posts

Since the website organization remains constant across all pages on this website, I’ve hardcoded the Organization schema to my root +layout.svelte file:

src/routes/+layout.svelte

{@html `<script type="application/ld+json">
    {
      "@context": "https://schema.org",
      "@type": "Organization",
      "url": "https://sveltekit.io",
      "name": "Sveltekit.io",
      "description": "SvelteKit.io is a website dedicated to helping developers learn about Svelte and Sveltekit."
    }
</script>`}

Then for the BreadcrumbList and Article schemas, both of these get populated automatically for every blog post using post-specific data.

Firstly, I’ve created a specific component which combines these 2 schema types:

ArticleBreadcrumbStructuredData.svelte

<script lang="ts">
	export let title: string;
	export let datePublished: string;
	export let dateModified: string;
	export let image: string | undefined;
</script>

{@html `<script type="application/ld+json">
    [{
      "@context": "https://schema.org",
      "@type": "Article",
      "headline": "${title}",
      ${
	image &&
	`"image": [
        "https://sveltekit.io${image}"
       ],`
}
      "datePublished": "${datePublished}",
      "dateModified": "${dateModified}",
      "author": [{
          "@type": "Person",
          "name": "Jack Landon",
          "url": "https://sveltekit.io/about"
        }]
    },
    {
        "@context": "https://schema.org",
        "@type": "BreadcrumbList",
        "itemListElement": [{
            "@type": "ListItem",
            "position": 1,
            "name": "Blog",
            "item": "https://sveltekit.io/blog"
        },{
            "@type": "ListItem",
            "position": 2,
            "name": "${title}"
        }]
        }
]
</script>`}

Which then gets added to the dynamic src/routes/blog/[slug]/+page.svelte directory, like this:

src/routes/blog/[slug]/+page.svelte

<script lang="ts">
	import ArticleBreadcrumbStructuredData from '$lib/components/ArticleBreadcrumbStructuredData.svelte';

	export let data;
</script>

<ArticleBreadcrumbStructuredData
	title={data.meta.title}
	datePublished={data.meta.datePublished}
	dateModified={data.meta.dateModified}
	image={data.meta.image}
/>

So then in the page’s load function, we pull all the necessary post data and then populate the structured data automatically.

In this directory, we also add the FAQStructuredDataTag component:

src/routes/blog/[slug]/+page.svelte

<script lang="ts">
	import FAQStructuredDataTag from '$lib/components/FAQStructuredDataTag.svelte';
	import ArticleBreadcrumbStructuredData from '$lib/components/ArticleBreadcrumbStructuredData.svelte';

	export let data;
</script>

<ArticleBreadcrumbStructuredData
	title={data.meta.title}
	datePublished={data.meta.datePublished}
	dateModified={data.meta.dateModified}
	image={data.meta.image}
/>

{#if data.meta.faqs}
	<FAQStructuredDataTag faqs={data.meta.faqs} />
{/if}

It’s a great system, and if your Svelte app has tens, hundreds, or thousands of posts/pages/products etc. then you can easily apply this structured data system to your site to instantly add structured data and upgrade your SEO opportunities on every page.

Vimeo applied a similar system to their site with outstanding results.

via GIPHY

Now we’ve applied applied the structured data across our app, it’s important to make sure it’s valid! Let’s look at how to test our pages live, or before we publish them.

Validate your schema

Given how fickle creating structured data can be, it’s important to test that each page has valid structured data. You can do this with the Google Rich Results Test, or the Schema Markup Validator which provides a more fine-grain assessment.

The validator will show the values of your structured data.

The right panel will break down each of the properties and the entered values. If there’s any errors in your snippet, the compiler will tell you and show you which line the error is on. 🙏

The validator will tell you where your errors are.

In this instance, I removed a comma after the answer, and the compiler was able to find it.

Nothing is guaranteed

Just because you’ve added structured data to your pages does not guarantee it will be honored. Search engines may deem it invalid, or just simply not show it as a rich result.

This doesn’t mean it’s not worth doing - you may receive some of the SEO benefits even if you don’t get rich search results.

via GIPHY

Conclusion

Well done for adding everything. Hopefully you’ve employed the automated system shared in this article. If you have, you won’t need to worry about structured data for the rest of your app’s life, provided no major changes.

In this article, we learned:

  • What structured data is,
  • How it can improve visibility on search engines and help us rank better,
  • How to find relevant schemas to use for our pages,
  • How to add structured data to our Svelte pages,
  • How to componentize structured data snippets,
  • How to systemize and automate structured data generation for all pages in our Svelte app,
  • We looked at how this is done on SvelteKit.io
  • How to extend our schemas and where to find valid properties to add,
  • How to validate our schemas before publishing and on live web pages

If it’s your first time doing this, it can be a little bit challenging, but you’ll be glad you took the time to systemize structured data through your Svelte app.

You can monitor how effective this strategy is in your Google Search ConsoleSearch Appearance” tab, where it tells you which type of rich snippets your content has been featured in.

Good luck - I wish you plenty of rich search results and for your Svelte app to have a positive reputation with search engines. ❤️

FAQ’s

Thanks for reading ❤️

Here are some other articles I think you might like!