Guide
Astro fundamentals explained
A documentation site with two hundred pages should not ship two megabytes of
React on every route. Astro is a content-focused web framework
that defaults to zero client-side JavaScript and hydrates
interactive widgets only where needed through its
islands architecture. Built on
Vite, Astro
renders .astro components to static HTML at build time, optionally
mixing in
React,
Vue, or
Svelte
islands with explicit client:* directives. Content collections
give typed Markdown and MDX with schema validation; output modes span fully
static sites, server-rendered pages, and hybrid per-route choices. This guide
covers project structure, the islands hydration model, content collections,
integrations and adapters, image optimization, a Harbor Archive docs portal
worked example, a framework decision table, common pitfalls, and a production
checklist — complementing our
rendering modes guide
and
Next.js guide
without repeating their app-router focus.
What Astro optimizes for
Most marketing sites, blogs, and documentation portals are read-heavy: visitors scroll text, follow links, and maybe submit a search or open a lightbox. Shipping a full single-page application runtime on every page wastes bandwidth and hurts Core Web Vitals — especially Largest Contentful Paint and Total Blocking Time. Astro inverts the default: pages are server-rendered HTML strings with no hydration unless you opt in per component.
- Content-first routing — file-based routes in
src/pages/; dynamic segments via[slug].astroand rest parameters. - Partial hydration — only components marked with
client:load,client:visible,client:idle, orclient:mediaship JavaScript to the browser. - Framework agnostic — use React for a date picker, Svelte for a chart, Vue for a modal on the same page without converting the whole site.
- Vite under the hood — fast dev server, HMR, and Rollup production builds familiar from other modern frontends.
Astro is a strong fit when SEO, performance, and editorial velocity matter more than rich client-side state. It is a weaker default for highly interactive dashboards where most of the screen updates every few seconds — those projects often prefer SPA-style state management from the start.
Project structure and .astro components
A typical Astro 4+ project separates concerns cleanly:
src/pages/— routes;index.astrobecomes/, nested folders mirror URL paths.src/layouts/— shared shells (header, footer, meta tags) wrapped around page content via slots.src/components/— reusable UI;.astrofor static markup, framework extensions for islands.src/content/— Markdown/MDX files managed by content collections (see below).public/— assets copied verbatim without processing (favicons,robots.txt).astro.config.mjs— integrations, output mode, site URL for sitemaps, adapter selection.
Anatomy of a .astro file
Each .astro file has a component script (frontmatter
between --- fences) and a template that looks like
HTML with expressions. The script runs only at build time (or on the server in
SSR mode); its imports and data fetching never reach the client bundle unless
referenced by a hydrated island.
---
// Runs at build time only
import BaseLayout from '../layouts/BaseLayout.astro';
const title = 'Release notes';
const posts = await fetchPosts();
---
<BaseLayout title={title}>
<h1>{title}</h1>
<ul>
{posts.map(p => <li><a href={p.url}>{p.title}</a></li>)}
</ul>
</BaseLayout>
Scoped styles live in <style> tags; Astro adds unique
attributes so rules do not leak globally. Use <style is:global>
sparingly for third-party widget overrides.
Islands architecture and client directives
The islands model treats each interactive widget as an isolated archipelago of JavaScript on an otherwise static HTML continent. You import a React component and choose when it hydrates:
client:load— hydrate immediately on page load; use for above-the-fold UI that must work instantly (search bar, theme toggle).client:idle— hydrate afterrequestIdleCallback; good for analytics widgets and secondary controls.client:visible— hydrate when the component enters the viewport; ideal for comments, charts, and embeds far down the page.client:media— hydrate when a CSS media query matches; useful for mobile-only menus.client:only— skip server render; render only on client (browser APIs, chart libraries that break SSR).
Each directive is a budget decision. A docs site with fifty
pages and one client:visible search island ships far less JS than
the same site as a Next.js app where the root layout hydrates everything.
Measure with Lighthouse and bundle analyzers after adding each island; it is
easy to accidentally mark too many components client:load and
recreate SPA weight.
Passing props to islands
Islands receive serializable props from the Astro template. Non-serializable values (functions, class instances) cannot cross the server/client boundary. Fetch data in the Astro frontmatter and pass plain objects into framework components.
Content collections
Blogs, changelogs, and API reference pages benefit from
content collections — typed content directories with
Zod schema validation in src/content/config.ts.
- Schema enforcement — required frontmatter fields (title, date, draft flag) fail the build if missing, catching editorial errors before deploy.
- getCollection() and getEntry() — query APIs replace fragile glob imports; filter drafts, sort by date, paginate.
- MDX support — embed React/Vue components inside Markdown for callouts and live demos while keeping prose in git.
- Generated types — TypeScript autocomplete for frontmatter keys in templates.
Dynamic routes like src/pages/guides/[slug].astro call
getStaticPaths() to enumerate slugs at build time for static
output, or use server mode for on-demand rendering. Pair collections with
SEO fundamentals
— unique titles, canonical URLs, and JSON-LD per entry are trivial when
layout components read collection metadata.
Integrations, images, and output modes
Official integrations
npx astro add react tailwind sitemap wires common stacks. Integrations
register Vite plugins, JSX handling, and type shims. Popular choices:
@astrojs/react,vue,svelte— island frameworks.@astrojs/tailwind— pairs with our Tailwind guide.@astrojs/mdx— MDX in content collections.@astrojs/sitemap— auto-generatesitemap.xmlfrom built routes.
Image optimization
The <Image /> component from astro:assets resizes,
converts to WebP/AVIF, and sets width/height to prevent layout shift. Local images
in src/assets/ are processed; remote images need explicit dimension
hints or authorized domains in config.
Static, server, and hybrid output
output: 'static'(default) — pre-render every page at build; deploy to any static host or CDN.output: 'server'— SSR on each request; requires an adapter (Node, Vercel, Netlify, Cloudflare).output: 'hybrid'— static by default withexport const prerender = falseon routes that need SSR (authenticated previews, A/B tests).
Content and marketing sites should default static; add SSR only for routes that truly need per-request data or cookies.
Worked example: Harbor Archive docs portal
Harbor Archive maintains four hundred internal how-to articles for warehouse staff. Requirements: sub-second LCP on cheap Android scanners, full-text search, versioned API reference from OpenAPI, and a single interactive “try this request” panel on API pages. The team chose Astro over a React SPA.
- Content collections —
guidesandapicollections with Zod schemas; drafts excluded from production builds viadraft: truefilter. - Layouts —
DocsLayout.astroinjects sidebar nav from collection tree, breadcrumb JSON-LD, and AdSense-safe meta tags on every page. - Search island — Pagefind indexes static HTML at build time; a
client:idleReact search box loads the Pagefind WASM bundle only after first paint. - API try-it panel — one
client:visibleReact island per API endpoint page; prose above the fold stays zero-JS. - Deploy —
output: 'static'to Cloudflare Pages; preview branches per pull request; sitemap integration lists only published slugs.
Result: median LCP dropped from 3.1s (previous Gatsby SPA) to 0.9s; JavaScript transferred on article pages fell from 380 KB to 42 KB (search island only after idle). Editorial workflow stayed Markdown-in-git with CI schema checks.
Framework decision table
| Your situation | Favor Astro when | Consider alternatives |
|---|---|---|
| Marketing site, blog, docs | Mostly static HTML, few interactive widgets, SEO-critical | Next.js if every page needs authenticated SSR |
| Mixed framework legacy | Islands let you embed existing React/Vue components incrementally | Micro-frontends if teams deploy independently |
| Editorial team writes Markdown | Content collections + MDX with build-time validation | Headless CMS + any framework if non-technical authors need WYSIWYG |
| Real-time dashboard | Not ideal — too much client state | React/Vue SPA or Next.js with heavy client components |
| Global CDN static hosting | output: 'static' deploys anywhere cheaply |
Plain HTML if you do not need components or collections |
| Already on Vite + React SPA | Migrate content routes to Astro; keep app shell as island or separate subdomain | Stay on Vite if migration cost exceeds perf gains |
Common pitfalls
- Hydrating everything with client:load — defeats islands; audit each component and prefer
client:visibleorclient:idle. - Fetching in island instead of Astro frontmatter — duplicates requests and exposes API keys if env vars leak into client bundles; fetch at build/SSR layer.
- Mixing output modes without planning — hybrid SSR routes need adapters and cold-start awareness on serverless.
- Ignoring image dimensions — skipping
<Image />width/height props hurts CLS scores. - Over-large MDX bundles — importing heavy chart libraries in every MDX file bloats pages; isolate charts in lazy islands.
- Assuming Astro replaces your CMS — git-based Markdown scales to mid-size teams; larger orgs still need workflow UI or headless CMS webhooks.
- Duplicate routing with public/ — files in
public/bypass Astro processing and can collide withsrc/pagespaths.
Production checklist
- Set
siteinastro.config.mjsfor canonical URLs and sitemap generation. - Define content collection schemas; fail CI on validation errors.
- Audit islands with bundle analyzer; document why each uses its
client:*directive. - Configure
@astrojs/sitemapor equivalent; exclude draft and admin routes. - Optimize images via
astro:assets; self-host fonts per web font guide. - Run Lighthouse on representative content pages (not just homepage).
- Choose
outputmode explicitly; add adapter only if SSR/hybrid routes exist. - Set security headers at CDN (CSP, HSTS); Astro does not replace edge configuration.
- Preview deploys on every PR; compare LCP and JS bytes vs production baseline.
Key takeaways
- Astro ships HTML-first pages with optional JavaScript islands — ideal for content-heavy sites where performance and SEO dominate.
.astrocomponents run at build time; framework components hydrate only when you add explicitclient:*directives.- Content collections provide typed, validated Markdown/MDX with query APIs for blogs, docs, and changelogs.
- Integrations cover React, Vue, Svelte, Tailwind, MDX, and sitemaps; Vite powers dev and production builds.
- Default to static output; reach for SSR or hybrid only on routes that genuinely need per-request logic.
Related reading
- Vite fundamentals explained — the build tool Astro uses under the hood
- SSR, CSR, SSG, and ISR explained — rendering mode vocabulary Astro maps to
- Next.js fundamentals explained — when a full React app framework fits better
- SEO fundamentals explained — meta tags, structured data, and crawlability for content sites