Astro Ships Zero JS by Default
Interactive islands only load JS when needed. For blogs and docs: instant loads, perfect Lighthouse scores.
Content Collections
const blog = defineCollection({
type: 'content',
schema: z.object({
title: z.string(),
description: z.string(),
publishedAt: z.date(),
tags: z.array(z.string()).default([]),
draft: z.boolean().default(false),
}),
})
export const collections = { blog }
---
export async function getStaticPaths() {
const posts = await getCollection('blog', ({ data }) => !data.draft)
return posts.map(post => ({ params: { slug: post.slug }, props: { post } }))
}
const { Content } = await Astro.props.post.render()
---
<article><Content /></article>
Islands Architecture
<StaticHeader /> <!-- 0 KB JS -->
<ReactCounter client:visible /> <!-- Load when visible -->
<VueSearch client:load /> <!-- Load immediately -->
<SvelteCart client:idle /> <!-- Load when browser idle -->
View Transitions
---
import { ViewTransitions } from 'astro:transitions'
---
<head><ViewTransitions /></head>
<h1 transition:name="post-title">Title</h1>
| Metric | Astro | Next.js App Router |
|---|---|---|
| JS homepage | 0 KB | 87 KB |
| Lighthouse | 100 | 91 |
-> Validate schemas with the JSON Viewer.