Astro has a feature called Prefetch and an architectural pattern called Islands architecture.
Roughly speaking, Prefetch is a mechanism for predicting the page a user may visit next and loading it ahead of time. Islands architecture is a mechanism for separating dynamic UI and server-side work as “islands” inside otherwise static HTML, then loading each part at an appropriate timing.
While thinking about these two ideas, one question came to mind:
When using Prefetch, do Prefetch and Islands architecture relate to or interfere with each other?
I have a rough mental model of how they behave, but I wanted to organize it properly in this article.
Conclusion First
When using Prefetch, do Prefetch and Islands architecture relate to or interfere with each other?
My current answer can be expressed with this diagram.
Prefetch is responsible for the left side of the diagram, while Islands architecture is responsible for the right side. In other words, I think they do not interfere much with each other. They are responsible for different layers. From the user’s point of view, however, those layers are connected as a continuous flow: Prefetch first, then island processing.
With that conclusion in mind, let’s look into the details.
Prerequisite: What is Prefetch?
Because this article discusses both Prefetch and Islands architecture, let’s start with the prerequisite concepts.
So, what is Prefetch?
It is a mechanism that predicts the page a user may navigate to next and loads it ahead of time.
I think that sentence and diagram capture the basic idea.
That said, the design philosophy of a framework or library tends to appear in two areas:
- How it predicts which page the user will navigate to
- How it actually performs the ahead-of-time loading
In Astro’s case, the basic stance is to use browser-standard APIs where possible. It helps to imagine the following pieces working behind the scenes.
Triggers for predicting user navigation
Astro Prefetch lets you choose from four strategies for deciding whether the user is likely to select a link next: hover, tap, viewport, and load. The default is hover. This means developers can tune the trigger depending on whether they want to prioritize speed or reduce unnecessary network requests.
hoverprefetches when the user hovers over a link or when keyboard focus enters it. In the implementation,mouseenterandfocusinare used as starting points. Astro waits briefly before running the prefetch, and cancels it if the user moves away.tapfires ontouchstartormousedown. Since this is very close to the click itself, it can reduce unnecessary prefetching.viewportprefetches at low priority when a link enters the viewport. Under the hood, it is based onIntersectionObserver.loadprefetches links on the page one by one, at low priority, after the page has finished loading.
Looking at these four strategies, Astro Prefetch is not only about how to load the next page. It also includes the question of which moment should be treated as the user’s navigation intent.
The loading logic
So what actually happens once one of those triggers fires? Here too, Astro stays close to browser-standard mechanisms. In normal Prefetch, if the browser supports it, Astro uses <link rel="prefetch"> to load the next page document ahead of time. In environments where <link rel="prefetch"> does not work well, such as some Safari cases, Astro falls back to fetch(). Also, based on the behavior of the prefetch() API, it avoids forcing prefetching on slow connections or when Data Saver is enabled.
If experimental.clientPrerender is enabled, Astro uses the Speculation Rules API to go beyond Prefetch and perform Prerender as well, preparing the next page more aggressively. I see this as an extension of the existing Prefetch behavior.
In short, Astro’s loading logic can be summarized like this:
- Under normal conditions, prioritize
<link rel="prefetch"> - Fall back to
fetch()in unsupported browsers - Use the Speculation Rules API when
experimental.clientPrerenderis enabled
This posture of using browser standards and only falling back when needed feels very representative of Astro’s design philosophy.
I have written a few related articles before, so please take a look if you want more detail.
- プリフェッチから学ぶWebパフォーマンスの最適化
- Astroはなぜ速い?プリフェッチの内部実装を追う
- Speculation Rules API から見るブラウザのプリフェッチ戦略
- AstroとNext.jsのPrefetchを比較し、アプローチの違いを整理する
What is Islands Architecture?
Overview
Now we get to the main topic. What is Islands architecture?
You can get a rough understanding by reading the official Astro docs.
There are also useful Zenn articles for understanding Astro’s Islands architecture. Since Islands architecture can look similar to Next.js PPR on the surface, comparison articles are also helpful. Reading them side by side can deepen the mental model, so I will include them here. Thank you to the authors.
After reading these, I would summarize Islands architecture like this:
A mechanism for embedding only the necessary dynamic UI and server-side processing as islands inside static HTML.
Of course, that may be too simple. The island concept did not originally come from Astro. Since I do not want to say something inaccurate here, I will refer to the official Astro documentation:
Source: https://docs.astro.build/en/concepts/islands/The term “component island” was first coined by Etsy’s frontend architect Katie Sylor-Miller in 2019. Preact creator Jason Miller later expanded and popularized the idea in 2020.
Historically, the rough flow is:
- Katie Sylor-Miller
- Jason Miller, creator of Preact
After Jason Miller popularized the idea, it influenced many frameworks and libraries. In terms of popularizing the concept, I think Astro has had a major influence on frameworks and libraries that came after it. I like Astro, so this may be a slightly biased take.
Key Points to Understand
To understand Astro’s Islands architecture, I think there are at least three points to keep in mind:
- Astro returns HTML and CSS by default. For JavaScript that does not go through an island, see A side note: what happens when you write JavaScript outside islands?
- In certain cases, such as when embedding interactive UI, hydration1 through an island is needed
- There are two kinds of islands: ones hydrated later in the browser, and ones rendered on the server at a separate timing
Astro itself is an MPA-style framework. Islands architecture is often discussed together with Astro’s default zero-JS approach and its ability to embed UI frameworks such as React. I think these three characteristics are deeply connected.
Let’s look at the ability to embed UI frameworks such as React as a bundle of JavaScript code that works with a different philosophy from Astro’s MPA approach.
Then it becomes clear that all three are related to JavaScript. In that sense, Astro feels very intentional about how JavaScript is handled. Islands architecture is not only about where JavaScript is needed. It is also a way to think about when JavaScript runs and what unit it should be separated into.
A Side Note: What Happens When You Write JavaScript Outside Islands?
In Astro, you can write JavaScript without using islands. If you are working with .astro files, there are mainly three ways to write it:
- Write it inside the component script, which is the frontmatter-like part inside the code fence
- Use a
<script>tag in the component template - Create a
*.tsor*.jsfile undersrc, then import it with<script src>inside an.astrofile
Briefly, each case is handled like this.
Case 1: Writing JavaScript Inside the Component Script
JavaScript written inside the component script runs at build time or during server rendering. It is used to create HTML and is not sent to the browser.
Case 2: Using a <script> Tag in the Component Template
A <script> in the template is sent to the browser as a client script. This is where you write page behavior such as DOM manipulation or event handling.
Case 3: Creating a *.ts or *.js File Under src and Importing it with <script src>
When a .ts or .js file under src is loaded with <script src>, it is also treated as a client script that runs in the browser. Compared with writing directly in the template, this can be easier to reuse and maintain, especially when the code gets longer.
There Are Two Kinds of Islands
Looking through the Astro documentation, there are two kinds of islands with different roles.
Client Island
A Client Island is the idea of hydrating interactive UI in the browser only when needed. In Astro, UI framework components are first output as HTML. Then, depending on directives such as client:load, client:idle, client:visible, client:media, and client:only, JavaScript is loaded later and the component becomes interactive.
In short, the concern of a Client Island is: when should this UI run in the browser? This is also one of the big differences from Prefetch, which will appear later.
Server Island
A Server Island, on the other hand, is a mechanism for separating dynamic content or heavy server work from the main page and inserting it later. When server:defer is used, Astro first returns the HTML for the whole page, then sends a GET request to a special endpoint and fetches the Server Island content separately. If you provide a slot="fallback", you can show temporary content until the real content is ready.
In other words, the concern of a Server Island is: when should this part be returned from the server? Client Islands handle browser-side hydration timing, while Server Islands handle server-side HTML response timing. I think this distinction makes the model easier to understand.
Organizing Prefetch and Islands Architecture by Execution Flow
First, it becomes much easier to understand if we separate the areas covered by each concept.
Prefetch is about how far the current page should prepare a page the user may navigate to next. Its main layer is before navigation.
Islands architecture is about which parts of the destination page should run, in what units, and at what timing. Its main area of responsibility is inside the page after navigation.
At this point, we can see that both mechanisms control how information is fetched or prepared, but they basically own different layers.
Assuming normal Prefetch, the execution flow can be organized roughly like this:
- On the current page, Astro waits for triggers such as
hoverortapon links - When the conditions are met, Astro prefetches the next page document using
<link rel="prefetch">orfetch() - At this stage, the user has not navigated yet, so the Client Islands on the next page are not hydrated
- When navigation actually happens, the next page is displayed using the prefetched document as a base
- After that, the Client Islands inside the next page hydrate according to their
client:*conditions - If there is a Server Island using
server:defer, its content is inserted through a separate request while the fallback is shown
Steps 1 and 2 are the Prefetch layer. Steps 4, 5, and 6 are the Islands architecture layer.
Viewed this way, standard Prefetch and Islands architecture are less like competing mechanisms and more like a handoff. Prefetch prepares the next page in advance, while Islands architecture determines which parts of that page run, when they run, and in what environment.
Summary
In this article, I observed Islands architecture through the lens of Astro Prefetch.
Prefetch and Islands architecture are both mechanisms for improving user experience, but under normal conditions they look at different layers. Prefetch handles how the next page is prepared before navigation. Islands architecture handles how the destination page is divided and when each part runs after navigation.
So in normal Astro Prefetch, I think it is natural to see the two as sharing consecutive phases rather than interfering with each other. However, if experimental.clientPrerender goes as far as prerendering, preparation for executing the next page may begin before navigation. That is where the overlap with Islands architecture may start to increase.
Writing this article made me investigate Islands architecture more deeply, and I feel I understand better why it is often raised as one of Astro’s defining characteristics. Islands architecture is often discussed in the context of embedding React or hydration, but from another angle, those integrations may be closer to byproducts.
I want to keep observing these kinds of details. Thank you for reading.
Let’s be an Astronaut!
References
Here are the resources I referred to, including the ones mentioned in the article.
- プリフェッチから学ぶWebパフォーマンスの最適化
- Astroはなぜ速い?プリフェッチの内部実装を追う
- Speculation Rules API から見るブラウザのプリフェッチ戦略
- Prefetch | Astro Docs
- Experimental client prerendering | Astro Docs
- Islands | Astro Docs
- Template directives reference | Astro Docs
- Server islands | Astro Docs
- Speculation Rules API | MDN
-
rel="prefetch" HTML attribute value - HTML | MDNThe prefetch keyword for the rel attribute of the <link> element provides a hint to browsers that the user is likely to need the target resource for future navigations, and therefore the browser can likely improve the user experience by preemptively fetching and caching the resource. <link rel="prefetch"> is used for same-site navigation resources, or for subresources used by same-site pages.
developer.mozilla.org
- Astro Prefetch tap logic | GitHub repository
- Astro Prefetch hover logic | GitHub repository
- Astro Prefetch viewport logic | GitHub repository
- Astro Prefetch load logic | GitHub repository