Sofortantwort
Headless CMS einfach erklärt
CMS ohne festes Frontend – Inhalte per API für beliebige Kanäle nutzbar.
- Kurz gesagt
- Trennung von Content (Backend) und Darstellung (Frontend) – API-first
- Typischer Einsatz
- Multi-Channel-Publishing, Moderne Web-Stacks, Marketing
- Wichtig zu wissen
- Entwickler wählen frei das Frontend-Framework (React, Vue, Astro, Next.js)
Headless CMS im Überblick
Traditionelle CMS wie WordPress sind monolithisch: Das System verwaltet Inhalte und generiert gleichzeitig das HTML für die Website. Das war jahrelang praktisch – aber es hat Grenzen.
Was ist, wenn der gleiche Inhalt auf einer Website, einer mobilen App, einem Smart Speaker und einem digitalen Infoscreen erscheinen soll? Ein traditionelles CMS kann das nicht – es ist auf eine Darstellungsform ausgerichtet.
Ein Headless CMS trennt diese Verantwortlichkeiten: Das CMS verwaltet Inhalte und stellt sie über eine API bereit. Das Frontend – egal ob Website, App oder Voice Interface – holt sich die Daten und stellt sie dar, wie es möchte.
Traditionell vs. Headless
Traditionelles CMS (WordPress):
Redakteur schreibt Artikel
↓
WordPress speichert + generiert HTML
↓
Browser zeigt HTML an
(nur eine Ausgabe möglich)
Headless CMS (Contentful):
Redakteur schreibt Artikel
↓
Contentful speichert als strukturierte Daten
↓
API gibt JSON zurück
↓
Website (React) → rendert als Artikel
Mobile App (Swift) → rendert als Card
Smart Speaker → liest Text vor
Newsletter → formatiert als E-Mail
Wann Headless, wann traditionell?
| Situation | Empfehlung |
|---|---|
| Einfache Website, ein Redakteur | WordPress / traditionell |
| Mehrere Kanäle (Web + App) | Headless |
| Entwicklerteam vorhanden | Headless |
| Kein Entwickler, Redakteur macht alles | Traditionell |
| Hohe Performance-Anforderungen | Headless + SSG |
| Komplexe Content-Strukturen | Headless |
Technisch betrachtet
Content-Modellierung in Contentful
// Content-Typ "Article" in Contentful definiert:
{
contentType: 'article',
fields: [
{ id: 'title', type: 'Symbol' },
{ id: 'slug', type: 'Symbol' },
{ id: 'body', type: 'RichText' },
{ id: 'author', type: 'Link', linkType: 'Entry' },
{ id: 'heroImage', type: 'Link', linkType: 'Asset' },
{ id: 'publishDate', type: 'Date' },
{ id: 'tags', type: 'Array', items: { type: 'Symbol' } },
]
}
REST API abfragen
// Contentful REST API
const response = await fetch(
`https://cdn.contentful.com/spaces/${SPACE_ID}/entries` +
`?content_type=article&include=2&order=-fields.publishDate`,
{ headers: { Authorization: `Bearer ${ACCESS_TOKEN}` } }
);
const data = await response.json();
const articles = data.items;
GraphQL API (Contentful)
query GetArticles {
articleCollection(order: publishDate_DESC, limit: 10) {
items {
title
slug
publishDate
heroImage {
url
width
height
}
author {
name
avatar { url }
}
body { json }
}
}
}
Astro + Headless CMS (wie dieses Projekt)
---
// src/pages/blog/[slug].astro
import { createClient } from 'contentful';
const client = createClient({
space: import.meta.env.CONTENTFUL_SPACE_ID,
accessToken: import.meta.env.CONTENTFUL_ACCESS_TOKEN,
});
export async function getStaticPaths() {
const entries = await client.getEntries({ content_type: 'article' });
return entries.items.map(entry => ({
params: { slug: entry.fields.slug },
props: { article: entry },
}));
}
const { article } = Astro.props;
---
<article>
<h1>{article.fields.title}</h1>
<!-- Rich Text rendern -->
</article>
Sanity GROQ-Abfragen
// GROQ – Sanity's eigene Query Language
const query = `*[_type == "article" && !(_id in path("drafts.**"))] | order(publishDate desc) {
title,
slug,
publishDate,
"heroImageUrl": heroImage.asset->url,
"authorName": author->name,
body
}[0...10]`;
const articles = await sanityClient.fetch(query);
Preview Mode für Redakteure
// Next.js Draft Mode für Headless CMS Preview
// Redakteure sehen unveröffentlichte Inhalte live
export async function GET(request) {
const { searchParams } = new URL(request.url);
const secret = searchParams.get('secret');
const slug = searchParams.get('slug');
if (secret !== process.env.PREVIEW_SECRET) {
return new Response('Unauthorized', { status: 401 });
}
// Draft Mode aktivieren
draftMode().enable();
redirect(`/blog/${slug}`);
}