Using SvelteKit it is very easy to create web applications with SSR, but for SEO we need to add some metadata like JSON-LD to the page.This article will explain how to use JSON-LD in SvelteKit applications.

What is JSON-LD

JSON-LD is a data structure that uses the JSON format to describe the semantics of data. It is a format used to add structured data to web pages, which helps search engines to better understand the content of web pages. JSON-LD is a very important technique when it comes to SEO. JSON-LD defines an embedded syntax for embedding structured data in HTML documents. Generally we use the data structure provided by Schema.org to describe the data.

Current Status of JSON-LD Use in SvelteKit

The +layout in SvelteKit adds a layout to each page. Adding JSON-LD data to the layout adds structured data to the structure of the entire site. Adding JSON-LD data to a page adds structured data to the content of the page. But at the same time, JSON-LD data can be added to +page.svelte, which describes the content of the page. SvelteKit doesn't automatically handle the relationship between the two for us automatically, we need to work around this manually.

Practices for adding JSON-LD data to SvelteKit

We can pre-organize the JSON-LD data in +layout.ts and then add the JSON-LD data for the page in +page.svelte. This is a good solution to the problem of JSON-LD data. We can add in +page.svelte the JSON-LD output:

<script lang="ts">
    export let data;

    $: ({ ldjson } = data);

    let ldjson = () => {
        let creativeWork = {
            "@context": "https://schema.org",
            "@type": "CreativeWork",
            "name": "Example Creative Work",
            "author": {
                "@type": "Person",
                "name": "Jane Doe"
            }
        };
        return Object.assign({}, ldjson, creativeWork);
    }
</script>

<svelte:head>
    {@html `<script type="application/ld+json">${JSON.stringify(
        json(),
    )}</script>`}
</svelte:head>

Where ldjson is the generic JSON-LD data from the parent layout and creativeWork is the JSON-LD data for the current page. We can add our own JSON-LD data to the page and merge it into ldjson. This is a good solution to the JSON-LD data problem. In +layout.ts, you can:

import type { Load } from '@sveltejs/kit';

export const load: Load = async ({ fetch, params, depends, data }) => {

    const ldjson: any = {
        '@context': 'https://schema.org',
    };

    ldjson.issn = '1234-5678';

    return { ldjson };
}

Use schema-dts

In TypeScript, we can use Schema type definitions to describe JSON-LD data.This allows for better organization of JSON-LD data.We can use the schema-dts maintained by Google

npm install -D --save schema-dts

Then use it in +page.svelte:

import type {
    WithContext,
    Article as SchemeArticle,
    Review,
    CreativeWork,
    WebPage,
} from "schema-dts";

let post: any = {};

let json = () => {
    
    let creativeWork: CreativeWork = {
        "@type": "CreativeWork",
        headline: post.title,
        image: post.image,
        datePublished: new Date(post.date).toISOString(),
        url: post.url,
    };

    if (post.authors) {
        let author = post.authors.map((author: any) =>
            Object.assign(
                {
                    "@type": "Person",
                    name: author.name || author.account || author,
                },
                author.url
                    ? {
                          url: author.url,
                      }
                    : {},
            ),
        );
        if (author.length === 1) {
            author = author[0];
        }
        creativeWork.author = author;
    }

    if (post.modified?.date) {
        creativeWork.dateModified = new Date(
            post.modified.date,
        ).toISOString();
    }
    if (post.summary) {
        creativeWork.description = post.summary;
    }

    if (post.aggregateRating) {
        creativeWork.aggregateRating = {
            "@type": "AggregateRating",
            ratingValue: post.aggregateRating.value,
            reviewCount: post.aggregateRating.count,
            bestRating: post.aggregateRating.best || 10,
            worstRating: post.aggregateRating.worst || 1,
        };
    }

    if (post.template == "item") {
        if (post.review) {
            creativeWork = Object.assign(creativeWork, {
                "@type": "Review",
                itemReviewed: {
                    "@type": post.review.item?.type,
                    name: post.review.item?.name,
                    url: post.review.item?.url,
                    image: post.review.item?.image,
                },
                reviewRating: {
                    "@type": "Rating",
                    ratingValue: post.review.rating,
                    bestRating: 10,
                    worstRating: 1,
                },
                reviewBody: post.review.body || post.summary,
            } as Review);
        } else {
            creativeWork = Object.assign(creativeWork, {
                "@type": "Article",
            } as SchemeArticle);
        }
    } else if (post.template == "links") {
        creativeWork = creativeWork as WithContext<CreativeWork>;
    } else if (post.template == "default") {
        creativeWork = Object.assign(creativeWork, {
            "@type": "WebPage",
        } as WebPage);
    } else {
        creativeWork = creativeWork as WithContext<CreativeWork>;
    }

    let schema: WithContext<any> = Object.assign(creativeWork, {
        "@context": "https://schema.org",
    });

    return Object.assign({}, ldjson, schema);
};

Conclusion

Using JSON-LD in SvelteKit applications can help search engines better understand page content.We can pre-organize the JSON-LD data in +layout.ts and then add the JSON-LD data of the page in +page.svelte.This is a good solution to the problem of JSON-LD data.We can use schema-dts to better organize JSON-LD data. This can better describe the semantics of the data. Some of my previous reviews have used JSON-LD data, and my ratings have been shown in Google search results.