β
seen
β later
basics
βΆ play
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>
#routing
#file-based
β Lesson 1 Β· Hello, page
β
seen
β later
basics
βΆ play
Frontmatter JavaScript at the top of an .astro file. Runs on the server.
---
const name = "Mae";
const missions = ["Endeavour", "Apollo 42"];
---
<h1>{name}</h1>
#server
#javascript
β Lesson 3 Β· Extract a component
β
seen
β later
basics
βΆ play
{ 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>
#template
#javascript
β Lesson 3 Β· Extract a component
β
seen
β later
basics
βΆ play
Scoped <style> Styles in an .astro file are auto-scoped to that component. No leaks.
<h1>Hello</h1>
<style>
h1 { color: crimson; }
</style>
#css
#scoping
β Lesson 2 Β· Style it up
β
seen
β later
basics
Component Any .astro file outside src/pages/ is a reusable component.
<!-- src/components/Card.astro -->
<article class="card">
<slot />
</article>
#reuse
β Lesson 3 Β· Extract a component
β
seen
β later
components
Astro.props Pass values to a component, destructure them in its frontmatter.
---
const { name, mission } = Astro.props;
---
<h1>{name}</h1>
<p>{mission}</p>
#data
#reuse
β Lesson 4 Β· Pass them around
β
seen
β later
components
<slot /> A hole for children. Layouts and wrappers fill it from outside.
<!-- Layout.astro -->
<html>
<body>
<main>
<slot />
</main>
</body>
</html>
#layouts
#children
β Lesson 1 Β· A shared shell
β
seen
β later
components
Spread props Forward an object as props with the JSX spread operator.
{ crew.map(a => <Card {...a} />) }
#reuse
#shorthand
β Lesson 5 Β· The whole crew
β
seen
β later
components
<Fragment> Group elements without adding a wrapper tag. Required for named slots.
<Layout>
<Fragment slot="pinboard">
<Sticky />
<Polaroid />
</Fragment>
</Layout>
#template
#slots
no lesson β just a useful trick
β
seen
β later
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
#pages
β Lesson 7 Β· A second page
β
seen
β later
routing
[name].astro Square brackets in a filename create a URL placeholder.
src/pages/crew/[name].astro
β /crew/mae
β /crew/ovidiu
β /crew/sergei
#dynamic
#params
β Lesson 8 Β· Profile pages
β
seen
β later
routing
βΆ play
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" } },
];
}
---
#dynamic
#build-time
#ssg
β Lesson 9 Β· Static paths, properly
β
seen
β later
routing
βΆ play
Astro.params Read the value that filled the [bracket] in the URL.
---
const { name } = Astro.params;
---
<h1>{name}</h1>
#dynamic
#url
β Lesson 9 Β· Static paths, properly
β
seen
β later
data
βΆ play
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>
#api
#build-time
#ssr
β Lesson 10 Β· Live from the API
β
seen
β later
data
βΆ play
.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>
#template
#arrays
β Lesson 5 Β· The whole crew
β
seen
β later
data
βΆ play
Conditional rendering Use && and ternaries to show or hide pieces of the template.
<p>{ status === "orbit" ? "π flying" : "π grounded" }</p>
{ admin && <button>delete</button> }
#template
#logic
β Lesson 6 Β· Status check
β
seen
β later
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" },
]);
}
#api
#json
#server
β Lesson 7 Β· An API endpoint
β
seen
β later
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 />
#markdown
#mdx
β Lesson 2 Β· Add some Markdown
β
seen
β later
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(),
}),
}),
};
#markdown
#schema
#zod
β Lesson 3 Β· Make a collection
β
seen
β later
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} />
#images
#performance
β Lesson 4 Β· Pictures, perfected
β
seen
β later
islands
client:load Hydrate this island as soon as the page loads. Eager. Use sparingly.
<Hype initial={42} client:load />
#hydration
#interactivity
β Lesson 5 Β· First island
β
seen
β later
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 -->
#hydration
#performance
β Lesson 6 Β· When to hydrate
β
seen
β later
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>
#philosophy
#performance
β Lesson 5 Β· First island
β
seen
β later
islands
View transitions Smooth cross-page animations. One component, browser does the rest.
---
import { ClientRouter } from "astro:transitions";
---
<head>
<ClientRouter />
</head>
#transitions
#ux
β Lesson 9 Β· Smooth as silk
β
seen
β later
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(),
});
#build
#ssr
#ssg
β Lesson 10 Β· Ship it for real
β
seen
β later
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
});
#ssr
#ssg
#config
β Lesson 10 Β· Ship it for real
β
seen
β later
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()],
});
#seo
#integrations
β Lesson 10 Β· Ship it for real the binder is quiet here
try a different word, or fewer letters.
27 concepts in the binder.