# SvelteKit SEO for AI agents

SvelteKit SSRs routes out of the box unless `ssr = false` is set or data is fetched inside `onMount`. Pair SvelteKit with clickd so your agent keeps copy in `+page.server.ts`, wires canonicals and JSON-LD in `<svelte:head>`, and verifies live HTML.

- Install skills: `npx skills add subinternal/clickd`
- Ask your agent: "Audit our SvelteKit app with clickd_brief and clickd_check (kind: "agent"), move client onMount fetches into +page.server.ts, wire svelte:head metadata, and rescan."
- MCP tools used: `clickd_brief`, `clickd_check`, `clickd_schema_create`, `clickd_rescan`

## What breaks most often on SvelteKit

- **Global or route ssr = false** (`content-in-html`): Setting `export const ssr = false` in `+layout.ts` for a browser-only library turns every child route into an empty client-rendered shell.
- **Data loaded in onMount instead of load()** (`content-in-html`): Fetching page copy inside `onMount` leaves server-rendered HTML blank when Googlebot or AI retrieval bots read the response.
- **Missing <svelte:head> per route** (`title`): Without route-level `<title>`, `<meta name="description">` and `<link rel="canonical">`, child routes repeat the root layout’s metadata.

## The SvelteKit agentic SEO loop

1. **Test non-JS HTML** (`clickd_check`): Run clickd_check (kind: "agent") on your live SvelteKit routes to verify full copy and headings arrive in the initial HTTP response.
2. **Move fetches to +page.server.ts** (`technical-seo`): Load page data in `+page.server.ts` (or universal `+page.ts`) and keep `ssr = true` on every public marketing and docs route.
3. **Standardize <svelte:head>** (`page-system`): Build a shared `<SeoHead>` Svelte component that renders `<title>`, canonical, Open Graph and escaped JSON-LD.
4. **Serve /sitemap.xml & /llms.txt** (`llms-txt`): Add `+server.ts` endpoints for `/sitemap.xml` and `/llms.txt` backed by your route manifest, deploy, and run clickd_rescan.

## Worked pattern (src/routes/guides/[slug]/+page.svelte)

Data returned from +page.server.ts renders in initial HTML and populates <svelte:head> cleanly.

```
<script lang="ts">
  let { data } = $props();
  const ld = JSON.stringify({
    '@context': 'https://schema.org',
    '@type': 'TechArticle',
    headline: data.guide.title,
    description: data.guide.description,
  }).replace(/</g, '\\u003c');
</script>

<svelte:head>
  <title>{data.guide.title} · Example Co</title>
  <meta name="description" content={data.guide.description} />
  <link rel="canonical" href={`https://example.com/guides/${data.guide.slug}`} />
  {@html `<script type="application/ld+json">${ld}</script>`}
</svelte:head>

<main>
  <h1>{data.guide.title}</h1>
  <p>{data.guide.answerFirst}</p>
</main>
```

## Prompts to run on SvelteKit

- **Check SvelteKit SSR coverage**: "Search src/routes for ssr = false and onMount fetches, verify live routes with clickd_check (kind: "agent"), and move public page data into +page.server.ts."
- **Build a shared SeoHead.svelte component**: "Create a reusable SeoHead.svelte component with title, description, canonical and escaped JSON-LD, and wire it across all public routes."
- **Add +server.ts sitemap & llms.txt**: "Create src/routes/sitemap.xml/+server.ts and src/routes/llms.txt/+server.ts from our routes and verify live with clickd_rescan."

## Paired skills

- [Technical SEO](https://clickd.com/skills/technical-seo): Audits and fixes crawlability, indexing, robots.txt, XML sitemaps, canonicals, redirect chains, server-rendered HTML and security headers in your site’s codebase, then checks every fix live with clickd.
- [Page system](https://clickd.com/skills/page-system): Builds a site’s page templates and shared components so crawlers and assistants read every page well, and proves each pattern with clickd. About structure and spacing, not color, type or brand.
- [Schema markup](https://clickd.com/skills/schema-markup): Generates, wires and validates JSON-LD structured data (Organization, LocalBusiness, Product, Service, Article, FAQPage and BreadcrumbList) from the visible facts on your pages, checked live with clickd.
- [llms.txt](https://clickd.com/skills/llms-txt): Adds a /llms.txt to a site, a plain text outline for AI assistants built from the site’s own pages and facts, with tests that catch a missing page, and checks it with clickd.

## Questions

### Why escape `<` in `{@html}` JSON-LD blocks in SvelteKit?

Because `{@html}` injects raw markup directly into `<svelte:head>`. Escaping `<` as `\u003c` guarantees a string containing `</script>` can never break out of the script tag.

### What if one interactive chart library requires `window` in SvelteKit?

Keep `ssr = true` on the route so all headings, prose and metadata render on the server, and guard only the interactive chart component with `browser` from `$app/environment` or dynamic import inside `onMount`.
