kursvorort.de ist ein Verzeichnis für Kurse vor Ort in Deutschland, mit Schwimmkursen, Tanzkursen und Yogakursen in 15 Städten. Die Lücke, die es schließen soll: Kursangebote sind über Bäder, VHS, Vereine und private Schulen verstreut, und ihre Preise lassen sich kaum vergleichen. In diesem Artikel geht es darum, wie die Seite technisch gebaut ist und warum sie ohne Datenbank, ohne Login und ohne ein einziges Formular auskommt.
Der Stack
- Next.js 15 mit App Router, React 19, TypeScript strict, Tailwind
- vollständig statisch: jede Seite entsteht beim Build über
generateStaticParams,dynamicParams = false - Hosting auf Vercel, Vercel Web Analytics ohne Cookies
- keine Datenbank, keine Konten; Anbieter melden sich per E-Mail
Für ein Verzeichnis klingt das ungewöhnlich, passt aber zum Inhalt. Die Seiten ändern sich nur, wenn neu recherchiert wird, und nicht bei jedem Seitenaufruf. Statisch heißt: schnelle Seiten, nichts, was nachts ausfallen kann, und keine personenbezogenen Daten, die nach DSGVO geschützt werden müssten.
Daten als typisierte Dateien
Alle Inhalte liegen typisiert in src/data/: Kategorien, Städte, Anbieter und pro Seite eine Content-Datei wie src/data/content/tanzkurse-berlin.ts. Seiten lesen diese Daten nie direkt, sondern nur über eine Schicht in src/lib/data.ts. Das hält die Quelle austauschbar: Wenn später bezahlte Einträge dazukommen, wandern die Anbieter in eine Datenbank, und keine Seite muss angefasst werden.
Die URL-Struktur folgt den Suchbegriffen: /{kategorie}/{stadt}, also /schwimmkurse/berlin, und eine Ebene tiefer /{kategorie}/{stadt}/{kursart} für Kursarten. Title, H1 und Description entstehen zentral aus einem Muster und werden in Tests auf ihre Länge geprüft.
Recherche als Pipeline mit Gate
Der interessanteste Teil ist der Weg von der Recherche zur Seite. Für jede Stadt und Kategorie gibt es zuerst ein Recherche-Dokument mit allen Angeboten aus den Primärquellen: Preis, Termine, Minuten pro Einheit, Pflichtkosten wie Eintritt oder Mitgliedsbeitrag, Anmeldeweg, Quelle und Datum. Daraus rechnet die Pipeline den Preis pro Einheit (45 Minuten bei Schwimmkursen, 60 Minuten pro Person bei Tanz und Yoga) und den Median je Anbietertyp.
Bevor eine Seite in den Build darf, läuft ein blockierendes Gate:
- jede Zahl im Text braucht eine Quelle mit Stand-Datum, jede Tabelle eine Quellenzeile direkt darunter
- externe Links im Redaktionstext müssen
nofollowtragen, und es braucht eine Mindestzahl verschiedener Quellen-Domains - keine Bewertungen oder Sterne im Text, keine zukünftigen Daten im Fließtext (die gehören in automatisch ablaufende Kursstart-Blöcke)
- kein Satz darf wortgleich auf einer anderen Stadtseite stehen, und jede Stadt braucht eigene Abschnitte
- FAQ-Schema, sichtbares Prüfdatum, Pflicht-Blöcke wie Preisvergleich und Rechner, dazu Typecheck, Tests, Build, ein SEO-Audit gegen den lokalen Server und ein Mobile-Check
Erst wenn alles grün ist, wird die Seite gebaut.
Indexierung nur mit Inhalt
Eine Stadtseite ist nur indexierbar, wenn sie redaktionellen Inhalt oder mindestens drei freigegebene Anbieter hat. Sonst bekommt sie noindex,follow und fehlt in der Sitemap. Interne Links zeigen nur auf indexierbare Seiten; Städte ohne Recherche erscheinen als "bald" ohne Link. Die Sitemap entsteht aus denselben Daten, aktuell mit 96 URLs.
Keine Redirects, nie
kursvorort hatte vorher eine Lovable-Version mit eigenen URLs. Beim Neubau blieben alle bestehenden URLs exakt gleich, ein Test prüft die alte Sitemap auf Status 200 ohne Weiterleitung und auf ein Canonical, das auf sich selbst zeigt. Routen, die es nicht mehr braucht, werden schlicht nicht gebaut und liefern 404.
Was ich daraus mitnehme
Für ein Verzeichnis, dessen Wert in geprüften Daten liegt, ist die statische Seite mit Datendateien und Gate die einfachste Architektur, die trägt. Die Komplexität steckt in der Recherche und in den Regeln, die das Gate erzwingt, und genau dort sollte sie liegen.
Wenn du dir das Ergebnis ansehen willst: kursvorort.de, zum Beispiel die Tanzkurse in Berlin. Fragen zur Pipeline beantworte ich gern in den Kommentaren.