Astro
Astro besitzt die statische Struktur: Seiten, Layouts und rein präsentative Bereiche.
SolidJS oder React besitzt die gesamte Interaktivität: Jede Komponente, die Benutzereingaben verarbeitet, Zustände verwaltet oder auf Ereignisse reagiert, ist ein Framework-Island – keine .astro-Datei.
CSS Modules werden für das gesamte Komponenten-Styling verwendet – immer in einer separaten Datei styles.module.css, niemals inline oder innerhalb des Astro <style>-Blocks.
Wann dieses Skill verwendet werden sollte
- Strukturierung eines neuen Astro-Projekts oder Hinzufügen von Seiten und Layout-Bereichen
- Erstellen von
.astro-Seiten, Layouts oder statischen präsentativen Bereichen - Entscheidung, ob etwas zu Astro oder zu einem SolidJS/React-Island gehört
- Auswahl der geeigneten Hydrationsdirektive für ein Island
- Anwendung von CSS Modules-Styling innerhalb von
.astro-Dateien oder Framework-Islands - Arbeiten mit Content Collections (
src/content/,src/content.config.ts) - Einspeisen von Service-Layer-Daten in serverseitig gerenderte Astro-Seiten
- Verwenden von Server Islands (
server:defer) für personalisierte, verzögerte Bereiche - Einrichtung von On-Demand-Rendering (SSR) mit Adaptern und Middleware
- Definieren und Aufrufen von Astro Actions für typsichere serverseitige Mutationen
Astro vs. Islands – Die zentrale Architekturentscheidung
Dies ist die wichtigste Entscheidung in jedem Astro-Projekt.
| Was es tut | Verwendung |
|---|---|
| Rendert statisches HTML aus Daten – keine Benutzerinteraktion | .astro |
| Seitenhülle, Layout-Wrapper, Navigationsstruktur | .astro |
| Inhaltsbereiche, Blogbeiträge, Marketingbereiche | .astro |
Jede Komponente mit onClick, onChange, Formularzustand oder reaktiven Daten | SolidJS- oder React-Island |
| Fundamentale UI-Komponenten (Button, Input, Modal, Alert, etc.) | SolidJS- oder React-Island |
| Jede Komponente, die einen Store oder Service-Hook liest oder schreibt | SolidJS- oder React-Island |
Die Regel in einfachen Worten:
- Astro rendert Struktur und Inhalt. Es verarbeitet keine Benutzerinteraktionen.
- SolidJS oder React übernimmt die gesamte Interaktivität, den gesamten Formularzustand und alle fundamentalen interaktiven UI-Primitive – selbst einen einfachen Button, der ein Ereignis auslöst.
- Erstelle keine interaktive UI in
.astro-Dateien. Astro hat keine Reaktivität. Der Versuch, einer.astro-Datei Interaktivität hinzuzufügen, erzeugt fragilen, nicht testbaren Code.
Welches Framework – SolidJS oder React?
Das Projekt verwendet durchgängig eines. Überprüfe astro.config.mjs auf die konfigurierte Integration (@astrojs/solid-js oder @astrojs/react) und verwende dieses Framework für alle Islands im Projekt. Mische nicht beide Frameworks im selben Projekt.
Lies ./references/islands-and-hydration.md für den vollständigen Entscheidungsleitfaden und die Referenz zu Hydrationsdirektiven.
Styling-Regeln – CSS Modules in separaten Dateien
Dies ist die wichtigste Regel in diesem Skill.
Jede Astro-Komponente, -Seite und jedes -Layout verwendet CSS Modules für bereichsbezogenes Styling. Das CSS befindet sich in einer separaten Datei – niemals innerhalb des Astro <style>-Tags.
ComponentName/
├── index.astro ← Komponentenlogik und Markup
└── styles.module.css ← ALLE Styles für diese Komponente, immer eine separate Datei
- Erstelle immer
styles.module.cssnebenindex.astro. - Setze niemals Styles innerhalb des Astro
<style>-Blocks. - Verwende niemals Inline-
style-Attribute. - Importiere das Modul oben im Frontmatter-Bereich:
import styles from './styles.module.css' - Wende Klassen mit der
class:list-Direktive oder direkter Klassenbindung an:<div class={styles.container}><div class:list={[styles.container, isActive && styles.active]}>
- Verwende semantische Klassennamen. Keine Stapelung von Utility-Klassen.
- Verwende CSS-Custom-Properties für alle Design-Tokens (Farben, Abstände, Typografie).
- Kein
!important. Keine Deep-Selektoren. Keine ID-Selektoren für das Styling.
Lies ./references/styling-and-css-modules.md für die Token-Strategie, Theme-Variablen, responsive Regeln und Animationsanleitung.
Lies ./examples/button-component/ für ein vollständiges Button-Beispiel mit separater CSS Module-Datei.
Projektstruktur
src/
├── components/
│ ├── foundation/ # Interaktive UI-Primitive – SolidJS oder React, NICHT .astro
│ │ ├── Button/
│ │ │ ├── index.tsx ← SolidJS (.tsx) oder React (.tsx)
│ │ │ └── styles.module.css
│ │ └── Input/
│ │ ├── index.tsx
│ │ └── styles.module.css
│ ├── layout/ # Statische Layout-Struktur – .astro
│ │ ├── Header/
│ │ │ ├── index.astro
│ │ │ └── styles.module.css
│ │ └── Footer/
│ ├── index.astro
│ └── styles.module.css
│ └── features/ # Interaktive Funktionsbereiche – SolidJS oder React
│ └── SomeFeature/
│ ├── index.tsx ← Island-Komponente
│ └── styles.module.css
├── layouts/
│ └── BaseLayout/
│ ├── index.astro ← Layout-Hülle ist immer .astro
│ └── styles.module.css
├── pages/
│ ├── index/
│ │ ├── index.astro ← Seite ist immer .astro
│ │ └── styles.module.css
│ └── [dynamic]/
│ ├── index.astro
│ └── styles.module.css
├── content/
│ ├── blog/
│ └── [collection-name]/
├── content.config.ts ← Astro 5 Collection-Schemas und Loader
├── store/
│ ├── repository/
│ ├── service/
│ └── config.ts
├── styles/
│ ├── tokens/
│ │ ├── _colors.css
│ │ ├── _spacing.css
│ │ └── _typography.css
│ ├── global.css
│ └── themes/
│ ├── light.css
│ └── dark.css
└── utils/
Namenskonventionen:
.astro-Seiten und -Layouts:PascalCase/-Ordner mitindex.astro- Framework-Island-Komponenten:
PascalCase/-Ordner mitindex.tsx - Seiten:
kebab-case/-Ordner mitindex.astrooder[dynamic]/mitindex.astro - CSS Module-Dateien: immer
styles.module.css– eine pro Komponentenverzeichnis - Hilfsfunktionen:
kebab-case.ts
Lies ./references/project-structure.md für eine vollständige Aufschlüsselung der Verzeichnisverantwortlichkeiten und Namenskonventionen.
Astro-Komponentenform
.astro-Dateien dienen nur der statischen Struktur. Sie rendern HTML aus Props und Daten – sie verarbeiten keine Ereignisse und verwalten keine Zustände.
- Verwende typisierte Props mittels
interface Propsim Frontmatter-Bereich. - Greife auf Props mit
Astro.propszu. - Halte Komponenten klein und auf eine Aufgabe ausgerichtet.
- Bevorzuge Komposition gegenüber Prop-Drilling.
- Halte Ausdrücke einfach; verlagere komplexe Logik in Frontmatter-Variablen.
- Verwende semantische HTML-Elemente –
<section>,<article>,<nav>,<main>,<header>– anstelle generischer<div>-Wrapper, wenn das Element eine semantische Bedeutung hat.
---
import styles from './styles.module.css'
interface Props {
title: string
subtitle?: string
}
const { title, subtitle } = Astro.props
---
<section class={styles.hero} aria-label={title}>
<h1 class={styles.title}>{title}</h1>
{subtitle && <p class={styles.subtitle}>{subtitle}</p>}
<slot />
</section>
Lies ./references/component-patterns.md für Frontmatter-Konventionen, Slot-Verwendung und Astro-API-Referenz.
Fundamentale Komponenten – SolidJS oder React, nicht Astro
Fundamentale UI-Primitive (Button, Input, Alert, Modal, Select, Toggle, etc.) werden immer als SolidJS- oder React-Komponenten erstellt. Sie sind niemals .astro-Dateien.
Warum: Fundamentale Komponenten verarbeiten Benutzerereignisse, verwalten den Fokus, stellen onClick- und onChange-Callbacks bereit und halten manchmal interne Zustände. Nichts davon ist in einer .astro-Datei möglich. Das Erstellen eines „Buttons“ als .astro erzeugt statisches HTML, das nicht in einer typisierten, wiederverwendbaren Weise auf Klicks reagieren kann.
Die Entscheidungsregel:
- Primitiv existiert – verwende es.
- Primitiv ist nah dran, aber es fehlt ein Prop – erweitere das bestehende Primitiv.
- Primitiv existiert nicht – erstelle es als SolidJS- oder React-Komponente in
components/foundation/, bevor du es verwendest. Integriere niemals einmalige interaktive Elemente in Seiten- oder Feature-Komponenten.
Alle fundamentalen Komponenten müssen konsistent, zugänglich und stark typisiert sein. Lies das Skill solid.js oder react für die Implementierungskonventionen, die innerhalb von Island-Komponenten gelten.
Islands und Hydration
Jede interaktive Komponente ist ein Island. Mounte Islands aus .astro-Seiten und -Layouts mit einer client:*-Direktive. Wähle die minimale Direktive, die die Interaktionsanforderung erfüllt.
| Direktive | Wann verwenden |
|---|---|
client:load | Muss sofort beim Seitenladen interaktiv sein |
client:idle | Kann warten, bis der Browser im Leerlauf ist |
client:visible | Muss nur hydrieren, wenn es in den Viewport kommt |
client:media="(query)" | Nur bei einer bestimmten Viewport-Größe interaktiv |
client:only="solid-js" oder client:only="react" | Benötigt Browser-APIs beim Rendern; SSR überspringen |
Standardmäßig client:visible oder client:idle verwenden. Verwende client:load nur für UI, mit der der Benutzer sofort interagiert – primäre Formulare, Navigations-Dropdowns, Auth-Seiten.
Island-Komponenten haben immer ihr eigenes styles.module.css:
components/features/ContactForm/
├── index.tsx ← SolidJS- oder React-Island
└── styles.module.css ← separates CSS Module, gleiche Regel wie überall sonst
Mounten eines Islands von einer Astro-Seite:
---
import styles from './styles.module.css'
import ContactForm from '../../components/features/ContactForm/index.tsx'
const { prefillEmail } = Astro.props
---
<section class={styles.section}>
<h2 class={styles.heading}>Kontaktiere uns</h2>
<ContactForm client:visible prefillEmail={prefillEmail} />
</section>
Lies ./references/islands-and-hydration.md für den vollständigen Entscheidungsleitfaden, frameworkspezifische Hinweise und Regeln zur Datenübergabe.
Content Collections
Content Collections sind die kanonische Methode, um Markdown-, MDX- und strukturierte Datendateien mit vollständiger TypeScript-Typsicherheit zu verwalten.
Wichtige Änderungen in Astro 5:
- Die Konfiguration befindet sich unter
src/content.config.ts(nichtsrc/content/config.ts) - Collections verwenden einen
loader–glob()oderfile()ausastro/loaders render(entry)ist eine Funktion, die ausastro:contentimportiert wird (nichtentry.render())- Importiere
zausastro:content, nicht direkt auszod
// src/content.config.ts
import { defineCollection, z, reference } from 'astro:content'
import { glob } from 'astro/loaders'
const blog = defineCollection({
loader: glob({ pattern: '**/*.md', base: './src/content/blog' }),
schema: z.object({
title: z.string(),
pubDate: z.coerce.date(),
draft: z.boolean().default(false),
}),
})
export const collections = { blog }
Rendern in einer Seite:
---
import { getEntry, render } from 'astro:content'
const post = await getEntry('blog', Astro.params.slug)
const { Content } = await render(post)
---
<Content />
Lies ./references/content-collections.md für die vollständige API: Loader, Cross-Collection-Referenzen, benutzerdefinierte Loader, Runtime-Collections und dynamische Routenmuster.
Server Islands, SSR und Actions
Server Islands (server:defer)
Verschiebe eine personalisierte oder dynamische .astro-Komponente, während die Seitenhülle sofort ausgeliefert wird. Erfordert einen Adapter.
<UserCart server:defer>
<span slot="fallback">···</span>
</UserCart>
Die verzögerte Komponente wird serverseitig ausgeführt und gestreamt. Verwende sie für benutzerspezifische UI (Warenkorb-Anzahl, Benachrichtigungen, personalisierte Begrüßungen), die sonst die statische Seitenauslieferung blockieren würde.
On-Demand-Rendering (SSR)
Optiere Seiten für serverseitiges Rendern mit export const prerender = false (Hybridmodus) oder setze output: 'server' global.
SSR-Seiten haben Zugriff auf Astro.cookies, Astro.request, Astro.redirect() und Astro.locals (befüllt durch Middleware).
Middleware
Definiere request-abfangende Logik in src/middleware.ts:
import { defineMiddleware } from 'astro:middleware'
export const onRequest = defineMiddleware(async (context, next) => {
context.locals.user = await getUser(context.cookies.get('session')?.value)
return next()
})
Astro Actions
Typsichere Serverfunktionen, definiert in src/actions/index.ts, aufrufbar von Islands oder HTML-Formularen:
// src/actions/index.ts
import { defineAction, ActionError } from 'astro:actions'
import { z } from 'astro:content'
export const server = {
newsletter: {
subscribe: defineAction({
input: z.object({ email: z.string().email() }),
handler: async ({ email }) => {
await addSubscriber(email)
return { success: true }
},
}),
},
}
Aufruf von einem Island:
import { actions } from 'astro:actions'
const { data, error } = await actions.newsletter.subscribe({ email })
Verwende isInputError(error), um Zod-Validierungsfehler von Handler-Fehlern zu unterscheiden.
Lies ./references/server-rendering.md für die vollständige Abdeckung: Rendermodi, Adapterkonfiguration, Middleware, Server Islands, On-Demand-APIs, Actions mit Formularen, Fehlercodes, typisierte Umgebungsvariablen (astro:env) und View Transitions.
Astro-API-Referenz
Astro.props– typisierte Komponenten-PropsAstro.params– dynamische Routenparameter ([slug],[...path])Astro.request– serverseitiges Request-Objekt (Header, Cookies, URL)Astro.url– aktuelle Seiten-URLgetStaticPaths()– Pfade für die statische Seitenerzeugung aufzählengetEntry()/getCollection()– Content Collection-Abfragen
Service-Layer-Datenfluss
Astro-Seiten und -Layouts sind der richtige Ort, um Service- oder Repository-Funktionen im Frontmatter-Bereich aufzurufen. Komponenten sollten Daten als Props erhalten.
- Rufe Daten im Seiten-Frontmatter ab, nicht innerhalb von Komponenten.
- Gib Daten als typisierte Props nach unten weiter.
- Komponenten dürfen keine Repositories oder externen APIs direkt aufrufen.
- Halte Validierung und Geschäftslogik im Service-Layer.
Barrierefreiheit
- Verwende semantische HTML-Elemente, wo immer das Element eine Bedeutung hat.
- Füge ARIA-Attribute hinzu, wo sie echte semantische Informationen liefern.
- Stelle sicher, dass alle interaktiven Elemente zugängliche Namen haben.
- Halte sichtbare Fokus-Zustände aufrecht.
- Halte eine Mindestberührungszielgröße von 44px für interaktive Elemente ein.
- Respektiere
prefers-reduced-motion. - Strukturiere Überschriften logisch (
h1→h2→h3) innerhalb des Seiteninhalts.
Lies ./references/accessibility-patterns.md für ARIA-Muster, Formularbeschriftungen und Anleitung zur Überschriftenhierarchie.
Unterstützende Dateien
./references/styling-and-css-modules.mdCSS Module-Regeln,