RationalDev
Simplifying SEO Schema in Svelte and Astro

Simplifying SEO Schema in Svelte and Astro

Updated:
Published:
4 min read

If you look after a blog or content site, you probably already know structured data (Schema.org) matters for search. It can be the difference between a plain blue link and a Google result with things like images and ratings.

When I first set up publishing for my Svelte and Astro sites, I used components for the schema. I wrote custom Svelte components that put Microdata attributes into the HTML all over the site. It seemed like a good idea at the time, as everything was a component, but it turned into a pain to maintain.

I recently threw a lot of those components away for something much simpler with no runtime library. This is what I did, and why I think you might not need an SEO library either.

Table of Contents

I wanted the SEO tags built right into the layout. I created Svelte components like <BlogPostingSchema> and <BreadcrumbSchema> that took props and output Microdata attributes inline with the HTML.

It looked something like this:

<!-- The old way: Microdata attributes mixed into the markup -->
<div itemscope itemtype="https://schema.org/BlogPosting">
  <h1 itemprop="headline">{post.title}</h1>
  <span itemprop="author" itemscope itemtype="https://schema.org/Person">
    <span itemprop="name">{post.author}</span>
  </span>
</div>

The problems showed up pretty quickly:

  1. Every time Schema.org changed or I wanted to add a property, I had to edit the component templates.
  2. I had to pass all the SEO data down through the layout components just so the Microdata tags had what they needed.
  3. Google Search Console seemed very picky. One missing itemprop in a nested component could break the whole snippet.

Looking back, mixing the visible UI components with metadata nobody sees was a mistake.

Back to Table of Contents

I wanted to move from inline Microdata to JSON-LD, which is a lot cleaner, so I started looking at what was already out there.

My first thought was that someone must have solved this already and there would be a package for it.

The Nuxt ecosystem has some good tools for this, like nuxt-jsonld and zhead, with composables for adding reactive SEO data. Bringing those patterns into Astro felt like too much though.

I also looked at Svelte schema libraries. There are some, but a lot of the ones I found seemed abandoned, were more complicated than I needed, or didn’t have good TypeScript types for the Schema.org vocabulary, which is huge.

What I wanted was autocomplete in the editor without adding a whole framework plugin.

Back to Table of Contents

Turns out I didn’t need a runtime library at all. Astro and Svelte can already put data into the <head> of a page. All I really needed was TypeScript types.

That is what schema-dts is. It’s a Google project with TypeScript definitions for the whole Schema.org vocabulary and no runtime code.

This is what it looks like now in the Astro frontmatter:

---
import type { BlogPosting, WithContext } from "schema-dts"

// A typed schema object
const articleSchema: WithContext<BlogPosting> = {
  "@context": "https://schema.org",
  "@type": "BlogPosting",
  headline: post.title,
  datePublished: post.publishedDate,
  author: {
    "@type": "Person",
    name: post.author,
  },
}
---

<!-- Output it as JSON-LD in the head -->
<head>
  <script type="application/ld+json" set:html={JSON.stringify(articleSchema)} />
</head>

That’s all I needed.

Back to Table of Contents

Dropping the custom components and using schema-dts with plain JSON.stringify has worked out a lot better for me.

No Runtime Code

schema-dts is only type definitions, so it disappears at build time. Nothing in the browser is parsing or generating schema.

Type Checking and Autocomplete

TypeScript tells me if I misspell datePublished or put a string where it wants an Organization. I also get autocomplete for all of Schema.org in the editor.

Schema Kept Out of the UI

My visual components, like the <article> tag and the <Card> layouts, no longer have itemprop attributes all through them. They just deal with HTML and CSS. The schema sits on its own in the <head>, and JSON-LD is the format Google recommends anyway.

Back to Table of Contents

Try the Simple Option First

When something seems complicated, like SEO, state management or how to structure components, it’s easy to go looking for a framework specific library to handle it.

This was a reminder for me that the language and the tools I already have are sometimes enough. Astro’s set:html and the types from schema-dts did the job, and I think it’s a lot easier to maintain than another runtime library would have been.

Home