Astro is a web framework for content-driven websites. It renders every page to plain HTML, either at build time or on the server, and sends no JavaScript to the browser unless a specific component asks for it. That makes the Astro framework a strong fit for blogs, marketing sites, documentation, portfolios and stores, where most of the page is content and only a few parts need to be interactive.
This guide explains how Astro works in Astro 7, the current major version: how a page gets rendered, what “islands” means, where content lives, and when you should pick something else. Every example runs on a fresh project.
Key Takeaways
- Astro renders components to HTML and ships zero client JavaScript by default. Interactivity is opt-in per component with
client:*directives.- You can use React, Vue, Svelte, Solid or Preact components inside Astro pages, and mix them on one page.
- Content Collections give you typed Markdown and MDX, validated against a schema at build time.
- Sites are static by default. Add an adapter and Astro can render pages on request as well.
- Astro is the wrong tool for app-like products where nearly every screen is interactive.
What Astro does
An Astro project is a folder of .astro components, Markdown files and a config file. When you run astro build, Astro executes each component’s frontmatter (the code between the --- fences), renders the template to HTML, and writes the result to dist/. The browser receives that HTML and CSS. Nothing is “booted” on the client.
Here is a complete Astro page:
---
// src/pages/index.astro
const title = 'Hello, Astro';
const posts = ['Routing', 'Styling', 'Deploying'];
---
<html lang="en">
<head>
<title>{title}</title>
</head>
<body>
<h1>{title}</h1>
<ul>
{posts.map((post) => <li>{post}</li>)}
</ul>
</body>
</html>
The frontmatter runs on the server (or at build time), never in the browser. The template looks like HTML with JSX-style expressions. After the build, dist/index.html contains a finished list and no script tag.
Most frameworks render a page on the server and then re-run the same components in the browser to make them interactive, a step called hydration. Astro skips hydration for everything that does not need it.
How a page gets rendered: build time vs request time
Astro supports two rendering modes, and you can mix them per page.
| Mode | When HTML is made | Needs a server? | Good for |
|---|---|---|---|
| Static (default) | Once, during astro build |
No | Blogs, docs, marketing pages |
| On-demand | On every request | Yes, via an adapter | Logged-in pages, personalised content, form handling |
Static is the default because most content does not change between visitors. A static site is a folder of files that any host can serve, which is why Astro sites are cheap to run and hard to break.
When a page needs fresh data per request, you install an adapter (for Node.js, Cloudflare, Netlify or Vercel) and mark that page as on-demand:
---
// src/pages/account.astro
export const prerender = false;
const user = Astro.cookies.get('session')?.value;
---
<p>Signed in as {user ?? 'guest'}</p>
The rest of the site stays static. Our Astro SSR guide covers adapters, server islands and actions in depth.
Islands: interactive parts without a single-page app
An island is one interactive component on an otherwise static page. Astro renders the island’s HTML on the server like everything else, then loads that component’s JavaScript in the browser so it can respond to clicks.
You create an island by adding a client:* directive to a framework component:
---
// src/pages/pricing.astro
import Header from '../components/Header.astro';
import PriceCalculator from '../components/PriceCalculator.jsx';
---
<Header />
<h1>Pricing</h1>
<PriceCalculator client:visible />
Header.astro produces HTML only. PriceCalculator.jsx is a React component, and client:visible tells Astro to download and hydrate it once it scrolls into view. If the reader never scrolls that far, the JavaScript never loads.
The directives you will use most:
client:loadhydrates as soon as the page loads. Use it for above-the-fold controls.client:idlewaits until the browser is idle. Good for widgets that matter but aren’t urgent.client:visiblewaits until the component enters the viewport.client:media="(max-width: 768px)"hydrates only when a media query matches, which suits mobile-only menus.client:only="react"skips server rendering and renders only in the browser.
Islands are independent. A slow chart widget can’t block a newsletter form elsewhere on the page, and each island can use a different framework. The official islands explainer has diagrams of how they load.
For a deeper look at directives and when each one pays off, see lazy-loading islands with client:visible.
Content is a first-class feature
Astro was built around content. Markdown and MDX files work out of the box, and Content Collections turn a folder of them into a typed data source.
You define collections in src/content.config.ts:
// src/content.config.ts
import { defineCollection } from 'astro:content';
import { glob } from 'astro/loaders';
import { z } from 'astro/zod';
const blog = defineCollection({
loader: glob({ pattern: '**/*.{md,mdx}', base: './src/content/blog' }),
schema: z.object({
title: z.string(),
pubDate: z.coerce.date(),
draft: z.boolean().default(false),
}),
});
export const collections = { blog };
Then you query them anywhere:
---
import { getCollection } from 'astro:content';
const posts = await getCollection('blog', ({ data }) => !data.draft);
---
If a post is missing title or has a date Astro can’t parse, the build fails with the file name and field. You find the mistake on your machine, not on the live site. The full API is in our Astro Content Collections guide.
Loaders are not limited to local files. The same getCollection() call can read from a headless CMS or an API through a custom loader, which is how most CMS integrations plug into Astro 7.
Components, layouts and styles
Astro components are the building blocks. They accept props, render slots (like children in React), and can include scoped <style> blocks that only affect that component. Layouts are ordinary components that wrap pages.
---
// src/layouts/BaseLayout.astro
interface Props { title: string }
const { title } = Astro.props;
---
<html lang="en">
<head><title>{title}</title></head>
<body>
<slot />
</body>
</html>
<style>
body { font-family: system-ui, sans-serif; }
</style>
Because .astro components render once and never ship to the browser, they have no state and no lifecycle hooks. When you need interactivity, you either add a small <script> tag (bundled and deduplicated by Astro) or reach for a framework island. The Astro components guide walks through props, slots and patterns in detail.
Every Komal theme is a complete Astro 7 project with layouts, components and content already wired up. If you’d rather edit a working site than assemble one, browse Astro themes by site type.
What Astro is good for, and what it is not
Astro fits best when content is most of the page:
- Blogs and publications. Markdown, MDX, RSS and sitemaps are all built in or one integration away.
- Marketing and landing pages. Fast first load, and you can still drop in a React pricing calculator.
- Documentation. Starlight, Astro’s docs theme, is one of the most popular ways to ship a docs site.
- Portfolios and agency sites. Image optimisation and view transitions without a heavy runtime.
- E-commerce storefronts that render product pages statically and call a cart API from an island.
It is a weaker choice when the whole product is an application:
- Dashboards and editors where nearly every element is stateful.
- Apps that rely on client-side routing with shared state across many screens.
- Teams already invested in a React meta-framework with server components across the codebase.
You can build interactive features in Astro, and server islands plus actions cover a lot of ground. But if 90% of your UI is islands, you are paying for the boundaries without getting the benefit. Our Astro vs Next.js comparison goes through that trade-off project by project.
What changed by Astro 7
If you learned Astro a few versions ago, a few patterns are different now:
- Collections are defined in
src/content.config.tswith aloader. The oldsrc/content/config.tswithout loaders only works behind the temporarylegacy.collectionsBackwardsCompatflag. - Entries have an
id, not aslug, and you render them withrender(entry)imported fromastro:content. Astro.glob()has been removed. Useimport.meta.glob()or a collection.<ViewTransitions />is now<ClientRouter />fromastro:transitions.- The Fonts API is stable and configured with the top-level
fontsoption. - Astro 7 requires Node.js 22.12 or newer and runs on Vite 8 and Zod 4.
Import z from astro/zod. Importing it from astro:content still works in Astro 7, but that export is marked for removal in Astro 8.
Your first Astro project in five commands
You need Node.js 22.12 or later.
npm create astro@latest my-site
cd my-site
npm run dev # http://localhost:4321
npx astro add mdx # optional: MDX support
npm run build # writes the static site to dist/
npm create astro asks a few questions (template, TypeScript, git) and installs dependencies. npm run dev starts a dev server with hot reload. npm run build produces dist/, a folder you can upload to Cloudflare Pages, Netlify, Vercel or any static host.
The folder you get looks like this:
my-site/
├── astro.config.mjs
├── package.json
├── public/ # copied as-is: favicon, robots.txt
└── src/
├── components/
├── layouts/
└── pages/ # every file here becomes a route
For the commands you’ll run day to day, keep the Astro CLI cheatsheet open. For what each folder does inside a production theme, see the folder structure docs.
How Astro compares at a glance
| Astro | Typical React meta-framework | Classic static site generator | |
|---|---|---|---|
| Default client JS | None | Framework runtime + page code | None |
| Interactive components | Islands, any framework | Everywhere, one framework | Hand-written scripts |
| Content model | Typed collections | Up to you | Front matter + templates |
| Server rendering | Optional, per page | Built in | Usually no |
| Output | Static files or server | Server or static export | Static files |
The comparison that matters most is the first row. Astro’s default is the smallest amount of JavaScript the page needs, and you add from there. Most alternatives start with a runtime and ask you to remove from it.
Common misconceptions about Astro
A few beliefs come up again and again from people evaluating Astro for the first time. Most date from early versions.
“Astro is only for static sites”
Static is the default, not the limit. With an adapter, any page can render on request, read cookies, and return custom responses. Server islands let a static page include one per-request component, actions handle form submissions with validation, middleware runs before every request, and sessions store per-visitor data. You can build a logged-in account area, a store with a live cart, or an API, all in the same project as your static marketing pages.
“You can’t build interactive UI with Astro”
You can, through islands. A React, Vue, Svelte, Solid or Preact component with a client:* directive behaves exactly as it would in a single-page app, with state, effects and event handlers. What Astro changes is the scope: interactivity is per component, not per page. For small interactions you don’t need a framework at all; a <script> tag in an Astro component is bundled and runs once per page.
---
// src/components/Disclosure.astro
---
<button class="disclosure" aria-expanded="false">Show details</button>
<div class="details" hidden><slot /></div>
<script>
document.querySelectorAll<HTMLButtonElement>('.disclosure').forEach((btn) => {
btn.addEventListener('click', () => {
const open = btn.getAttribute('aria-expanded') === 'true';
btn.setAttribute('aria-expanded', String(!open));
(btn.nextElementSibling as HTMLElement).hidden = open;
});
});
</script>
That disclosure widget ships a few hundred bytes of JavaScript and no framework.
“Astro replaces React”
Astro is a framework for building sites; React is a library for building components. Astro can render React components, on the server only or as hydrated islands, so the two work together. Teams often keep a design system in React and use it from Astro pages.
“Every island needs its own framework”
Islands are independent, but they can all use the same framework, and they share its runtime. Two React islands on one page load React once. Mixing frameworks is possible, not required, and on a production site you’ll usually pick one for client components.
“Content collections lock you into Markdown”
Collections are a data layer, not a file format. The glob() loader reads Markdown, MDX, JSON or YAML files; the file() loader reads a single data file; custom loaders read from any API. The query API is the same for all of them, so moving content from local files into a CMS later doesn’t change your page code.
What a real project looks like after six months
Tutorials stop at “hello world”. Here’s where a typical marketing site with a blog ends up:
src/
├── actions/ # form handlers, if an adapter is used
├── assets/ # images processed by astro:assets
├── components/
│ ├── ui/ # Button, Card, Badge
│ └── sections/ # Hero, Pricing, FAQ, CTA
├── content/
│ ├── blog/ # Markdown/MDX posts
│ └── case-studies/
├── data/ # site.ts: navigation, footer links, contact details
├── layouts/ # BaseLayout, BlogPost
├── pages/
│ ├── blog/[...slug].astro
│ ├── rss.xml.ts
│ └── index.astro
├── styles/ # global.css, tokens.css
└── content.config.ts
The pattern is a clean split: content in src/content/ and src/data/, presentation in src/components/ and src/layouts/, and routes in src/pages/ that do little more than query and render. Following it from day one is what keeps a site easy to hand over. Our folder structure docs describe each folder in a Komal theme.
Where to go next
If you’re evaluating Astro for a real project, the fastest route to an informed decision is to clone a working site and change it. Our getting started docs show how to run any Komal theme locally in a couple of minutes, and the themes catalog covers 26 site categories.
To learn the framework piece by piece, read the pillar guides in order: components, content collections, routing, styling, performance, SEO and deployment.
FAQ
Is Astro a static site generator?
Astro generates static sites by default, but it is more than a static site generator. With an adapter it renders pages on request, handles form submissions through actions, and runs middleware. You can mix static and on-demand pages in one project.
Do I need to know React to use Astro?
No. Astro components use their own HTML-like syntax and need no framework. React, Vue, Svelte, Solid and Preact are optional integrations you add when a component needs client-side interactivity.
Is Astro free?
Yes. Astro is open source under the MIT licence and maintained on GitHub. Hosting a static Astro site is free on most major static hosts.
Is Astro good for SEO?
Astro outputs complete HTML that crawlers can read without running JavaScript, and it loads fast by default, both of which help search performance. You still need titles, descriptions, a sitemap and canonical URLs; the Astro SEO checklist covers each one.
What version of Astro is current?
Astro 7 is the current major version as of October 2026. It requires Node.js 22.12 or later.