SvelteKit
@citedseo/svelte gives your load functions the feed and your pages their head tags. Two routes with server load functions.
Hand this to your coding agent
Read https://developer.citedseo.ai/llms-full.txt. My CitedSEO site ID is <YOUR_SITE_ID>. Install @citedseo/svelte. Add /blog and /blog/[slug] routes with server load functions and the article's JSON-LD in svelte:head in this SvelteKit app, reading my articles with that package. The feed needs no key. Use only the functions and addresses that file lists.
Install
Section titled “Install”npm install @citedseo/sveltepnpm add @citedseo/svelteyarn add @citedseo/sveltebun add @citedseo/svelteWhat you’ll add
Section titled “What you’ll add”- .env
Directorysrc
Directoryroutes
Directoryblog
- +page.server.ts
- +page.svelte the index
Directory[slug]
- +page.server.ts
- +page.svelte one article
Directoryapi
Directorycitedseo
- +server.ts the webhook
-
Set your site ID.
.env CITEDSEO_SITE_ID=YOUR_SITE_ID -
Add the index.
src/routes/blog/+page.server.ts import { fetchArticles } from '@citedseo/svelte';import { CITEDSEO_SITE_ID } from '$env/static/private';import type { PageServerLoad } from './$types';export const load: PageServerLoad = async ({ fetch }) => {const articles = await fetchArticles({ siteId: CITEDSEO_SITE_ID, fetch });return {articles: articles.map(({ id, slug, title, description }) => ({ id, slug, title, description })),};};src/routes/blog/+page.svelte <script lang="ts">let { data } = $props();</script><ul>{#each data.articles as article (article.id)}<li><a href={`/blog/${article.slug}`}>{article.title}</a><p>{article.description}</p></li>{/each}</ul> -
Add the article page.
articleHeadgives the title, description, canonical address, Open Graph tags and the JSON-LD script tag.src/routes/blog/[slug]/+page.server.ts import { articleHead, fetchArticle } from '@citedseo/svelte';import { error } from '@sveltejs/kit';import { CITEDSEO_SITE_ID } from '$env/static/private';import type { PageServerLoad } from './$types';export const load: PageServerLoad = async ({ params, fetch }) => {const article = await fetchArticle({ siteId: CITEDSEO_SITE_ID, slug: params.slug, fetch });if (!article) error(404, 'Article not found');return { article, head: articleHead(article) };};src/routes/blog/[slug]/+page.svelte <script lang="ts">let { data } = $props();const { article, head } = $derived(data);</script><svelte:head><title>{head.title}</title><meta name="description" content={head.description} /><link rel="canonical" href={head.canonical} />{#each head.meta as tag}<meta {...tag} />{/each}{#if head.jsonLdTag}{@html head.jsonLdTag}{/if}</svelte:head><article><h1>{article.title}</h1>{#if article.cover}<img src={article.cover.src} alt={article.cover.alt} />{/if}{@html article.html}</article>
Prerender, or render on the server
Section titled “Prerender, or render on the server”To build the blog as static pages, add export const prerender = true; to both +page.server.ts files, and rebuild when a webhook arrives.
On a server, each page view reads the manifest and one small file, both cached by the network, so a new article shows within a minute. See Caching and limits.
Either way, webhookHandler checks the signature of a post for you:
import { webhookHandler } from '@citedseo/svelte';import { CITEDSEO_WEBHOOK_SECRET } from '$env/static/private';import type { RequestHandler } from './$types';
const handle = webhookHandler({ secret: CITEDSEO_WEBHOOK_SECRET, onPost: async (post) => { // Start a build, clear your cache, or store post.data. },});
export const POST: RequestHandler = ({ request }) => handle(request);See Update the moment you publish.
Keep the address the same
Section titled “Keep the address the same”Each article’s url is where CitedSEO expects it to live, and its structured data and canonical address say so. If your blog isn’t at /blog, set the site’s blog path in the app’s settings to match your route. See SEO for your article pages.