🧡

GEO for SvelteKit: Complete Implementation Guide

intermediate

SvelteKit implements GEO via the svelte:head element in +layout.svelte for site-wide meta tags, and per-page +page.svelte for article-specific data. Use prerender=true in +page.ts for static generation. JSON-LD is injected directly in svelte:head as a raw script tag.

GEO for SvelteKit: Complete Implementation Guide

SvelteKit implements GEO via the svelte:head element in +layout.svelte for site-wide meta tags, and per-page +page.svelte for article-specific data. Use prerender=true in +page.ts for static generation. JSON-LD is injected directly in svelte:head as a raw script tag.

SvelteKit uses SSR by default and can pre-render pages to static HTML — both options are AI-crawler compatible. The svelte:head element gives you direct control over every tag in the HTML head.

Page Implementation

<!-- src/routes/geo-guide/+page.svelte -->
<script lang="ts">
  const publishedTime = '2026-04-18T00:00:00Z'
  const url = 'https://yoursite.com/geo-guide'

  const schema = JSON.stringify({
    '@context': 'https://schema.org',
    '@type': 'Article',
    headline: 'How to Implement GEO in SvelteKit',
    author: { '@type': 'Organization', name: 'My Company' },
    publisher: {
      '@type': 'Organization',
      name: 'My Site',
      logo: { '@type': 'ImageObject', url: 'https://yoursite.com/logo.png' },
    },
    datePublished: publishedTime,
    dateModified: publishedTime,
    mainEntityOfPage: { '@type': 'WebPage', '@id': url },
  })
</script>

<svelte:head>
  <title>How to Implement GEO in SvelteKit | My Site</title>
  <meta name="description" content="GEO in SvelteKit with svelte:head, Open Graph, and JSON-LD. Complete guide with examples.">
  <meta name="author" content="My Company">
  <link rel="canonical" href={url}>

  <meta property="og:type" content="article">
  <meta property="og:title" content="How to Implement GEO in SvelteKit">
  <meta property="og:description" content="Complete technical GEO guide for SvelteKit">
  <meta property="og:url" content={url}>
  <meta property="og:site_name" content="My Site">
  <meta property="og:image" content="https://yoursite.com/og/geo-guide.jpg">
  <meta property="og:locale" content="en_US">

  <meta property="article:published_time" content={publishedTime}>
  <meta property="article:modified_time" content={publishedTime}>
  <meta property="article:author" content="https://yoursite.com/author/my-company">
  <meta property="article:section" content="Technical Guides">
  <meta property="article:tag" content="GEO">

  <meta name="robots" content="index, follow">

  <!-- eslint-disable-next-line svelte/no-at-html-tags -->
  {@html `<script type="application/ld+json">${schema}</script>`}
</svelte:head>

<main>
  <article>
    <h1>How to Implement GEO in SvelteKit</h1>
    <!-- Inverted pyramid: direct answer first -->
    <p>
      GEO in SvelteKit is implemented via svelte:head for all meta tags and
      JSON-LD schema. SSR is the default, making pages accessible to AI crawlers.
    </p>
  </article>
</main>

Dynamic Pages with Load Functions

For CMS-driven content, load data in +page.ts and pass it to +page.svelte:

// src/routes/blog/[slug]/+page.ts
import type { PageLoad } from './$types'

export const load: PageLoad = async ({ params, fetch }) => {
  const post = await fetch(`/api/posts/${params.slug}`).then(r => r.json())
  return { post }
}

// Optional: prerender for static HTML output
export const prerender = false // true for fully static pages
<!-- src/routes/blog/[slug]/+page.svelte -->
<script lang="ts">
  import type { PageData } from './$types'

  export let data: PageData
  const { post } = data

  const schema = JSON.stringify({
    '@context': 'https://schema.org',
    '@type': 'Article',
    headline: post.title,
    datePublished: post.publishedAt,
    dateModified: post.updatedAt,
    author: { '@type': 'Person', name: post.author.name },
    publisher: { '@type': 'Organization', name: 'My Site' },
  })
</script>

<svelte:head>
  <title>{post.title} | My Site</title>
  <meta name="description" content={post.excerpt}>
  <link rel="canonical" href="https://yoursite.com/blog/{post.slug}">
  <meta property="og:type" content="article">
  <meta property="og:title" content={post.title}>
  <meta property="article:published_time" content={post.publishedAt}>
  <meta property="article:modified_time" content={post.updatedAt}>
  <!-- eslint-disable-next-line svelte/no-at-html-tags -->
  {@html `<script type="application/ld+json">${schema}</script>`}
</svelte:head>

<article>
  <h1>{post.title}</h1>
  {@html post.content}
</article>

Root Layout for Site-Wide Defaults

<!-- src/routes/+layout.svelte -->
<script lang="ts">
  import { page } from '$app/stores'
</script>

<svelte:head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta name="robots" content="index, follow">
</svelte:head>

<slot />

robots.txt

Create static/robots.txt (SvelteKit serves the static/ directory as root):

User-agent: GPTBot
Allow: /

User-agent: OAI-SearchBot
Allow: /

User-agent: ClaudeBot
Allow: /

User-agent: Claude-User
Allow: /

User-agent: Claude-SearchBot
Allow: /

User-agent: PerplexityBot
Allow: /

User-agent: Google-Extended
Allow: /

User-agent: BingBot
Allow: /

Sitemap: https://yoursite.com/sitemap.xml
Sitemap: https://yoursite.com/llms.txt

llms.txt

Create static/llms.txt:

# My Site Name
> Description of what your site does and who it serves.

## Main Content
- [GEO Guide](https://yoursite.com/geo-guide): Complete GEO implementation guide
- [Technical Reference](https://yoursite.com/technical): Meta tags, schema, robots.txt

## About
- [About](https://yoursite.com/about): Team credentials

Dynamic Sitemap Route

Create a server route that generates the sitemap:

// src/routes/sitemap.xml/+server.ts
import type { RequestHandler } from './$types'

export const GET: RequestHandler = async () => {
  const pages = await getAllPages()

  const sitemap = `<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
  <url>
    <loc>https://yoursite.com/</loc>
    <lastmod>${new Date().toISOString().split('T')[0]}</lastmod>
    <priority>1.0</priority>
  </url>
  ${pages.map(page => `
  <url>
    <loc>https://yoursite.com/${page.slug}</loc>
    <lastmod>${page.updatedAt.split('T')[0]}</lastmod>
    <priority>0.8</priority>
  </url>`).join('')}
</urlset>`

  return new Response(sitemap, {
    headers: { 'Content-Type': 'application/xml' },
  })
}

Static Prerendering

For fully static output (maximum AI crawler compatibility):

// src/routes/+layout.ts
export const prerender = true
export const trailingSlash = 'always'

Or per-page:

// src/routes/geo-guide/+page.ts
export const prerender = true

GEO Checklist for SvelteKit

  • SSR active (default) or prerender=true for static output
  • svelte:head: title, description, canonical, author, robots
  • Open Graph: og:type=article, og:title, og:description, og:url, og:site_name, og:image
  • Article dates: article:published_time, article:modified_time
  • JSON-LD via {@html} injection in svelte:head
  • static/robots.txt with all 8 AI crawlers
  • static/llms.txt with site description and page listing
  • Dynamic sitemap.xml route with lastmod dates
  • Inverted pyramid content structure
  • Core Web Vitals: LCP < 2.5s, INP < 200ms, CLS < 0.1