Islands and deployment
Finish the Astro site
Deploy the content project with one deliberate island and record why every piece of browser JavaScript exists.
Let’s finish the notes site with the one island it needs: a topic filter on the list page.
HTML first
The list page already renders every published note as plain HTML, with working links. Keep it that way. Before any JavaScript runs, a visitor must be able to read the list and open a note.
The filter is an enhancement on top of that. It hides the notes outside the selected topic. If it never loads, nothing is lost.
The component
Here is a small React version in src/components/TopicFilter.jsx:
import { useState } from 'react'
export default function TopicFilter({ notes }) {
const [topic, setTopic] = useState('all')
const visible = topic === 'all' ? notes : notes.filter(note => note.topic === topic)
return (
<>
<select value={topic} onChange={e => setTopic(e.target.value)}>
<option value="all">All topics</option>
<option value="astro">Astro</option>
<option value="css">CSS</option>
<option value="javascript">JavaScript</option>
</select>
<ul>
{visible.map(note => (
<li key={note.id}>
<a href={`/notes/${note.id}/`}>{note.title}</a>
</li>
))}
</ul>
</>
)
}
In src/pages/notes/index.astro, keep the getCollection() call with the draft filter from the data module. Then pass only what the component needs:
---
import TopicFilter from '../../components/TopicFilter.jsx'
const items = notes.map(note => ({
id: note.id,
title: note.data.title,
topic: note.data.topic
}))
---
<TopicFilter client:idle notes={items} />
Three small fields per note. No dates, no bodies, nothing you wouldn’t print in the HTML. Astro renders the full list on the server, so the links are there at time zero. Hydration only adds the behavior of the <select>.
Pick the directive on purpose
I chose client:idle because the filter sits below the page intro, and nobody filters in the first second. If the filter were the whole point of the page, client:load would be right. If it sat far below the fold, client:visible.
Write the reason down next to the component. “This is what I always use” is not a reason.
Verify the build
Run npm run build and check:
- the collection schema accepts every published entry
- draft notes produce neither links nor detail pages
- every note page has a title, a description, and one main heading
- local images have generated dimensions and useful alt text
- the page still shows all notes and links with JavaScript disabled
- only the filter’s React code loads in the browser
Then preview, and deploy. On the real domain, test a nested note URL, the custom 404, one optimized image, and the filter with the network throttled. Read status codes and browser requests, not appearances.
Record three facts
Before you call it done, write down how many routes were generated, which routes are static or on demand, and why each client-side script exists. If you can’t explain a script, remove its directive and see whether the site gets better. It usually does.
You now have the mental model for any Astro project, large or small. Data is loaded and validated at one boundary. Routes decide what becomes public. Components compose the HTML. Islands add browser code only where the visitor needs it.
Lesson completed