astrobench Β· play with code

/explore Β· the binder

Astro concepts

Every idea from the curriculum, with the smallest example that fits. Search, filter, jump straight to the lesson when you want depth.

tip: press ⌘K from anywhere to fuzzy-find a concept Β· cards with β–Ά open a live editor

basics

Astro page

Every file in src/pages/ becomes a route. No router config.

src/pages/about.astro

<h1>About</h1>
<p>I'm building in space.</p>
β†’ Lesson 1 Β· Hello, page
basics

Frontmatter

JavaScript at the top of an .astro file. Runs on the server.

---
const name = "Mae";
const missions = ["Endeavour", "Apollo 42"];
---

<h1>{name}</h1>
β†’ Lesson 3 Β· Extract a component
basics

{ expressions }

Embed any JavaScript expression in the template with curly braces.

<p>2 + 2 = {2 + 2}</p>
<p>upper: {name.toUpperCase()}</p>
<p>length: {crew.length}</p>
β†’ Lesson 3 Β· Extract a component
basics

Scoped <style>

Styles in an .astro file are auto-scoped to that component. No leaks.

<h1>Hello</h1>

<style>
    h1 { color: crimson; }
</style>
β†’ Lesson 2 Β· Style it up
basics

Component

Any .astro file outside src/pages/ is a reusable component.

<!-- src/components/Card.astro -->
<article class="card">
    <slot />
</article>
β†’ Lesson 3 Β· Extract a component
components

Astro.props

Pass values to a component, destructure them in its frontmatter.

---
const { name, mission } = Astro.props;
---

<h1>{name}</h1>
<p>{mission}</p>
β†’ Lesson 4 Β· Pass them around
components

<slot />

A hole for children. Layouts and wrappers fill it from outside.

<!-- Layout.astro -->
<html>
    <body>
        <main>
            <slot />
        </main>
    </body>
</html>
β†’ Lesson 1 Β· A shared shell
components

Spread props

Forward an object as props with the JSX spread operator.

{ crew.map(a => <Card {...a} />) }
β†’ Lesson 5 Β· The whole crew
components

<Fragment>

Group elements without adding a wrapper tag. Required for named slots.

<Layout>
    <Fragment slot="pinboard">
        <Sticky />
        <Polaroid />
    </Fragment>
</Layout>
no lesson β€” just a useful trick
routing

File-based routing

The file's path is the URL. No router config, no boilerplate.

src/pages/
β”œβ”€β”€ index.astro       β†’ /
β”œβ”€β”€ about.astro       β†’ /about
└── crew/
    └── index.astro   β†’ /crew
β†’ Lesson 7 Β· A second page
routing

[name].astro

Square brackets in a filename create a URL placeholder.

src/pages/crew/[name].astro

β†’ /crew/mae
β†’ /crew/ovidiu
β†’ /crew/sergei
β†’ Lesson 8 Β· Profile pages
routing

getStaticPaths

Tell Astro which dynamic-route pages to generate at build time.

---
export function getStaticPaths() {
    return [
        { params: { name: "mae" },    props: { mission: "Endeavour" } },
        { params: { name: "ovidiu" }, props: { mission: "Apollo 42" } },
    ];
}
---
β†’ Lesson 9 Β· Static paths, properly
routing

Astro.params

Read the value that filled the [bracket] in the URL.

---
const { name } = Astro.params;
---

<h1>{name}</h1>
β†’ Lesson 9 Β· Static paths, properly
data

fetch() in frontmatter

await fetch at build time. Data lands baked into the HTML.

---
const res = await fetch("/api/crew");
const crew = await res.json();
---

<h1>{crew.length} astronauts on shift</h1>
β†’ Lesson 10 Β· Live from the API
data

.map() over a list

Loop over an array and return components or markup. No v-for, no key spreading.

<ul>
    { crew.map(a => <li>{a.name}</li>) }
</ul>
β†’ Lesson 5 Β· The whole crew
data

Conditional rendering

Use && and ternaries to show or hide pieces of the template.

<p>{ status === "orbit" ? "πŸš€ flying" : "🌍 grounded" }</p>

{ admin && <button>delete</button> }
β†’ Lesson 6 Β· Status check
data

API endpoint

Export GET / POST from src/pages/api/* to return JSON instead of HTML.

// src/pages/api/crew.json.ts
export async function GET() {
    return Response.json([
        { name: "Mae", mission: "Endeavour" },
    ]);
}
β†’ Lesson 7 Β· An API endpoint
content

Markdown imports

Import .md files like modules. Get rendered HTML plus typed frontmatter.

---
import { Content, frontmatter } from "../logs/day-one.md";
---

<h1>{frontmatter.title}</h1>
<Content />
β†’ Lesson 2 Β· Add some Markdown
content

Content collections

A typed folder of markdown. Schema, queries, autocomplete β€” built in.

// content.config.ts
import { defineCollection, z } from "astro:content";

export const collections = {
    logs: defineCollection({
        schema: z.object({
            title: z.string(),
            date: z.date(),
        }),
    }),
};
β†’ Lesson 3 Β· Make a collection
content

<Image>

Drop-in tag that resizes, optimizes to webp, and lazy-loads.

---
import { Image } from "astro:assets";
import launch from "../assets/launch.jpg";
---

<Image src={launch} alt="liftoff" width={600} />
β†’ Lesson 4 Β· Pictures, perfected
islands

client:load

Hydrate this island as soon as the page loads. Eager. Use sparingly.

<Hype initial={42} client:load />
β†’ Lesson 5 Β· First island
islands

client:visible Β· idle Β· only

Defer hydration: when in viewport, when browser is idle, or skip SSR entirely.

<Search client:visible />      <!-- when scrolled to -->
<Chat   client:idle />         <!-- after first paint -->
<Map    client:only="react" /> <!-- never on server -->
β†’ Lesson 6 Β· When to hydrate
islands

Islands architecture

Page ships as static HTML. Each client:* directive is a small interactive island.

<article>
    <h1>Launch day</h1>
    <p>Static prose, zero JavaScript.</p>

    <Hype client:load />   <!-- this bit ships JS -->
</article>
β†’ Lesson 5 Β· First island
islands

View transitions

Smooth cross-page animations. One component, browser does the rest.

---
import { ClientRouter } from "astro:transitions";
---

<head>
    <ClientRouter />
</head>
β†’ Lesson 9 Β· Smooth as silk
deploy

Deploy adapter

An adapter tells Astro where it's going to run. One per host.

// astro.config.mjs
import { defineConfig } from "astro/config";
import vercel from "@astrojs/vercel";

export default defineConfig({
    adapter: vercel(),
});
β†’ Lesson 10 Β· Ship it for real
deploy

static vs server

Pick how each page renders: HTML at build time, or fresh on every request.

// astro.config.mjs
export default defineConfig({
    output: "static",   // default β€” pre-render everything
    // output: "server", // SSR every route
    // output: "hybrid", // static by default, opt-in SSR per page
});
β†’ Lesson 10 Β· Ship it for real
deploy

site + sitemap

Set your canonical URL, then the official integration generates sitemap.xml for free.

// astro.config.mjs
import sitemap from "@astrojs/sitemap";

export default defineConfig({
    site: "https://mission-control.vercel.app",
    integrations: [sitemap()],
});
β†’ Lesson 10 Β· Ship it for real

27 concepts in the binder.