SvelteKit propose un modèle de chargement de données qui s'adapte au travail avec un CMS headless mieux que la plupart des frameworks. La séparation entre +page.ts (universel) et +page.server.ts (serveur uniquement) vous indique où placer la clé d'API. L'objet params gère le routage par langue et par slug sans middleware supplémentaire. Le résultat est une configuration qui prend une heure pour un développeur ayant déjà utilisé le framework et produit un blog rapide à charger, bien référencé, et tenant dans un seul fichier de configuration.
Ce guide connecte SvelteKit à une API REST de CMS headless, de bout en bout. Le code est écrit en s'appuyant sur l'API REST de Kamaan comme exemple concret, mais le schéma se transpose à tout CMS basé sur REST.
À retenir rapidement
- Utilisez
+page.server.tspour les appels au CMS. La fonction de chargement côté serveur garde la clé d'API hors du bundle client, et les données renvoyées au client ne contiennent jamais l'identifiant. - Acheminez la langue avec
params. Un dossier nommé[lang]produit une valeurparams.langque la fonction de chargement lit pour récupérer la bonne locale. - Définissez
entries()pour que SvelteKit puisse prérendre les URL des articles. La fonction renvoie les combinaisons de slug et de langue à prérendre lors du build, et l'étape de build génère un fichier HTML par article et par langue. - L'API REST de Kamaan livre les articles avec un paramètre de requête
languageet renvoie le corps, les champs SEO et le slug préfixé par la locale déjà renseignés. L'appel se fait en une seule requête par page. - Le même article est publié sur /en/blog/, /es/blog/, /de/blog/, /fr/blog/, /it/blog/ avec un hreflang correct automatiquement. SvelteKit lit depuis Kamaan, Kamaan gère la livraison multilingue.
Les fonctions de chargement de SvelteKit sont la seule chose à comprendre. Trois variantes :
+page.ts s'exécute à la fois côté serveur lors du SSR et côté client lors de la navigation. Tout ce que vous importez ici se retrouve dans le bundle client. N'y placez pas de clés d'API.
+page.server.ts s'exécute uniquement côté serveur. La fonction s'exécute pendant le SSR, renvoie du JSON au client pour l'hydratation, puis s'exécute à nouveau comme endpoint distant lorsque l'utilisateur navigue. Les clés d'API, les variables d'environnement réservées au serveur et les appels à la base de données vivent ici.
+layout.server.ts s'exécute côté serveur pour chaque page qui hérite du layout. À utiliser pour le contexte global du site nécessitant des identifiants serveur, comme un objet « site config » global récupéré depuis le CMS.
Pour un blog branché sur un CMS headless, chaque appel passe par +page.server.ts. Il n'existe aucun cas où vous devriez appeler l'API du CMS depuis +page.ts.
![]()
Structure de projet pour un blog multilingue
L'arborescence qui prend en charge /[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
Le dossier [lang] produit une valeur params.lang à chaque chargement. Le dossier [slug] fournit params.slug. SvelteKit gère le routage sans aucun plugin.
Le +page.server.ts minimum pour un article unique
Voici l'intégralité de la fonction de chargement pour /[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 };
};
Quelques points à noter. Les imports d'env utilisent $env/static/private, qui est la vérification à la compilation de SvelteKit assurant que la variable n'est disponible que côté serveur. Le fetch est la version augmentée de SvelteKit qui gère correctement les cookies et le comportement entre requêtes. Le helper error() lève une erreur SvelteKit typée que le template error.html de SvelteKit affichera.
La structure des données renvoyée par l'API REST de Kamaan inclut title, content, excerpt, meta_title, meta_description, og_image, featured_image_url, slug, language, et un tableau alternates contenant les URL de toutes les autres versions linguistiques. C'est ce tableau d'alternatives qui sert à rendre les balises hreflang dans le head.
Rendre l'article dans +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>
La boucle hreflang constitue à elle seule toute l'implémentation du SEO multilingue côté SvelteKit. Kamaan émet le tableau alternates ; SvelteKit rend les balises link. Aucune nécessité de calculer ou maintenir les références croisées manuellement.
Prérendu pour la performance
Pour un blog marketing, chaque article devrait être prérendu au build. Deux ajustements :
// +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 fonction entries() indique à SvelteKit quelles routes dynamiques prérendre au build. SvelteKit écrit alors un fichier HTML par article et par langue dans la sortie du build. Le build s'exécute une fois lorsque le contenu change et les fichiers statiques résultants sont servis depuis un CDN.
Pour des contenus qui changent fréquemment, passez prerender à false et utilisez la couche de cache de la plateforme (Vercel ISR, cache Cloudflare Workers, ou le stale-while-revalidate de votre CDN). Le fetch renvoie le contenu le plus récent à chaque cache miss.
La page d'index du blog
La page d'index liste tous les articles pour une langue donnée. Même schéma, endpoint différent :
// /[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 };
};
La pagination, le filtrage par catégorie et le tri se font tous via des paramètres de requête sur le même endpoint. La forme REST reste uniforme entre les pages.
Pourquoi ce schéma s'impose
Les fonctions de chargement côté serveur de SvelteKit éliminent la plus grande partie des surfaces où les blogs multilingues se trompent. La clé d'API n'atteint jamais le bundle client. Les annotations hreflang sont rendues à partir des données serveur, pas calculées à l'exécution. Le build peut produire du HTML statique pour chaque article dans chaque langue, ce que le crawler de Google récompense. Le petit bundle du framework et le rendu côté serveur maintiennent les Core Web Vitals au bon niveau.
L'Auto-Multilingual Delivery de Kamaan prend en charge la partie que SvelteKit ne peut pas gérer : produire les corps traduits, les slugs et les champs SEO. Le CMS publie les quatre versions traduites ; SvelteKit les lit avec le même fetch d'une ligne. Une seule configuration, quatre langues en ligne, aucun workflow séparé.
Deux flux de travail réels
Un fondateur de SaaS bootstrappé monte un blog SvelteKit en un week-end. Il branche un seul +page.server.ts sur l'API REST de Kamaan, déploie sur Vercel et connecte son domaine. Le blog est en ligne en anglais avec la structure d'URL /blog/[slug]. Quand il active l'Auto-Multilingual Delivery, le même code SvelteKit sert /es/blog/[slug], /de/blog/[slug], /fr/blog/[slug], /it/blog/[slug] à partir du prochain build. Temps total de développement : moins de deux heures.
Une petite agence gère trois sites SaaS clients tous sur SvelteKit. Le site de chaque client suit le même schéma alimenté par Kamaan avec un fichier d'environnement différent. Les nouveaux articles sont publiés depuis un seul compte Kamaan et reconstruisent les trois applications SvelteKit via webhook. L'opérateur de l'agence ne modifie jamais le code Svelte ; c'est le CMS qui fait le travail.
FAQ
Dois-je utiliser +page.ts ou +page.server.ts pour les appels au CMS ?
Toujours +page.server.ts. Les identifiants et l'URL de l'API appartiennent au serveur. Les données renvoyées au client devraient déjà être filtrées pour correspondre à ce que la page affiche.
SvelteKit dispose-t-il d'un adaptateur spécifique à Kamaan ?
Non, et il n'en a pas besoin. L'API REST de Kamaan renvoie du JSON standard. N'importe quel fetch SvelteKit fonctionne. Le schéma est identique à un appel vers n'importe quel endpoint REST.
Comment gérer le contenu en aperçu depuis le CMS ?
Ajoutez un paramètre de requête à l'appel d'API (?status=draft) et conditionnez-le derrière une vérification de variable d'environnement. Le schéma de mode preview de SvelteKit utilise des cookies pour activer l'accès aux brouillons uniquement sur le déploiement de preview.
Comment prérendre efficacement des milliers d'articles ?
La fonction entries() renvoie toutes les combinaisons à prérendre. SvelteKit gère la concurrence au build. Pour des blogs très volumineux, passez à l'ISR ou au rendu côté serveur avec mise en cache CDN pour amortir le coût du build.
Cela fonctionne-t-il avec adapter-static pour un export entièrement statique ?
Oui. prerender = true combiné à entries() permet à adapter-static de générer l'ensemble des fichiers HTML. Ajoutez une règle de repli pour les langues ajoutées après le build et déclenchez des rebuilds quand le contenu change.
Et pour les routes authentifiées qui ont besoin du CMS ?
Utilisez un hook SvelteKit (hooks.server.ts) pour vérifier la session avant l'exécution du load. L'appel au CMS reste dans +page.server.ts, mais le load renvoie 401 si la session est invalide.
À lire aussi sur Kamaan
- How to Add a Blog to Your SaaS Product: A Developer Guide. Le pilier parent qui couvre toute la décision côté développeur pour ajouter un blog à un frontend SaaS.
- Headless CMS Next.js Integration: Step-by-Step Setup From Zero to Live Blog. L'équivalent Next.js de ce guide, utile si vous hésitez entre les frameworks.
- Blog API: How to Fetch and Render Headless CMS Content in Any Framework. La version agnostique du contrat d'API, incluant la forme JSON renvoyée par Kamaan.
- What Is a Headless CMS: A Plain-English Guide for SaaS Teams. L'ancrage définitionnel si vous arrivez ici sans contexte de catégorie.
Commencez avec Kamaan
Un seul endpoint REST, toutes les langues en ligne
Kamaan vous offre un tableau de bord unique pour tous vos blogs produits, livrés via une API REST propre à n'importe quel framework. Traduction automatique en 99+ langues à chaque publication. Un seul compte couvre un nombre illimité de sites à 19 $ par mois, forfait fixe. Premier mois gratuit.

