Astro Islands Architect
Astro bedeutet: „HTML standardmäßig ausliefern, nur das hydrieren, was JS braucht.“ Für Marketing-, Dokumentations- und inhaltsreiche Websites erzeugt dies deutlich kleinere Bundles als React/Next.js. Die gesamte Herausforderung besteht darin zu entscheiden, welche Komponenten statisch und welche Inseln sind.
Wann verwenden
- Marketing-Website, Blog, Dokumentationsportal – Inhalt ist das Produkt.
- Migration von einer Next.js-Marketingseite, die zu viel clientseitiges JavaScript enthält.
- Multi-Framework – eine React-Komponentenbibliothek wird zusammen mit Vue- oder Svelte-Komponenten genutzt.
- Umfangreiche Inhaltserstellung mit MDX + typsicheren Frontmatter-Daten.
- View Transitions zwischen Seiten ohne SPA.
Kernfunktionen
Seitenstruktur
---
// src/pages/blog/[slug].astro – Frontmatter wird zur Build-/SSR-Zeit ausgeführt
import { getCollection, getEntry } from 'astro:content';
import Layout from '../../layouts/Default.astro';
import Newsletter from '../../components/Newsletter.tsx';
export async function getStaticPaths() {
const posts = await getCollection('blog');
return posts.map((p) => ({ params: { slug: p.slug }, props: { post: p } }));
}
const { post } = Astro.props;
const { Content } = await post.render();
---
<Layout title={post.data.title}>
<h1>{post.data.title}</h1>
<article><Content /></article>
{/* Insel – nur diese Komponente liefert JS aus */}
<Newsletter client:visible />
</Layout>
Die ----Begrenzungen sind Server-Code. Innerhalb von <...> ist der Standard statisches HTML. Komponenten mit client:*-Direktiven werden zu Inseln.
Client-Direktiven – die richtige auswählen
| Direktive | Wann wird hydriert | Verwendung für |
|---|---|---|
client:load | Seitenladevorgang | Oberhalb des sichtbaren Bereichs interaktiv (Warenkorb, Suche). |
client:idle | Wenn der Browser im Leerlauf ist | Nicht-kritische Interaktivität (Chat-Widget). |
client:visible | Wenn in den Viewport gescrollt wird | Unterhalb des sichtbaren Bereichs (Newsletter-Anmeldung, Kommentare). |
client:media="(min-width: 768px)" | Wenn die Media Query zutrifft | Desktop-only Widgets. |
client:only="react" | SSR überspringen; nur clientseitig rendern | Komponenten, die beim Mounten Browser-APIs benötigen. |
client:idle ist die richtige Standardeinstellung für die meisten nicht-kritischen Inseln. client:load ist der schwere Hammer; heben Sie ihn sich auf.
Content Collections
// src/content/config.ts
import { defineCollection, z } from 'astro:content';
const blog = defineCollection({
type: 'content', // Markdown-/MDX-Dateien
schema: z.object({
title: z.string().max(120),
description: z.string().max(160),
date: z.coerce.date(),
tags: z.array(z.string()).default([]),
draft: z.boolean().default(false),
cover: z.object({ src: z.string(), alt: z.string() }).optional(),
}),
});
export const collections = { blog };
Jetzt ist getCollection('blog') vollständig typisiert. Fehler im Frontmatter führen zu einem Build-Fehler, nicht zu Problemen in der Produktion.
const posts = (await getCollection('blog'))
.filter((p) => !p.data.draft)
.sort((a, b) => +b.data.date - +a.data.date);
Multi-Framework
npx astro add react vue svelte
---
import ReactSearch from '../components/Search.tsx';
import VueChart from '../components/Chart.vue';
import SvelteToggle from '../components/Toggle.svelte';
---
<ReactSearch client:idle />
<VueChart client:visible />
<SvelteToggle client:load />
Die Laufzeitumgebungen jedes Frameworks werden separat gebündelt und nur auf den Seiten geladen, die sie verwenden. Für eine überwiegend statische Dokumentationsseite kann dies bedeuten, dass auf den meisten Seiten kein React-JS ausgeliefert wird.
Server Islands (Astro 4+)
<Layout>
<Header />
<PersonalizedRecommendations server:defer>
<p slot="fallback">Empfehlungen werden geladen...</p>
</PersonalizedRecommendations>
<Footer />
</Layout>
server:defer rendert den Rest der Seite sofort und streamt das HTML der zurückgestellten Komponente über eine separate Anfrage nach. Nützlich für personalisierte Inhalte, die sonst die gesamte Seite zu SSR zwingen würden.
Bildoptimierung
---
import { Image } from 'astro:assets';
import cover from '../assets/cover.jpg';
---
<Image src={cover} alt="Titelfoto" widths={[400, 800, 1200]} sizes="(max-width: 768px) 400px, 1200px" />
Astro generiert srcset, nutzt Sharp zum Ändern der Größe und liefert nur das Nötigste aus. Bilder unter src/assets/ ablegen für vollständige Optimierung; public/ bleibt unbearbeitet.
View Transitions
---
import { ViewTransitions } from 'astro:transitions';
---
<html>
<head>
<ViewTransitions />
</head>
...
</html>
<a href="/about" transition:name="hero">Über uns</a>
Seitenübergreifende Übergänge ohne SPA. Der Browser erledigt den Großteil der Arbeit; Astro koordiniert das Element-Matching.
MDX mit Komponenten
---
title: Mein Beitrag
date: 2026-04-30
---
import Callout from '../../components/Callout.astro';
# {frontmatter.title}
<Callout type="warn">
Dies ist eine serverseitig gerenderte Warnung. Kein JS.
</Callout>
In MDX importierte Komponenten werden zur Build-/SSR-Zeit ausgeführt, sofern es sich nicht um Inseln handelt.
Hybrid SSR
// astro.config.mjs
import { defineConfig } from 'astro/config';
import cloudflare from '@astrojs/cloudflare';
export default defineConfig({
output: 'hybrid',
adapter: cloudflare(),
});
---
// src/pages/api/search.ts – läuft am Edge
import { search } from '../lib/search';
export const prerender = false;
export async function GET({ request }) {
const url = new URL(request.url);
const q = url.searchParams.get('q') ?? '';
const results = await search(q);
return new Response(JSON.stringify(results), { headers: { 'content-type': 'application/json' } });
}
---
prerender = false optiert eine Route für SSR; der Rest der Website bleibt statisch.
Anti-Patterns
Überall client:load
Symptom: Lighthouse JS-Bundle bläht sich auf; Astros Hydrierungsvorteil verschwindet.
Diagnose: Entwickler verwenden standardmäßig client:load, weil es am bekanntesten ist.
Fix: Standardmäßig client:idle oder client:visible verwenden. client:load für interaktive Inhalte oberhalb des sichtbaren Bereichs reservieren.
Daten in Client-Komponenten abrufen
Symptom: Lade-Spinner auf jeder Seite; SEO leidet.
Diagnose: Datenabruf in React-Komponenten verlagert, anstatt im Astro-Frontmatter.
Fix: Daten in den ----Begrenzungen abrufen, serverseitig rendern, Daten als Props an Inseln übergeben.
Ungetyptes Frontmatter
Symptom: Build erfolgreich; Produktion zeigt undefined in Titeln.
Diagnose: Kein Schema für Content Collections; Tippfehler im Frontmatter unbemerkt.
Fix: defineCollection mit einem Zod-Schema verwenden. Fehler im Frontmatter führen zu Build-Fehlern.
Vermischung von public/ und src/assets/ für Bilder
Symptom: Einige Bilder optimiert, andere in Originalgröße ausgeliefert.
Diagnose: public/ wird unverändert ausgeliefert; nur src/assets/ durchläuft Sharp.
Fix: Bilder nach src/assets/ verschieben. Überall <Image> verwenden. public/ nur für OG-Bilder und Favicons.
Astro für eine SPA verwenden
Symptom: Ständiger Kampf mit der Hydrierung; Inseln wachsen, bis die gesamte Seite interaktiv ist. Diagnose: Falsches Framework für die Aufgabe. Astro glänzt bei Inhalten; SPAs benötigen Next/Remix. Fix: Zu Next/Remix greifen, wenn die meisten Seiten interaktiv sind. Astro für Marketing + Dokumentation verwenden.
Übermäßige Verwendung von client:only
Symptom: Layout-Verschiebung (CLS) bei jedem Seitenladevorgang; „Blinzeln“ vor dem Erscheinen des Inhalts.
Diagnose: Das Überspringen von SSR führt dazu, dass der Browser einen leeren Platzhalter sieht, bis JS ausgeführt wird.
Fix: client:only nur verwenden, wenn die Komponente wirklich nicht serverseitig gerendert werden kann. Einen Platzhalter bereitstellen, der der endgültigen Größe nahekommt.
Qualitätskriterien
- Standard-Client-Direktive ist
client:idleoderclient:visible;client:loadist pro Verwendung begründet. - Alle Inhalte haben ein typisiertes Schema mittels
defineCollection. - Bilder liegen unter
src/assets/und verwenden die<Image>-Komponente. - Multi-Framework-Integrationen (
@astrojs/react, etc.) nur für tatsächlich vorhandene Komponenten hinzugefügt. - LCP unter 2s auf der langsamsten repräsentativen Seite.
- JS-Budget beim ersten Laden pro Seite festgelegt; CI schlägt bei Regressionen fehl.
- MDX verwendet Astro-Komponenten für statische Teile; Inseln nur dort, wo Interaktivität nötig ist.
- View Transitions auf den wichtigsten Navigationspfaden getestet.
NICHT geeignet für
- Vollständige SPAs – Next.js, Remix, TanStack Start sind besser geeignet.
- Erstellung von Astro-Plugins/Integrationen – separates Skill (noch kein dediziertes Skill).
- Vite-seitige Build-Optimierung – Astro verwendet Vite unter der Haube. →
vite-build-optimizerfür Chunk-Größen, HMR, Plugin-Lebenszyklus-Probleme. - Schwer lastende Client-State-Apps (Dashboards, Echtzeit-Tools) – zu viel JS, als dass Inseln helfen könnten.
- Native mobile Apps – Astro ist nur für das Web.