SvelteKit include un modello di caricamento dati che si adatta al lavoro con un CMS headless meglio della maggior parte dei framework. La separazione tra +page.ts (universale) e +page.server.ts (solo server) vi indica dove mettere la API key. L'oggetto params gestisce il routing della lingua e dello slug senza middleware aggiuntivi. Il risultato e' una configurazione che richiede un'ora a uno sviluppatore che ha gia' usato il framework e produce un blog che carica velocemente, si posiziona in modo pulito e resta in un singolo file di configurazione.
Questa guida collega SvelteKit a una REST API di un CMS headless, dall'inizio alla fine. Il codice e' scritto contro la REST API di Kamaan come esempio concreto, ma il pattern si trasferisce a qualunque CMS basato su REST.
Punti chiave
- Usate
+page.server.tsper le fetch al CMS. La funzione di load lato server tiene la API key fuori dal bundle client, e i dati restituiti al client non includono mai la credenziale. - Instradate la lingua con
params. Una cartella chiamata[lang]produce un valoreparams.langche la funzione di load legge per recuperare la localizzazione corretta. - Definite
entries()per permettere a SvelteKit di pre-renderizzare gli URL degli articoli. La funzione restituisce le combinazioni di slug e lingua da pre-renderizzare al build, e il build genera un file HTML per ogni articolo per ogni lingua. - La REST API di Kamaan fornisce gli articoli con un query parameter
languagee restituisce il corpo, i campi SEO e lo slug con prefisso locale gia' popolati. La fetch e' una sola chiamata per pagina. - Lo stesso articolo viene pubblicato su /en/blog/, /es/blog/, /de/blog/, /fr/blog/, /it/blog/ con hreflang corretto in automatico. SvelteKit legge da Kamaan, Kamaan gestisce la distribuzione multilingue.
Le funzioni di load di SvelteKit sono l'unica cosa che dovete capire. Tre varianti:
+page.ts viene eseguita sia lato server durante l'SSR sia lato client durante la navigazione. Tutto cio' che importate qui finisce nel bundle client. Non mettete qui le API key.
+page.server.ts viene eseguita solo lato server. La funzione viene eseguita durante l'SSR, restituisce JSON al client per l'idratazione e viene eseguita di nuovo come endpoint remoto quando l'utente naviga. API key, variabili d'ambiente solo server e chiamate al database vivono qui.
+layout.server.ts viene eseguita lato server per ogni pagina che eredita il layout. Usatela per il contesto a livello di sito che richiede credenziali server, come un oggetto globale "site config" recuperato dal CMS.
Per un blog collegato a un CMS headless, ogni fetch va in +page.server.ts. Non esiste uno scenario in cui dovreste chiamare la API del CMS da +page.ts.
![]()
Struttura del progetto per un blog multilingue
Il layout delle cartelle che supporta /[lang]/blog/[slug]:
src/routes/
[lang]/
blog/
+page.server.ts # blog index
+page.svelte
[slug]/
+page.server.ts # single article
+page.svelte
+layout.server.ts # site-wide data
+layout.svelte
La cartella [lang] produce un valore params.lang a ogni load. La cartella [slug] fornisce params.slug. SvelteKit gestisce il routing senza alcun plugin.
Il +page.server.ts minimo per un singolo articolo
Questa e' l'intera funzione di load per /[lang]/blog/[slug]:
import type { PageServerLoad } from './$types';
import { error } from '@sveltejs/kit';
import { KAMAAN_API, KAMAAN_KEY, KAMAAN_SITE_ID } from '$env/static/private';
export const load: PageServerLoad = async ({ params, fetch }) => {
const { lang, slug } = params;
const url = `${KAMAAN_API}/sites/${KAMAAN_SITE_ID}/articles/${slug}?language=${lang}`;
const res = await fetch(url, {
headers: { Authorization: `Bearer ${KAMAAN_KEY}` }
});
if (!res.ok) throw error(res.status, 'Article not found');
const article = await res.json();
return { article };
};
Alcune cose da notare. Gli import delle env usano $env/static/private, che e' il controllo a tempo di compilazione di SvelteKit per garantire che la variabile sia disponibile solo lato server. La fetch e' la versione aumentata da SvelteKit che gestisce correttamente i cookie e il comportamento cross-request. L'helper error() lancia un errore tipizzato di SvelteKit che il template error.html di SvelteKit renderizzera'.
La struttura dei dati restituita dalla REST API di Kamaan include title, content, excerpt, meta_title, meta_description, og_image, featured_image_url, slug, language e un array alternates con gli URL di tutte le altre versioni linguistiche. Quell'array alternates e' cio' che usate per renderizzare i tag link hreflang nell'head.
Rendering dell'articolo in +page.svelte
<script lang="ts">
export let data;
const { article } = data;
</script>
<svelte:head>
<title>{article.meta_title}</title>
<meta name="description" content={article.meta_description} />
<link rel="canonical" href={article.canonical_url} />
{#each article.alternates as alt}
<link rel="alternate" hreflang={alt.language} href={alt.url} />
{/each}
<meta property="og:image" content={article.og_image} />
</svelte:head>
<article>
<h1>{article.title}</h1>
{@html article.content_html}
</article>
Il loop hreflang e' l'intera implementazione SEO multilingue sul lato SvelteKit. Kamaan emette l'array alternates; SvelteKit renderizza i tag link. Non c'e' bisogno di calcolare o mantenere i riferimenti incrociati manualmente.
Prerendering per le prestazioni
Per un blog di marketing, ogni articolo dovrebbe essere pre-renderizzato al build. Due modifiche:
// +page.server.ts
export const prerender = true;
export const entries = async () => {
const res = await fetch(`${KAMAAN_API}/sites/${KAMAAN_SITE_ID}/articles?per_page=1000`, {
headers: { Authorization: `Bearer ${KAMAAN_KEY}` }
});
const { articles } = await res.json();
return articles.flatMap(a =>
a.languages.map(lang => ({ lang, slug: a.slug }))
);
};
La funzione entries() indica a SvelteKit quali rotte dinamiche pre-renderizzare al build. SvelteKit scrive quindi un file HTML per ogni articolo per ogni lingua nell'output di build. Il build viene eseguito una volta quando il contenuto cambia e i file statici risultanti vengono serviti da una CDN.
Per contenuti che si aggiornano frequentemente, impostate prerender a false e usate il livello di caching della piattaforma (Vercel ISR, Cloudflare Workers cache o lo stale-while-revalidate della vostra CDN). La fetch restituisce il contenuto piu' recente a ogni cache miss.
La pagina indice del blog
La pagina indice elenca tutti gli articoli per una determinata lingua. Stesso pattern, endpoint diverso:
// /[lang]/blog/+page.server.ts
import type { PageServerLoad } from './$types';
import { KAMAAN_API, KAMAAN_KEY, KAMAAN_SITE_ID } from '$env/static/private';
export const load: PageServerLoad = async ({ params, fetch }) => {
const res = await fetch(
`${KAMAAN_API}/sites/${KAMAAN_SITE_ID}/articles?language=${params.lang}&per_page=50`,
{ headers: { Authorization: `Bearer ${KAMAAN_KEY}` } }
);
const { articles } = await res.json();
return { articles };
};
Paginazione, filtro per categoria e ordinamento avvengono tutti come query parameter sullo stesso endpoint. La forma REST resta uniforme tra le pagine.
Perche' questo pattern funziona
Le funzioni di load server di SvelteKit rimuovono la maggior parte della superficie su cui i blog multilingue vanno in errore. La API key non raggiunge mai il bundle client. Le annotazioni hreflang vengono renderizzate dai dati server, non calcolate a runtime. Il build puo' produrre HTML statico per ogni articolo in ogni lingua, che e' cio' che il crawler di Google premia. Il bundle ridotto del framework e il rendering lato server mantengono i Core Web Vitals dove devono essere.
L'Auto-Multilingual Delivery di Kamaan gestisce la parte che SvelteKit non puo' coprire: produrre i corpi tradotti, gli slug e i campi SEO. Il CMS pubblica le quattro versioni tradotte; SvelteKit le legge con la stessa fetch di una riga. Un'unica configurazione, quattro lingue in produzione, nessun workflow separato.
Due flussi di lavoro reali
Un fondatore SaaS bootstrappato avvia un blog SvelteKit in un weekend. Collega un singolo +page.server.ts alla REST API di Kamaan, fa il deploy su Vercel e collega il proprio dominio. Il blog e' in produzione in inglese con la struttura URL /blog/[slug]. Quando attiva Auto-Multilingual Delivery, lo stesso codice SvelteKit serve /es/blog/[slug], /de/blog/[slug], /fr/blog/[slug], /it/blog/[slug] dal build successivo. Tempo totale di sviluppo: meno di due ore.
Una piccola agenzia gestisce tre siti SaaS clienti tutti su SvelteKit. Il sito di ogni cliente segue lo stesso pattern alimentato da Kamaan con un file env diverso. I nuovi articoli vengono pubblicati da un unico account Kamaan e ricompilano le tre app SvelteKit via webhook. L'operatore dell'agenzia non modifica mai il codice Svelte; il CMS fa il lavoro.
FAQ
Dovrei usare +page.ts o +page.server.ts per le fetch al CMS?
Sempre +page.server.ts. Le credenziali e l'URL della API appartengono al server. I dati restituiti al client dovrebbero essere gia' filtrati a quello che la pagina renderizza.
SvelteKit ha un adapter specifico per Kamaan?
No, e non ne ha bisogno. La REST API di Kamaan restituisce JSON standard. Qualunque fetch di SvelteKit funziona. Il pattern e' identico al fetch da qualunque endpoint REST.
Come gestisco i contenuti in preview dal CMS?
Aggiungete un query parameter alla chiamata API (?status=draft) e proteggetelo dietro un controllo su variabile d'ambiente. Il pattern di preview mode di SvelteKit usa i cookie per abilitare l'accesso ai draft solo sul deployment di preview.
Come pre-renderizzo migliaia di articoli in modo efficiente?
La funzione entries() restituisce tutte le combinazioni da pre-renderizzare. SvelteKit gestisce la concorrenza al build. Per blog molto grandi, passate a ISR o al rendering lato server con caching CDN per ammortizzare il costo del build.
Funziona con adapter-static per un export interamente statico?
Si'. prerender = true piu' entries() permette ad adapter-static di generare l'intero set di file HTML. Aggiungete una regola di fallback per le lingue aggiunte dopo il build e attivate la ricompilazione quando i contenuti cambiano.
E le rotte autenticate che hanno bisogno del CMS?
Usate un hook di SvelteKit (hooks.server.ts) per verificare la sessione prima che la load venga eseguita. La fetch al CMS resta in +page.server.ts, ma la load restituisce 401 se la sessione non e' valida.
Correlati su Kamaan
- How to Add a Blog to Your SaaS Product: A Developer Guide. Il pillar genitore che copre l'intera decisione lato sviluppatore per aggiungere un blog a un frontend SaaS.
- Headless CMS Next.js Integration: Step-by-Step Setup From Zero to Live Blog. L'equivalente Next.js di questa guida, utile se state scegliendo tra framework.
- Blog API: How to Fetch and Render Headless CMS Content in Any Framework. La versione agnostica al framework del contratto API, inclusa la forma JSON che Kamaan restituisce.
- What Is a Headless CMS: A Plain-English Guide for SaaS Teams. L'ancora definitoria se siete arrivati qui senza il contesto di categoria.
Inizia con Kamaan
Un endpoint REST, tutte le lingue in produzione
Kamaan vi offre un'unica dashboard per tutti i blog dei vostri prodotti, distribuita tramite una REST API pulita a qualunque framework. Tradotto in automatico in 99+ lingue a ogni pubblicazione. Un singolo account copre siti illimitati a 19$ al mese, fisso. Primo mese gratis.

