Emanuel Suriano Hi 👋 I build stuff with JavaScript 💻 Once a month I write an article ✍️ and sometimes I give talks 💬

Understanding Astro islands architecture

5 min read 1671

Nowadays, we are all used to seeing new frameworks emerging with the promise of revolutionizing the way we build for the web. Nevertheless, we most often remain tied to a specific UI library (React, Vue, Svelte, etc.) to define our components and build the experiences for the users.

This time, the story is different! Using the power of Vite.js, we received Astro: an agnostic framework that can work as SSG (static site generator) and provide SSR (server-side rendering).

Using the Astro plugin system, we can build and enhance our websites the way that we want, and even combine different UI libraries into a single project.

In this post, we’ll learn about Astro islands and how they work under the hood to provide this functionality.

Jump ahead:

Astro 101: A brief introduction

When you look at the definition on their official website, you will find:

Astro is an all-in-one web framework for building fast, content-focused websites.

Recently, they released the first stable release, marking the framework as ready for production, a huge milestone for emerging frameworks. The web community has reacted very positively, and every day we are seeing more and more projects shipping with Astro frontends.

Astro has many cool features to make the developer and user experience great. I recommend checking out this other LogRocket article for a more general overview of Astro.

In my personal opinion, the key differentiator between Astro and other frameworks relies on its architecture: islands architecture. This concept was first described in 2019 by Katie Sylor-Miller and expanded on later in this post by the Preact creator Jason Miller.

Astro islands architecture

What are Astro islands?

The term “Astro island” refers to an interactive UI component on an otherwise static page of HTML. Multiple islands can exist on a page, and an island always renders in isolation, which means that each island can use any UI framework, or even just plain Astro code, alongside the other islands on a page.

Something quite important to highlight is that Astro generates every website with zero client-side JavaScript by default. Every time we render an island on a page, Astro automatically renders it to HTML ahead of time and then strips out all the JavaScript. This keeps every site fast by removing all unused JavaScript from the page.

Let’s take the implementation of a Counter component made in React as an example. When rendering the first time, it will show a button with the text “Counter: 0“, and every time the user clicks it, the counter will increase by 1.

// src/components/Counter.tsx
import { useState } from 'react';

const Counter = () => {
  const [count, setCounter] = useState(0);
  return (
    <button onClick={() => setCounter((number) => number + 1)}>
      Counter: {count}

export default Counter;

Then, let’s render this component in Astro.

To use React components in your Astro project, you have to add the @astrojs/react integration into your project.

// src/pages/index.astro
import Counter from '../components/Counter';

<Counter />

This code will render the button, but because JavaScript is removed by Astro at build time, the user won’t be able to increment the counter. When we want to make our app interactive, we have to be explicit by using client directives (we are going to cover them later).

This process is called partial or selective hydration. Essentially, it means shipping any framework code or runtime that is needed to support a component’s dynamic runtime requirements. Things like state changes and interactivity are prime examples.

// src/pages/index.astro
import Counter from '../components/Counter.jsx';

<Counter client:load />

One of the benefits of Astro islands, besides their heavy focus on making your app as light as it can be, is that every island is loaded in parallel. You can even specify the loading strategy for each island individually using client directives! This means we are in total control of how and when assets are loaded for the client and provide the best experience that we can.

How do Astro islands compare to the rest?

As I mentioned at the beginning of this post, the Astro team did not create islands architecture. The same technique is implemented in many frameworks, and has been shipped as an individual library.

A great example is is-land (from the 11ty team), which provides additional conditions when hydrating the component (a.k.a., client directives) such as:

  • on:interaction
  • on:save-data

It also allows you to specify a fallback when the component has not been hydrated yet:

<is-land on:interaction>
    <button type="button">Hydrate the island</button>

  <p>This content is before hydration.</p>

  <template data-island="replace">
    <vanilla-web-component>My component content after hydration</vanilla-web-component>

Despite some differences in syntax among frameworks, the main idea is that each framework ultimately implements partial hydration. The way that Astro composes the UI ensures that user demands always define the strategy for hydration, which is optional in other island frameworks such as is-land.

If you’d like to learn more about islands architecture, I found the GitHub awesome-islands to be a great resource that organizes a lot of content related to islands architecture.

All Astro client directives

A directive is a component attribute that tells Astro how your component should be rendered. At the moment of writing this article, Astro supports a total of five different client directives. This number may change as the framework adds new features.

Assuming we want to render our component, called MyComponent, depending on the client directive that we use, we can modify the way the user can interact with it:

  • <MyComponent client:load/>: Hydrates the component JavaScript immediately on page load
  • <MyComponent client:idle/>: Hydrates the component JavaScript once the page is done with its initial load and the requestIdleCallback event has fired
  • <MyComponent client:visible/>: Hydrates the component JavaScript once the component has entered the user’s viewport. This uses an IntersectionObserver internally to keep track of visibility
  • <MyComponent client:media={string}/>: Hydrates the component JavaScript once a certain CSS media query is met
  • <MyComponent client:only={string}/>: Skips HTML server rendering and renders only on the client. The component will be skipped at build time, which makes this useful for components that are entirely dependent on client-side APIs

Working with client directives

To illustrate the power of client directives, I created a small Astro project where I render the same Counter component that I showed above, using different client directives for anyone to try them all in one place. Feel free to take a look at the live application on Netlify.

Client directives demo

Below is the whole code for the page in the above screenshot. The content of the page is basically the same Counter component rendered a total of six times: first without specifying any client directive (the component is not interactive at all), and then the rest using all the different client directives that we covered above.

// src/pages/index.astro
import Layout from '../layouts/Layout.astro';
import Counter from '../components/Counter';

<Layout title="Welcome to Astro">
    <h1>Welcome to <span class="text-gradient">Astro</span></h1>

    <h2><pre>no directive</pre></h2>
    <p class="instructions">
      <code>No JS, no interactive</code>
      <Counter />

    <p class="instructions">
      <code>Loads JS as soon as possible</code>
      <Counter client:load />

    <p class="instructions">
      <code>Loads JS when rendering is over</code>
      <Counter client:idle />

    <p class="instructions">
      <code>Loads JS when the button is visible to the user</code>
      <Counter client:visible />

    <p class="instructions">
      <code>Loads JS when the media query (min-width: 680px) is valid</code>
      <Counter client:media="(min-width: 680px)" />

    <p class="instructions">
      <code>Loads JS only in client (No SSR)</code>
      <Counter client:only="react" />

The whole source code can be found in this GitHub repository. I highly recommend forking the project and running the project locally to fully understand how client directives can modify your application’s behavior.

Last words

I see Astro as a new fresh framework for building a website with the power to ship super-light websites using zero JavaScript code. It makes us be more aware of when we do need JavaScript in this heavy ecosystem, and when we can ship fewer KB to our clients.

Another great advantage of Astro is that it’s UI-agnostic, which means you can Bring Your Own UI Framework (BYOF)! React, Preact, Solid, Svelte, Vue, and Lit are all officially supported in Astro. You can even mix and match different frameworks on the same page, making future migrations easier and preventing project lock-in to a single framework.

Thanks for reading, and let’s keep building stuff together! 👷‍♂️

Are you adding new JS libraries to improve performance or build new features? What if they’re doing the opposite?

There’s no doubt that frontends are getting more complex. As you add new JavaScript libraries and other dependencies to your app, you’ll need more visibility to ensure your users don’t run into unknown issues.

LogRocket is a frontend application monitoring solution that lets you replay JavaScript errors as if they happened in your own browser so you can react to bugs more effectively.

LogRocket Dashboard Free Trial Banner https://logrocket.com/signup/

LogRocket works perfectly with any app, regardless of framework, and has plugins to log additional context from Redux, Vuex, and @ngrx/store. Instead of guessing why problems happen, you can aggregate and report on what state your application was in when an issue occurred. LogRocket also monitors your app’s performance, reporting metrics like client CPU load, client memory usage, and more.

Build confidently — Start monitoring for free.


Emanuel Suriano Hi 👋 I build stuff with JavaScript 💻 Once a month I write an article ✍️ and sometimes I give talks 💬

One Reply to “Understanding Astro islands architecture”

Leave a Reply