Capstone: Building a Content-Driven Site
This capstone builds one real, working blog — every earlier chapter contributes a real, working piece of it, not just a concept in isolation.
The Content Collection
Both zero-js-by-default.md and live-demo-post.mdx satisfy this same schema (Chapters 5 and 11).
Listing & Rendering Posts
This is Chapter 3's own getStaticPaths() pattern and Chapter 5's own props-passing shortcut, working together exactly as designed.
An Island for the Newsletter Signup
Placed on the blog index below the fold via <NewsletterSignup client:visible /> — Chapter 7's own genuine performance payoff: its JS never even downloads unless a visitor scrolls that far.
The API Endpoint — and Why It Forces a Rendering-Mode Decision
A real newsletter signup has to accept whatever email a visitor types, at the moment they submit it — exactly the case Chapter 10 identified as needing server output, not a static build. So the finished project's config reflects that decision:
output: 'server' for the whole project means the blog index and post pages now render per-request instead of once at build time — a genuine cost, since Chapter 3's own getStaticPaths() isn't even necessary anymore in this mode. A more finely-tuned version of this same project could keep the blog pages statically pre-rendered (export const prerender = true on just those two files, per Chapter 10) while leaving only subscribe.ts dynamic — the right call depends on how much of the site is genuinely static versus genuinely dynamic, exactly the judgment call Chapter 10 raised.
Every Chapter's Own Contribution
| Piece | Chapter(s) |
|---|---|
| Zero-JS static rendering as the baseline | 1 |
| Frontmatter, props, markup expressions | 2 |
Blog index/detail routing, getStaticPaths() | 3 |
BaseLayout, <slot /> | 4 |
| The blog Content Collection & schema | 5 |
| Scoped and global dark-theme styling | 6 |
client:visible on the newsletter island | 7 |
| The React integration itself | 8 |
The /api/subscribe POST endpoint | 9 |
output: 'server' + the Node adapter | 10 |
| The MDX post with its own embedded island | 11 |
Coding Challenges
Build this capstone's own blog collection, BaseLayout, index page, and [slug].astro route from scratch, with at least two real Markdown posts.
📄 View solutionAdd the NewsletterSignup island with client:visible, and the /api/subscribe endpoint it posts to, confirming a real submission returns { status: 'ok' }.
📄 View solutionRefine the project by marking the blog index and [slug] pages export const prerender = true while leaving subscribe.ts dynamic, and confirm the site still builds correctly with this mixed approach.
📄 View solutionChapter 12 Quick Reference
- Content Collections + Zod — one schema, mixed
.md/.mdxentries getStaticPaths()+props— dynamic routing without a second lookupclient:visible— an island whose JS never downloads until scrolled into view- A real POST endpoint — the concrete reason a project needs server output, not static
- Per-page
prerender— the finer-grained alternative to an all-or-nothing rendering mode
★ Astro Course Complete — 12 / 12 chapters
From zero-JS-by-default and the islands architecture through Content Collections, styling, cross-framework composition, data fetching, and rendering modes. Chapters 3 and 5 in particular — arbitrary-depth routing and structured content — are the direct foundation a future Website Rebuild with Astro course can now build on.