Zum Inhalt springen
Zurück zum Blog

Neue Seiten und neue Beiträge anlegen

RoutingInhalte

Astro leitet Routen direkt aus src/pages/ ab. Eine Datei anlegen genügt, es gibt keine Router-Konfiguration, in die man den Pfad zusätzlich eintragen müsste.

Datei Ergebnis
src/pages/index.astro /
src/pages/ueber.astro /ueber
src/pages/blog/index.astro /blog
src/pages/blog/[id].astro /blog/beliebiger-name

Eine gewöhnliche Seite

Das Grundmuster ist immer dasselbe: Layout importieren, Titel und Beschreibung mitgeben, Inhalt in einen Container legen.

---
import BaseLayout from '../layouts/BaseLayout.astro';
import Container from '../components/Container.astro';
---

<BaseLayout title="Leistungen" description="Was ich anbiete.">
  <Container width="narrow" class="py-16">
    <h1 class="text-4xl font-semibold tracking-tight">Leistungen</h1>
  </Container>
</BaseLayout>

title und description wandern automatisch in <title>, die Meta-Description und die Open-Graph-Tags. Um den <head> musst du dich nicht kümmern.

Container kennt drei Breiten: narrow für Fließtext, default als Normalfall, wide für Übersichten mit mehreren Spalten.

Ein Blogbeitrag

Beiträge sind Markdown-Dateien in src/content/blog/. Der Dateiname wird zur URL: mein-beitrag.md landet unter /blog/mein-beitrag.

Der Kopf der Datei ist nicht beliebig, sondern wird gegen ein Schema geprüft:

---
title: 'Titel des Beitrags'
description: 'Ein bis zwei Sätze für Übersicht und Suchergebnis.'
pubDate: 2026-07-31
tags: ['Optional']
draft: false
---

Das Schema steht in src/content.config.ts. Fehlt description oder ist pubDate kein gültiges Datum, bricht der Build mit einer klaren Meldung ab – statt eine halb kaputte Seite auszuliefern.

draft: true ist praktisch für angefangene Texte: Der Beitrag erscheint im Dev-Server, wird beim Produktions-Build aber übersprungen.

Ein neues Feld ergänzen

Angenommen, du willst pro Beitrag ein Titelbild. Dann kommt ins Schema:

schema: z.object({
  title: z.string(),
  cover: z.string().optional(),
});

Ab sofort kennt TypeScript post.data.cover, inklusive Autovervollständigung in allen Templates – ohne dass du irgendwo einen Typ von Hand pflegen musst.