Skip to the content

Module 1: How a website reaches you

Module 2: Tools, frameworks and the build step

Module 3: Your first Astro project

Module 4: Inside an .astro file

Module 5: Components and props

Module 6: Slots: components that wrap other things

Module 7: Styles and small scripts

Module 8: Pages, links and routes

Module 9: Layouts and the page head

Module 10: Markdown and content collections

Module 11: Images, fonts and other files

Module 12: Islands and client directives

Module 13: Rendering modes, adapters and deploying

Module 14: Endpoints, middleware and environment variables

Module 15: Forms and actions

Module 16: Languages and view transitions

Module 17: Performance and caching

Module 18: Security and the Content Security Policy

Module 19: Integrations, the build pipeline and architecture

Module 20: Mastery challenge

Module 21: Final project: a conference website

Answer key

  1. 1. How a website reaches you · An address says which server, and which page · QuizIn https://example.com/blog/first-post?ref=home, what is the path?B. /blog/first-postThe path starts at the first / after the domain and stops before ?. example.com is the domain (which server), and ?ref=home is the query (extra details). The path is what tells the server which page you want.
  2. 1. How a website reaches you · The browser reads the HTML, then fetches what it points to · Put in orderPut a first visit to a page in order.1. You type an address and press Enter 2. The browser sends a request to the server 3. The server answers with HTML 4. The browser asks for the CSS and images the HTML links to 5. The page appears on screenNothing can be drawn before the HTML arrives, because the HTML is what tells the browser which other files exist. That is why a page with small, ready-made HTML appears quickly.
  3. 1. How a website reaches you · Static pages are made ahead; dynamic pages are made per visit · QuizWhich page needs to be dynamic (made per visit)?C. A page that greets the signed-in reader by nameThe greeting depends on who is asking, which is only known when the request arrives. A list of posts changes only when you publish, so it can be made again then, ahead of time, and still be static.
  4. 1. How a website reaches you · Every bit of JavaScript costs the visitor time · QuizWhich parts of a recipe page need JavaScript? Pick all that apply.B. A button that doubles every quantity in place; D. A search box that filters recipes as you typeThe ingredients and the link work with HTML alone: a link is just an <a> element, which browsers follow without any script. Changing quantities in place and filtering as you type both react to the reader, after the page has loaded, so they need JavaScript.
  5. 2. Tools, frameworks and the build step · Check: which tool does what · QuizYou want to add a package called date-fns to your project. What do you use?B. npm, by running npm install date-fnsnpm is the tool that downloads packages and records them in package.json. Node runs JavaScript files, but node date-fns would look for a file to run, not install anything.
  6. 2. Tools, frameworks and the build step · Check: which tool does what · QuizYou open a terminal and type npm run dev, but it says it can’t find package.json. What is the likely cause?B. The terminal is in the wrong folder: cd into the project firstnpm looks for package.json in the folder the terminal is in (and the folders above it). pwd shows where you are and cd moves you. A missing internet connection would fail differently, and only when downloading.
  7. 2. Tools, frameworks and the build step · While you work, a dev server rebuilds as you save · QuizYou want to publish your blog to a hosting service. What do you give it?B. The dist/ folder written by astro buildBrowsers can’t read .astro files: the build turns them into HTML. dist/ is the finished site. The dev server runs on your own computer and stops when you close it. (Many hosts run the build for you from your source, but what they serve is still the build’s output.)
  8. 2. Tools, frameworks and the build step · The same page, two ways · QuizA reader on a slow phone opens a recipe. With which approach do they see the text sooner?B. The Astro page: the text is already in the HTML that arrives firstThe browser can draw HTML as it arrives. In a single-page app the text exists only after a script is downloaded and run, which is slowest exactly on slow phones. Same text, different route to the screen.
  9. 3. Your first Astro project · Each folder has one job · QuizYou want a new page at /contact. Where do you create it?B. src/pages/contact.astroOnly files in src/pages/ become pages, and their file name becomes the path. A file in public/ would be copied as is, without any of Astro’s help, and a file loose in src/ is not a page at all.
  10. 3. Your first Astro project · package.json lists the commands you will run · Fill the blanksFill in the commands: start writing, then make the finished site, then look at it.1: dev, 2: build, 3: previewdev is for writing: it rebuilds as you save. build writes the finished site into dist/. preview serves that dist/ folder on your computer, so you can check exactly what visitors will get.
  11. 3. Your first Astro project · A new file in src/pages is a new address · QuizYou create src/pages/recipes.astro. What address shows it?B. /recipesThe path is the file’s place inside src/pages/, without the extension. src/pages/ itself is the root of the site, so recipes.astro becomes /recipes, and index.astro becomes /.
  12. 3. Your first Astro project · Build writes the finished site into dist · QuizYou fix a typo directly in dist/index.html, then run npm run build again. What happens to the fix?B. It is lost: the build rewrites dist/ from your sourcedist/ is output: the build makes it again from src/ and public/ every time. Always fix the source (src/pages/index.astro), then build.
  13. 3. Your first Astro project · Read an error from the top: what, where, which line · Find the bugThis page fails to build with a syntax error. Which line is wrong?Line 2: const title = 'Soups'A string that opens with ' must close with one on the same line. Line 2 never closes it, and a quoted string can’t carry on into the next line, so the parser stops there. In general an error names a problem, not always its exact spot: look at the line it points to and the one just before.
  14. 4. Inside an .astro file · The script runs while the page is built, never in the browser · QuizWhere does the console.log message appear?B. In the terminal running astro dev or astro buildThe script runs in Node, on your computer, while the page is made. Its console is the terminal. The browser only ever receives the finished HTML, so builtAt is the build’s date for every visitor, not today’s date when they visit.
  15. 4. Inside an .astro file · Curly braces put values into the HTML · Fill the blanksShow the recipe’s name in the heading, and use its photo as the image source.1: {recipe.name}, 2: {recipe.photo}Braces switch from HTML to JavaScript. In text, {recipe.name} is replaced by its value. In an attribute, braces replace the quotes entirely: src="{recipe.photo}" would be the literal text {recipe.photo}.
  16. 4. Inside an .astro file · Show something only sometimes · QuizWhat does this render?A. <p>Quick</p>15 < 20 is true, so the ternary gives <p>Quick</p>. 15 > 60 is false, so && stops there and gives false, which Astro renders as nothing (not the word "false").
  17. 4. Inside an .astro file · Turn an array into a list with map · Find the bugThe page shows an empty list. Which line is wrong?Line 5: {steps.map((step) => <li>{step}</li>)}With { } after the arrow, the function has a body and returns nothing unless it says return. So map collects three undefined values, which render as nothing. Drop the braces (or write return <li>…</li>) to return the element.
  18. 4. Inside an .astro file · The script can wait for data · QuizThe tips file changes after you build the site. What do visitors see?B. The tips as they were at build time, until you build againThe fetch ran once, in the build, and its result became part of the HTML file. To pick up new data, build again (many hosts can rebuild on a schedule), or render the page per request, which module 13 covers.
  19. 5. Components and props · Import a component, then use it like a tag · QuizFrom src/pages/recipes/index.astro, which import finds src/components/Header.astro?B. import Header from '../../components/Header.astro'The page is two folders below src/ (src/pages/recipes/), so it goes up twice with ../../, reaching src/, then down into components/. One ../ only reaches src/pages/.
  20. 5. Components and props · An interface says which props a component takes · Fill the blanksType the props of a tag link: a required label and an optional count, defaulting to 0.1: Props, 2: string, 3: ?, 4: = 0, 5: Astro.propsAstro looks for an interface called exactly Props. ? marks count optional in the type, and = 0 supplies the value when it is missing. Both are needed: without the default, count would be undefined and show as nothing.
  21. 5. Components and props · Components and map make lists of cards · QuizA fourth recipe is added to the array. What else must change for it to appear?B. Nothing: map makes one card per itemThe template describes one card and map repeats it for each item, however many there are. The component and its props stay the same because each recipe has the same shape.
  22. 5. Components and props · Astro components render once, to HTML · QuizWhat happens with this Astro component when a reader clicks the button?B. Nothing useful: the script ran at build time and isn’t in the pageThe script ran once, on the server, and likes stayed 0. A function can’t be turned into HTML, so the click does nothing useful. For behaviour you add a browser <script> (module 7) or an island (module 12).
  23. 5. Components and props · Pass through the attributes you don’t name · Find the bugCallers can’t add their own aria-label: it never appears. Which line is wrong?Line 8: <a href={href} class="button" {...rest}>Read more</a>The props are typed, so the editor accepts aria-label, and line 6 collects it into rest. But line 8 never puts rest on the element, so it is dropped. Spread it on the <a> with {...rest}.
  24. 6. Slots: components that wrap other things · Check: where do the children go · QuizWhat HTML does this produce?A. <aside class="note"><strong>Note:</strong> Use cold butter.</aside>Components disappear from the output: only their HTML remains, and the children land exactly where <slot /> was. The text does not end up after the component, and it does not need a prop: anything between the tags is the default slot.
  25. 6. Slots: components that wrap other things · Practise: fill a named slot · Fill the blanksThe layout has a sidebar slot. Put the list of tags in it, and the post in the default slot.1: name, 2: slotThe component names the hole with name, the parent targets it with slot. They are different attributes on purpose: name on a <slot> declares, slot on a child chooses. The <article> has no slot attribute, so it goes to the default slot inside <main>.
  26. 6. Slots: components that wrap other things · Astro.slots.has asks if a slot was filled · Find the bugThe footer never shows, even when the page passes one. Which line is wrong?Line 2: const hasFooter = Astro.slots.has('footer')Slot names are compared exactly, and upper and lower case count. "Footer" is a different slot from "footer", which nobody fills, so has returns false every time. The template line is fine: it only runs when hasFooter is true.
  27. 6. Slots: components that wrap other things · Check: props or a slot · QuizWhich of these are better as a slot than as a prop?B. The body of a warning box, which may contain links and bold text; D. The whole content of a page, inside a page frame componentRich markup belongs in slots: it can contain any elements and components, and the parent writes it naturally. A number or a URL is a single value: a prop is clearer and can be typed in interface Props. Passing HTML as a string prop would also need set:html, which is unsafe with untrusted text.
  28. 7. Styles and small scripts · Check: what a scoped rule reaches · QuizRecipeCard.astro has <style> p { color: gray; } </style>. The page around it also has paragraphs. Which ones turn gray?B. Only the <p> elements written in RecipeCard.astro’s own templateScoping limits the rule to the elements the component itself writes. The page’s paragraphs don’t carry the card’s marker, so they are untouched. Plain element selectors are fine in scoped styles: that is the point of scoping.
  29. 7. Styles and small scripts · class:list builds a class from conditions · Fill the blanksGive the recipe card the class quick only when it takes under 30 minutes.1: class:list, 2: <class:list keeps the string card and adds quick when its value is truthy. A plain class attribute would print the array as text. In an object, the key is the class name and the value is the condition.
  30. 7. Styles and small scripts · is:inline leaves a script exactly as written · Find the bugThe page shows three CopyLink buttons, but only the first one works. Which line is the bug?Line 3: for (const button of document.querySelectorAll('.copy-link'))A processed script runs once per page, not once per component. querySelector returns only the first match, so only the first button gets a listener. Loop over querySelectorAll (or use a custom element) so every copy is set up. Making it is:inline would also “work”, but would repeat the code three times.
  31. 8. Pages, links and routes · Links are plain <a> tags · QuizYou want a page at /recipes/breakfast. Which files would answer it?A. src/pages/recipes/breakfast.astro; B. src/pages/recipes/breakfast/index.astroBoth files inside src/pages/ map to /recipes/breakfast: a file named after the address, or an index.astro in a folder of that name. Have only one of them, or Astro complains about a conflict. Files outside src/pages/ are never routes, and public/ is copied as is, not rendered.
  32. 8. Pages, links and routes · Practise: tags pages · Fill the blanksBuild one page per tag at /tags/[tag], each with the recipes that have that tag.1: getStaticPaths, 2: params, 3: params, 4: propsA Set keeps each tag once, so there is one page per tag, not one per use. The key tag in params must match the [tag] in the file name. Astro.params reads the address part; Astro.props reads what you passed. Mixing them up (Astro.props.tag) gives undefined.
  33. 8. Pages, links and routes · When two routes match, the more specific one wins · Put in orderPut these files in the order Astro tries them for /recipes/new, first to last.1. src/pages/recipes/new.astro 2. src/pages/recipes/[slug].astro 3. src/pages/recipes/[...path].astro 4. src/pages/[...all].astroA file with the exact name is most specific, so new.astro wins and [slug] never sees new. A named parameter matches exactly one piece; a rest parameter can match anything, so it comes after. A rest parameter at the top level is the last resort of all, which makes it a good catch-all.
  34. 8. Pages, links and routes · Not found pages and redirects · Find the bugThe build fails with a missing parameter error for src/pages/blog/[slug].astro. Which line is wrong?Line 5: params: { slug: post.slug },The keys in params must match the names in brackets in the file name. The file is [slug].astro, so Astro expects params.slug and finds only id. Renaming the file to [id].astro would fix it too: the name in brackets is what counts.
  35. 9. Layouts and the page head · A page wraps itself in the layout · QuizA page uses <BaseLayout title="Recipes">, but its <h1> and list never appear. What is the most likely cause?B. The layout has no <slot />Children only appear where the component places a slot (module 6). Without <slot /> in the layout, everything between <BaseLayout> and </BaseLayout> is dropped, while the frame still renders. Layouts can live anywhere and take any props.
  36. 9. Layouts and the page head · Mark the current page in the navigation · Find the bugOn /recipes/soup/, the Recipes link should be highlighted too, but it isn’t. Which line should change?Line 3: const isActive = (href: string) => path.startsWith(href)An exact comparison only matches the section’s own page. startsWith also matches pages inside it, like /recipes/soup/. Keep an exact match for the home link, though: every address starts with /. Strictly, aria-current="page" should only be on the exact page; for the section, a class is the more accurate choice.
  37. 9. Layouts and the page head · Layouts can wrap layouts · Fill the blanksMake a PostLayout that sits inside BaseLayout, passes the title on, and places the post.1: BaseLayout, 2: title, 3: slotThe inner layout uses the outer one as a component and gives it the props it needs. Its own <slot /> is where the page’s content goes; that slot is itself inside BaseLayout’s slot. Forgetting to pass title on would leave the browser tab without a page title.
  38. 10. Markdown and content collections · Zod in five minutes · QuizA recipe has servings: four in its frontmatter. With the schema from the last slide, what happens?B. The build stops with an error naming the file and the servings fieldYAML reads four as text, and z.number() rejects text, so Astro reports the bad entry when it loads the collection, in astro dev and in astro build. That is the point of a schema: a broken file fails early, loudly and at its source, not as a strange page later.
  39. 10. Markdown and content collections · Practise: list the quick recipes · Fill the blanksList the published recipes tagged quick, sorted by title.1: getCollection, 2: data, 3: includesThe filter receives each entry and keeps it when it returns true. Frontmatter lives under data, not on the entry itself: entry.tags would be undefined. includes checks whether the array has the tag; localeCompare sorts text alphabetically.
  40. 10. Markdown and content collections · One page per entry, with render · Find the bugThis page comes from an old tutorial and fails in Astro 7. Which line?Line 5: const { Content } = await render(recipe) // import renderEntries no longer have a render() method: import render from astro:content and call render(recipe). getEntry(collection, id) is current, and so is checking for undefined, since an id may not exist.
  41. 10. Markdown and content collections · Data files and references between collections · QuizWhat is post.data.author after loading the collection?B. A pointer like { collection: "authors", id: "sam" }A reference is checked (an unknown id is reported as an error when the content loads) but not filled in, so entries stay small and an author is loaded only where it is shown. Pass the pointer to getEntry to get the entry. It is not plain text either: it records the collection too, and Astro has checked it points somewhere.
  42. 11. Images, fonts and other files · The Image component does the work for you · QuizWhy does Astro write width and height on the <img> even though CSS may resize it?B. So the browser can reserve the right space before the file arrives, and nothing jumpsWith both numbers the browser knows the picture’s proportions and keeps a box of the right shape while it downloads. Without them the box is empty, then pops open, pushing the text down: a layout shift. They don’t change the file’s size; the resizing does that.
  43. 11. Images, fonts and other files · Picture offers several formats; the browser picks one · Find the bugThis card stops the build with an image error. Which line is wrong?Line 5: <Image src={pancakes} alt="Pancakes"A string src must be an address on the web: a full URL, or a path in public/ starting with /. ../assets/pancakes.jpg is a path in your project, so Astro stops with “Local images must be imported”. Pass the imported object: src={pancakes}. The import is also what lets Astro find, check and optimise the file.
  44. 11. Images, fonts and other files · Images from other sites must be allowed first · QuizWhy must you list the hosts whose images Astro may optimise?B. So nobody can use your site to resize and serve any image from anywhere, at your costOn a site rendered on demand, the image service takes an address and returns a resized file. Without a list, anyone could point it at any image on the internet and use your server as a free image converter. image.remotePatterns does the same with patterns (protocol, hostname with wildcards, path).
  45. 11. Images, fonts and other files · Images in content collections are checked too · Fill the blanksMake cover a checked image in the recipes collection.1: image, 2: image()The schema becomes a function that receives helpers; image() turns a relative path like cover: ./pancakes.jpg into image metadata. A misspelt file name fails the build, instead of shipping a broken picture. Keep the description (coverAlt) beside it so every cover has its alt.
  46. 11. Images, fonts and other files · Other files: ?url and ?raw · QuizSam wants /recipes.pdf to keep that exact address forever, because it is printed on flyers. Where does it go?B. public/recipes.pdfFiles in public/ are served at their own path, untouched. A ?url import gets a hashed name like /_astro/recipes.D8f2a.pdf that changes when the file does: perfect for caching, wrong for a printed address. src/pages/ is for routes, not documents.
  47. 12. Islands and client directives · By default, Astro sends no JavaScript · QuizWhich parts of a recipe page need to run in the browser?B. A timer that counts down while you cook; D. A box that scales the ingredients as you change the number of servingsThe timer and the servings box change what is on screen in response to the visitor, after the page has arrived: that needs code in the browser. A list and a link are just HTML; the browser handles a link click by itself.
  48. 12. Islands and client directives · A client directive turns a component into an island · QuizSam renders <Search recipes={list} /> with no directive. What does a visitor get?B. The search box and the full list as HTML, but typing filters nothingWithout a directive, Astro still renders the React component to HTML on the server and sends no JavaScript for it. That is useful for components that only display things. A search box needs to react to typing, so it needs a directive.
  49. 12. Islands and client directives · Check: choose the directive · QuizA likes counter sits at the very bottom of long recipe pages. Most visitors never scroll that far. Which directive?C. client:visibleWith client:visible its JavaScript is fetched only when it comes into view, so most visitors never download it. client:idle would still download it for everyone, just a little later. client:only would also skip the server HTML, leaving an empty gap until it loads.
  50. 12. Islands and client directives · Check: choose the directive · QuizWhat do you lose with client:only="react"?B. The server-rendered HTML: the island is empty until its JavaScript runsThe other directives send HTML first, so the island shows at once. client:only skips the server render, so there is a blank space (or a fallback you provide with slot="fallback") until the code loads. Use it only when the component truly can’t run on a server, for example a map library that reads window as soon as it is imported. The framework name is needed because Astro can’t guess it without rendering.
  51. 12. Islands and client directives · Props cross from server to browser as data · Find the bugThis island breaks. Which line causes it?Line 5: <Save client:visible slug={slug} />The Astro page only exists on the server, so a function created there can’t be sent to the browser: it is left out of the serialised props, and the island receives onSaved as undefined. Move the behaviour into the island itself: Save can log or show a message when it finishes. Pass islands data, not callbacks.
  52. 13. Rendering modes, adapters and deploying · The choice is made per route · QuizWhich pages of Sam’s blog need on-demand rendering?C. A page listing the recipes the current visitor saved, from their cookieOnly the saved-recipes page depends on who is asking: its cookie arrives with each request. The others are the same for everyone and only change when Sam edits content, so they are prerendered, and the next build updates them. Tags pages come from getStaticPaths, which also runs at build time.
  53. 13. Rendering modes, adapters and deploying · Check: personal data on a static page · QuizThe prerendered home page shows Hello, {name}, with name read from Astro.cookies. What do visitors see once it is deployed?B. Everyone sees the same HTML, made at build time when there was no cookieThe page was rendered once, during the build, with no request and so no cookies, and that one file is served to everybody. It never becomes personal. Make the route on demand with prerender = false, or read the value in the browser from an island.
  54. 13. Rendering modes, adapters and deploying · Check: what a hybrid build produces · Put in orderA visitor opens /saved on a static-default site with the Node adapter. Put what happens in order.1. The browser sends a request for /saved, with its cookies 2. The server finds no prerendered file for /saved 3. It runs the saved.astro route’s code with the real request 4. The route reads the cookie and renders HTML 5. The server sends that HTML backPrerendered pages are answered straight from files and never reach your code. /saved exported prerender = false, so the build left it as code in the server, which runs it for every request. That is why reading cookies works there and not on static pages.
  55. 13. Rendering modes, adapters and deploying · Deploying: a folder of files, or a server · QuizA prerendered page shows the time it was rendered. Under astro dev the time changes on every refresh. What do visitors see once it is deployed?B. The time of the build, the same for everyone until the next buildIn development each page is rendered again whenever you open it, so its code runs on every refresh. The build runs it once and saves the HTML. That difference hides bugs, which is why astro preview (or a staging deploy) belongs in your routine. Cookies are not one of those bugs: the dev server already leaves them out of prerendered pages, and warns you.
  56. 14. Endpoints, middleware and environment variables · Prerendered endpoints become files · QuizOn a fully static site with no adapter, Sam adds export const POST to likes.ts. What happens when a visitor sends a POST?B. Nothing runs: the host only serves the files the build made, and POST was never part of themA static build calls only GET, once, to make a file. There is no server afterwards to run code when a request arrives, so a POST has nothing to reach. Handling POST needs the route on demand (prerender = false) and an adapter.
  57. 14. Endpoints, middleware and environment variables · locals carry information to the route · Fill the blanksIn an on-demand recipe page, read the visitor’s units.1: Astro.localsPages reach locals through the Astro global; endpoints and middleware get it on the context argument. It belongs to this request only, so one visitor’s units never leak to another.
  58. 14. Endpoints, middleware and environment variables · On static pages, middleware runs at build time · QuizMiddleware sends visitors without a member cookie away from /members/. The members pages are prerendered. What happens in production?B. The check ran once at build time; the files are served to anyone who asksAt build time there was no cookie, so the middleware either redirected the build itself or let it through, and afterwards nothing runs when the file is fetched. A check on each visit needs those routes on demand (prerender = false), so the middleware runs for every request.
  59. 14. Endpoints, middleware and environment variables · Check: where a secret may go · Find the bugThe comments island must never expose the token. Which line is the problem?Line 2: // No import: the endpoint adds the token on the serverIsland code is sent to the browser, where anyone can read it. Astro refuses astro:env/server there. The design is already right otherwise: the island calls your own endpoint, /api/comments, which runs on the server, adds the token, and calls the comments service.
  60. 15. Forms and actions · Check: what a form sends · Find the bugThe server always receives an empty email. Which line is wrong?Line 3: <input id="email" name="email" type="email" required />Only inputs with a name are included in the form data. id links the label to the input; it isn’t sent. Add name="email" and the server gets the address under the key email.
  61. 15. Forms and actions · Check: what a form sends · QuizThe input has type="email" and required. Does the server still need to check the email?B. Yes: anyone can send a request without your form, with any dataBrowser checks are a kindness to visitors: quick feedback. They are not protection, because a request can be made by any program, skipping the form entirely. The server must validate everything it receives.
  62. 15. Forms and actions · Point a form at the action · Fill the blanksRead the result of the subscribe action in the page.1: getActionResult, 2: subscribegetActionResult takes the action itself (not a string), so the result is typed: result.data.email is known to be a string. It is undefined when the page was opened normally, without a form post.
  63. 15. Forms and actions · Throw ActionError for problems the visitor should hear about · QuizWhich result does a visitor get when they submit email=hello (no @)?B. result.error is an input error, and fields.email says why; the handler never runsThe schema is checked before the handler. z.email() fails, so Astro returns an input error with a message per field, and your code isn’t called with bad data. That is the main gift of actions: the handler can assume its input is valid.
  64. 15. Forms and actions · Progressive enhancement: works first, then better · Put in orderPut a no-JavaScript action form submission in order.1. The visitor presses Subscribe 2. The browser posts the form data to the page 3. Astro checks the data against the action’s schema 4. The handler runs with valid input 5. The page renders, reading getActionResult 6. The browser shows the new page with the messageThe browser does the sending; Astro validates before your handler; the page renders once the action has finished, so getActionResult already has the result. If validation fails, step 4 is skipped and the page renders with the input error instead.
  65. 16. Languages and view transitions · Tell Astro which locales exist · QuizWith the config above, where does the English about page live?B. /about/With prefixDefaultLocale: false, the default language keeps the plain addresses and only the others get a prefix. /en/about/ would only exist with prefixDefaultLocale: true. Astro never makes a page appear in two places on its own: every address still comes from a file.
  66. 16. Languages and view transitions · You still write the pages for each language · Fill the blanksComplete the Spanish about page so it reuses the shared component.1: es, 2: esThe folder name must match the locale code in locales exactly, so the page is src/pages/es/about.astro and its address is /es/about/. The component gets the locale as a prop and picks its words with it.
  67. 16. Languages and view transitions · Build every link from the config · Find the bugThe config has prefixDefaultLocale: false. One link is broken. Which line?Line 5: <a href={getRelativeLocaleUrl('en', 'recipes/')}>Recipes</a>English lives at the plain addresses here, so /en/recipes/ is a 404. Typing addresses by hand bakes in today’s routing rules; the helper asks the config every time.
  68. 16. Languages and view transitions · Guessing the visitor’s language needs a request · QuizA prerendered home page redirects to /es/ when Astro.preferredLocale === "es". What happens for a visitor whose browser prefers Spanish?B. Nothing: the page was built with no request, so preferredLocale was undefined and no redirect was writtenPrerendered code runs once, during the build, for nobody in particular. To act on Accept-Language, render that one route on demand, or let the visitor choose with a language picker (which is also kinder: people often read a language their browser doesn’t list first).
  69. 16. Languages and view transitions · The ClientRouter swaps pages without a full load · Put in orderPut a ClientRouter navigation in order.1. The visitor clicks a link to another recipe 2. The router fetches the new page’s HTML 3. The old and new pages are swapped, animated by named elements 4. astro:after-swap fires, then the new page’s astro:page-loadThe router intercepts the click, so the browser never does a full load. It fetches, swaps (running the animation), then fires its events: astro:after-swap right after the swap, astro:page-load once the page is ready. Scripts that only run at load would miss every page after the first.
  70. 16. Languages and view transitions · Choosing between CSS and the router · QuizSam wants recipe photos to morph from the list into the recipe page, and the site has a strict CSP. What fits best?B. @view-transition { navigation: auto; } plus a matching view-transition-name on both pagesThe morph needs only paired names, which native transitions support. The router would also do it, but it isn’t supported with Astro’s CSP and adds JavaScript for nothing here. Reach for the router when something must survive navigation, like audio that keeps playing.
  71. 17. Performance and caching · The fastest JavaScript is the JavaScript you don’t send · QuizEvery island on the recipe page uses client:load. The page feels slow to respond on a cheap phone. What is the best first change?B. Give each island the latest directive that still works, and drop the islands that only show static contentclient:load hydrates everything at once, as the page loads, so the phone is busy exactly when the visitor first taps. Deferring with idle and visible, and removing islands that don’t need to move, cuts that work. Changing framework is a big job for a smaller win; on-demand rendering changes where HTML is made, not how much JavaScript runs.
  72. 17. Performance and caching · Images and fonts decide LCP and CLS · Find the bugVisitors see the recipe text jump down as the page loads. Which line causes it?Line 6: <img src="/photos/lemon-cake.jpg" alt="A lemon cake" width="1200" height="800" />A plain <img> from public/ has no width or height, so the browser gives it no space until the file loads, then pushes everything below it down: layout shift (CLS). <Image> with an imported file knows the size and writes both attributes. If you must use <img>, write the dimensions yourself.
  73. 17. Performance and caching · How CSS and HTML reach the browser · QuizA blog with 200 posts shares one 60 kB stylesheet. Someone sets inlineStylesheets: "always". What happens as a reader moves from post to post?B. Each page’s HTML carries the 60 kB again, instead of reusing one cached fileInlined CSS is part of each HTML page, and HTML is not cached for long, so every page repeats it. A separate file with a hashed name is downloaded once and reused. Inlining wins for small CSS on the first visit; auto makes that call per stylesheet.
  74. 17. Performance and caching · Prefetch the page they are about to open · Fill the blanksPrefetch the next recipe as soon as its link is visible.1: prefetch, 2: viewportviewport suits the one obvious next step. Prefetching spends the visitor’s data, so don’t put load or viewport on every link in a long list: hover waits for a sign of intent.
  75. 17. Performance and caching · Astro 7 caches on-demand pages for you · QuizWith maxAge: 300, swr: 60, a visitor asks for a recipe 320 seconds after it was cached. What do they get?B. The cached copy straight away, while a fresh one is rendered in the backgroundAfter 300 s the copy is stale, but for 60 s more it may still be served while the server renders a new one behind the scenes. That visitor doesn’t wait; the next one gets the fresh page. Past 360 s, a request waits for a new render.
  76. 17. Performance and caching · Purge by tag when content changes, and never cache what’s personal · Find the bugThis page leaks one reader’s saved recipes to others. Which line?Line 5: Astro.cache.set(false)The cache keys on the address, and every reader asks for the same one. The first reader’s list is cached for an hour and shown to everyone after them. Personal responses opt out with set(false). Shortening maxAge only shrinks the leak.
  77. 18. Security and the Content Security Policy · Expressions are escaped; set:html is not · Find the bugReaders can post comments. Which line lets one reader run a script on everyone else’s screen?Line 8: <p>{c.body}</p>The name is escaped, so it’s fine; the body is written as raw HTML, so <img src=x onerror=…> in a comment runs in every reader’s browser. Render it as an expression, or sanitise it before using set:html. A Content Security Policy (later) is the safety net, not the fix.
  78. 18. Security and the Content Security Policy · Secrets stay on the server · QuizWhich of these puts the mail key where readers can see it?B. Passing MAIL_API_KEY as a prop to a client:visible islandAn island’s props are serialised into the HTML so the browser can hydrate it (module 12): the key would be in the page source. Actions, endpoints and middleware run on the server and only send back what you return. Let the island call an action that uses the key, rather than holding the key.
  79. 18. Security and the Content Security Policy · Astro checks where form posts come from · QuizDoes checkOrigin protect a prerendered page?B. No: a prerendered page is a file the host serves, and no Astro code runs for that requestThe check runs in Astro’s server, so it only covers routes rendered on demand. A static page can’t receive a form post anyway: forms should post to an action or endpoint, which is on demand and therefore checked.
  80. 18. Security and the Content Security Policy · Astro hashes every script and style it builds · QuizWhy does a hash-based policy suit a prerendered blog better than nonces?B. A nonce must change for every response, which needs a server per request; a hash depends only on the content, so it can be written into the file onceA nonce only protects you if it is unpredictable, so the HTML must be made fresh each time. A script’s hash is the same for every visitor, so a static file can carry its own policy and still be cached by a CDN.
  81. 18. Security and the Content Security Policy · You write the other directives · QuizA new island shows the weather from https://weather.example.org. It works in astro dev. What happens in the built site?B. The browser blocks the fetch under connect-src, and the island shows its error stateThe dev server has no CSP, so the problem hides there. In the build, connect-src lists only the site and the search service. Add the host deliberately, or fetch the weather on the server (an endpoint) so the browser only talks to your own site.
  82. 18. Security and the Content Security Policy · Scripts Astro can’t see need your hash · Fill the blanksHash the inline script and add it to this page’s policy.1: sha256, 2: base64, 3: insertScriptHashA CSP hash is the base64 of the digest, prefixed with the algorithm (sha256-). A hex digest looks similar but never matches, so the script is refused.
  83. 18. Security and the Content Security Policy · What a meta policy can’t do, and what it rules out · Find the bugThis config is meant to stop other sites framing the blog. Which line does nothing?Line 5Astro’s policy is a <meta> tag, and browsers ignore frame-ancestors there, so the page can still be framed. Send it as a header instead.
  84. 19. Integrations, the build pipeline and architecture · What astro build does, in order · Put in orderPut these build steps in order.1. Integrations’ astro:config:setup hooks run 2. Content collections are loaded and types generated 3. Client JavaScript and CSS are bundled with hashed names 4. Static pages are rendered to HTML 5. astro:build:done runs with the list of pagesConfig comes first because it decides everything else (integrations can still change it in setup). Content must be loaded before pages can query it. Pages are rendered once bundles exist, so the HTML can point at their hashed names. astro:build:done is last: the files are on disk.
  85. 19. Integrations, the build pipeline and architecture · Astro is built on Vite · QuizA pull request passes astro build but a prop is passed with the wrong type in an .astro file. Which command in CI would have caught it?B. astro checkastro build doesn’t type-check: it transforms code and renders pages, and a wrong type often still renders. astro check runs the TypeScript checker over .astro files too. sync only generates types; it doesn’t report misuse.
  86. 19. Integrations, the build pipeline and architecture · Writing a small integration · Fill the blanksUse the integration in the config.1: integrations, 2: pageList()Integrations go in the integrations array, called so they return their object. Order matters when two integrations touch the same thing: they run in the order listed.
  87. 19. Integrations, the build pipeline and architecture · Testing components with the container API · QuizWhat is the container API good for?B. Rendering a component with chosen props and slots in a test, and checking the HTMLIt is about isolation: one component, chosen inputs, the HTML it makes. Nothing to do with Docker containers despite the name. For the whole page in a real browser, use an end-to-end tool such as Playwright against astro preview.
  88. 19. Integrations, the build pipeline and architecture · Architecture: decide where each part runs · QuizSam adds comments to recipes. New comments should appear within a minute. Which design fits best?B. Keep the recipe static and load comments in a server island or a small endpoint, cached for about a minuteThe recipe itself rarely changes, so it stays a fast static file. Only the comments need freshness, and a short shared cache is fine because they are the same for every reader. A full rebuild per comment is slow; uncached on-demand pages pay for the server on every view; build-time props are stale until the next build.
  89. 20. Mastery challenge · How this challenge works · QuizIn a prerendered recipe page, which of these run in the reader’s browser?B. B: the search island’s component code; D. D: the <script>Frontmatter runs at build time only. A framework component without a directive is rendered to HTML on the server and sends no JavaScript, so C never runs in the browser. B hydrates there once the browser is idle, and a <script> tag is bundled and run by the browser.
  90. 20. Mastery challenge · Review: a dynamic route · Find the bugThe build stops on the first recipe page. Which line is wrong?Line 12: const { recipe } = Astro.propsWhatever getStaticPaths puts in props arrives in Astro.props under the same name. It sends recipe, the page reads post, which is undefined, so render(post) throws. With an interface Props declared (module 5), TypeScript and astro check would have flagged it before the build.
  91. 20. Mastery challenge · Review: a dynamic route · QuizSam adds src/content/recipes/plum-tart.md to the deployed, fully static site. When does /recipes/plum-tart/ exist?B. After the next build and deployA static site is the files made by the last build. getStaticPaths ran then, and plum tart wasn’t there. The dev server watches files; production doesn’t. Making the route on demand would also work, but for content that changes when Sam publishes, a rebuild is the simple answer.
  92. 20. Mastery challenge · Predict: an island’s props · QuizWhat happens with this island?B. Astro can’t serialise the function prop, so this doesn’t work as writtenIsland props are written into the HTML as data and read back in the browser. Functions can’t be written that way, whatever the directive or syntax. Build the URL inside the island, or pass ready-made URLs. Also look at recipes: the whole entries, bodies included, end up in the page source. Pass only title and id.
  93. 20. Mastery challenge · Predict: an island’s props · Fill the blanksPass the island only what it needs.1: data, 2: id, 3: idleSchema fields live under data; the entry’s id comes from the file name. Small props mean a smaller page and nothing private by accident. idle suits a search box in the header; visible would do too if it sat lower down.
  94. 20. Mastery challenge · Choose: which routes need a server · QuizThe site has the default output: "static" and an adapter. Which routes should export prerender = false?B. A page greeting a signed-in reader by name; D. An endpoint that receives the CMS’s webhookA greeting depends on the visitor’s cookie, and a webhook is a POST that must run when it arrives: both need a request. The about page, tag pages and the RSS feed are the same for everyone and change only when content does, so they stay files. The adapter only makes on-demand rendering possible; prerender decides.
  95. 20. Mastery challenge · Choose: which routes need a server · Put in orderA reader signs up for the newsletter on an on-demand page. Put the steps in order.1. The browser posts the form to the page 2. Astro checks the request’s origin (checkOrigin) 3. Middleware runs and fills locals 4. The action validates the input and runs its handler 5. The page renders with Astro.getActionResult()The origin check comes before any of your code, so a cross-site post is rejected with 403 early. Middleware wraps everything that follows. A form action runs before the page renders, so the page can show the result or the errors.
  96. 20. Mastery challenge · Complete: middleware for the members area · Fill the blanksSend signed-out readers away from /members/, and let everyone else through.1: defineMiddleware, 2: locals, 3: redirect, 4: nextdefineMiddleware types the function. locals carries the user to pages for this one request. Returning a response (the redirect) skips the page; next() renders it. Remember that the members pages must be on demand: for prerendered pages, middleware runs once at build time, with no reader.
  97. 20. Mastery challenge · Complete: middleware for the members area · QuizThe middleware above is in place, but /members/ was left prerendered. What does a signed-out reader get in production?B. The members page: it was built once, with no user, and is served as a file without running middlewareAt build time there is no cookie, so user was empty and the redirect happened while building, or the page was built as if public, depending on the code. Either way, later requests for the file never reach the middleware. Protection needs a request to protect: make the route on demand.
  98. 20. Mastery challenge · Find the bug: the newsletter action · Find the bugThe form posts with method="POST" action={actions.subscribe}, and every submission comes back with an error. Which line?Line 6: subscribe: defineAction({ accept: 'form',An action accepts JSON by default. An HTML form sends form data, so Astro rejects every submission (UNSUPPORTED_MEDIA_TYPE: the action only accepts JSON) before the schema is even checked. accept: "form" tells the action to parse FormData and check it against input. The secret is fine: it comes from astro:env/server and only runs in the handler.
  99. 20. Mastery challenge · Review a pull request · QuizWhich review comments are right?A. set:html on reader content is an XSS hole: render it as {tip.body} or sanitise it; B. client:visible would delay the island’s JavaScript until readers reach the tips; C. Caching a page that shows a reader’s own saved tips would show them to other readers; D. frame-ancestors is ignored in Astro’s <meta> policy: send it as a headerThe first four are each real. The personal part should move to a server island or an island that fetches it, so the shared page can stay cached. The last is wrong: on-demand routes can query collections like any other route.
  100. 20. Mastery challenge · Design: a recipe site that grows · QuizWhich plan fits all four wishes?B. Static recipes in en and es folders with i18n routing, a server island (or cached on-demand endpoint) for the seasonal box, native @view-transition CSS, and security.cspRecipes are the same for everyone, so they stay static in both languages. Only the seasonal box needs freshness, so only it touches a server, with a cache. The router isn’t supported with Astro’s CSP and 'unsafe-inline' would gut the policy, so animations come from native CSS. preferredLocale guesses a language; it doesn’t translate anything.
  101. 21. Final project: a conference website · Step 2: mark the current page · QuizSo that a page inside a section marks it too, you compare with here.startsWith(trim(href)) instead of ===. What does the nav show on /about/?B. Home and About, underlinedHome’s href is /, and every address starts with /, so Home now matches every page. About matches too, and Schedule doesn’t. startsWith suits a section like module 9’s recipes, but Home needs an exact match.
  102. 21. Final project: a conference website · Step 4: the schema checks every file · QuizWithout .regex(…), the file with start: 9:30 would load. Where would the opening keynote land on a schedule sorted by start?B. Last, after the closing keynote at 16:00The times are text, and text is compared a character at a time: "9" comes after "1", so "9:30" sorts after "16:00" (and after "13:30"). With two digits for the hour, text order and time order agree, and that’s what the pattern makes sure of. YAML does read 9:30: the error says “Invalid string”, so it was text that failed the pattern.
  103. 21. Final project: a conference website · Step 5: only the listed pages exist · Find the bugThis comes from a tutorial for an older Astro. In Astro 7, every talk page fails with Missing parameter: id. Which line is wrong?Line 7: params: { id: talk.id },Entries of a collection made by a loader have an id, and no slug: that was the old collections’ field. talk.slug is undefined, so no page gets an id, and the error names the parameter, not the line. npm run check points at it: slug doesn’t exist on the entry’s type.
  104. 21. Final project: a conference website · Step 7: link each talk to its tags · QuizEight talks have 13 tags between them, and three of them have css. How many pages does src/pages/tags/[tag].astro make?C. 6, one per different tagThe Set keeps each tag once, so css makes one page however many talks have it: the build lists six /tags/… pages. The pages are made at build time, one per entry getStaticPaths returns; nothing is worked out when someone visits, which is why /tags/php/ is the 404 page.
  105. 21. Final project: a conference website · Step 9: check the types · QuizIn TalkCard.astro you type {data.minute} min instead of {data.minutes} min. What happens?C. npm run build succeeds, every card loses its length, and npm run check reports itNeither the dev server nor the build checks types: data.minute is undefined, which draws as nothing, so the cards say “· Main hall · min”. astro check stops at it: error ts(2551): Property 'minute' does not exist on type '{ title: string; … minutes: number; … }'. Did you mean 'minutes'? The schema checks the files, not your code. Run the check before every build.