Astro Islands

By Syncfusion®, Inc. · Last updated

The islands architecture, adding a UI framework with npx astro add, framework components without a directive, client:load, client:idle, client:visible, client:media and client:only, props and children, sharing state between islands with a store, and when to use a script instead.

Astro Islands

  • Most of a page is just reading: text, pictures, links.
  • A few parts must react to clicks: a like button, a search box, a cart.
  • Astro sends zero JavaScript by default.
  • An island is one interactive component on an otherwise static page.
  • In this lesson, you will add islands built with a UI framework, and choose when each one wakes up.

Simple lesson: an island is a small interactive part in a sea of plain HTML.

A Real-World Example

  • Asha's post page is plain HTML. It loads fast.
  • She wants one Like button that counts clicks.
  • She does not want to ship JavaScript for the whole page.
  • So she writes the button as a Preact component and adds client:load to it. Only the button gets JavaScript.

Press ▶ Run. Click the button. Then read the Terminal: only the button became interactive.

What happened:

  • Astro rendered the button to HTML on the server, like everything else.
  • client:load told Astro: "also send this component's JavaScript".
  • In the browser, Preact hydrated the button: it attached the click code to the HTML that was already there.

What You Will Learn

In this lesson, you will learn:

  1. What the islands architecture is.
  2. How to add a UI framework such as Preact, React, Vue or Svelte.
  3. Why a framework component without a directive sends no JavaScript.
  4. The five client:* directives and when to use each.
  5. How to pass props and children to an island.
  6. How islands share state.
  7. When to use an island and when a plain <script> is enough.

The Islands Architecture

Islands in a Sea of HTML

Animation: a page is mostly static HTML. Two small islands, a like button and a search box, light up one after the other as their JavaScript loads.

The page: static HTML (no JavaScript)

Header and navigation

Blog post text and images

Island: Like button

Island: Search box

Blue is plain HTML. Each orange island loads its own small JavaScript, on its own schedule.

  • The page is the "sea": plain HTML and CSS.
  • Each interactive component is an "island".
  • Every island loads on its own. A slow island does not block the others.
  • Many frameworks render the whole page in JavaScript. Astro hydrates only the islands.

Why Islands Are Fast

  • Less JavaScript to download.
  • Less JavaScript to run on a cheap phone.
  • The text is visible before any JavaScript runs.
  • You choose when each island loads: now, later, or only when it scrolls into view.

Astro components (.astro files) never run in the browser. Islands are built with a UI framework component, such as Preact, React, Vue, Svelte or Solid.

Simple lesson: islands keep the page static and add JavaScript only where it is needed.

Adding a UI Framework

Installing an Integration

Add a framework with one command:

npx astro add preact
  • The command installs @astrojs/preact and preact.
  • It adds preact() to integrations in astro.config.mjs.
  • For React use npx astro add react. For Vue, vue. For Svelte, svelte.
  • You may use more than one framework on the same site.

This course uses Preact. It looks like React (same JSX and hooks) but is about 4 KB. Everything you learn here works the same way with React.

A Framework Component Without a Directive

  • You can use a .tsx or .jsx component in an .astro page, like an Astro component.
  • With no client:* directive, Astro renders it to HTML on the server and sends no JavaScript.
  • The button below shows, but clicking does nothing.

Press ▶ Run and click the first button. Nothing happens. The second button works.

  • Open the HTML tab. Only the island is wrapped in <astro-island>.
  • <astro-island> is the element Astro uses to find and wake up an island.

When the Integration Is Missing

  • Without the integration, Astro does not know how to render .tsx files.
  • The build stops with "No valid renderer was found".

Press ▶ Run to see the error. Then add integrations: [preact()] to fix it.

Simple lesson: add the framework with npx astro add. A component without client:* is only HTML.

Client Directives

The Five Directives

DirectiveWhen the island wakes upGood for
client:loadAs soon as the page loadsThings needed at once: a menu button
client:idleWhen the browser is freeLess urgent things: a chat widget
client:visibleWhen it scrolls into viewThings far down: comments, a footer form
client:media="(query)"When a CSS media query matchesMobile-only parts: a hamburger menu
client:only="preact"Only in the browser, never on the serverCode that needs window
  • client:load is the most eager. Use it only for things needed at once.
  • client:idle and client:visible keep the first load light.
  • client:media saves JavaScript on screens that do not need the island.
  • client:only skips server HTML. The island is empty until JavaScript runs.

Animation: a timeline from page load. client:load wakes first, client:idle a little later, client:visible only when the reader scrolls to it.

page loads time →

client:load

client:idle

client:visible

Each island wakes at its own time. Until then it is plain HTML.

Press ▶ Run. Read the Terminal to see the order the islands wake up in.

  • The clocks start at slightly different times.
  • WindowWidth uses window, which does not exist on the server. So it needs client:only.

Do not add client:load to everything "just in case". Each island adds JavaScript. Start with no directive, and add the laziest one that works.

Simple lesson: pick the laziest directive that still works: often client:visible or client:idle.

Props and Children

Passing Props

  • Pass props to an island like any component: <Counter start={5} client:load />.
  • Astro sends the props to the browser as data, so the island can hydrate with the same values.
  • So props must be data: text, numbers, booleans, arrays, plain objects, Date, Map, Set.
  • Functions cannot be passed to a hydrated island.

Passing Children

  • Content between the tags arrives in the component as children.
  • That content is rendered by Astro as static HTML. It can even contain Astro components.

Press ▶ Run. The island gets a number prop and some HTML children.

  • [JS] In Spoiler.jsx, leave out interface Props and the : Props type.

A Function Prop Breaks the Island

Press ▶ Run. The build stops, because a function cannot travel to the browser.

  • Fix: keep the click code inside the island.

Simple lesson: island props must be plain data. Children arrive as static HTML.

Sharing State Between Islands

Islands Are Separate

  • Each island is its own small app.
  • Two islands cannot read each other's useState.
  • To share data, both islands import the same store module.

A Tiny Shared Store

  • A store holds a value and tells listeners when it changes.
  • Real projects often use the small nanostores library (npm install nanostores @nanostores/preact).
  • Below, a 10-line store does the same job, so you can see how it works.

Press ▶ Run. Click Add to cart in one island. The badge in the other island updates.

  • Both islands imported ../stores/cart, so they share one count.
  • useEffect(() => subscribe(setN), []) re-renders the badge on every change.

With nanostores the same idea is const cart = atom(0) in the store, and useStore(cart) in each island.

Simple lesson: islands share data through a store module that both import.

Island or Script?

Choosing the Right Tool

  • A <script> in an .astro file is enough for small behaviour: toggle a class, copy text, open a menu.
  • An island is better when the UI has state that changes what is drawn: a cart, a filter list, a form with live checks.
  • An island is also right when you already have a framework component you want to reuse.
NeedUse
Toggle a menu open and closed<script>
Copy a code block to the clipboard<script>
A search box that filters a list as you typeIsland
A cart shared by the header and the pageIslands + a store
A chart library made for ReactIsland (client:visible)

Islands render on the server first, so readers see the HTML even before JavaScript loads. Only client:only islands start empty.

Simple lesson: use a <script> for small actions and an island for UI with changing state.

TL;DR

  1. Island = one interactive component on a static page. The rest sends no JavaScript.
  2. Framework = add one with npx astro add preact (or react, vue, svelte).
  3. No directive = server HTML only, no JavaScript.
  4. client:load = at once. client:idle = when the browser is free. client:visible = when seen.
  5. client:media = when a media query matches. client:only="preact" = browser only.
  6. Props = plain data only, no functions. Children = static HTML.
  7. Shared state = a store module both islands import (nanostores in real projects).
  8. Small action = <script>. Changing UI state = island.

Common mistakes

MistakeWrong ideaCorrect ideaWhat goes wrong
No client:* directiveA framework component is always interactiveAdd a directiveClicks do nothing
client:load on every islandEager is always betterUse idle or visible when you canSlower first load
client: on an .astro componentAstro components can hydrateUse a framework componentThe directive does nothing
Passing a function propProps can be anythingOnly data can be sentBuild error
Using window without client:onlyThe component runs only in the browserIslands render on the server firstwindow is not defined
No framework integration.tsx just worksRun npx astro add preact"No valid renderer was found"
Expecting two islands to share useStateIslands are one appUse a shared storeEach island has its own value

Check Yourself

Exercises

What to Remember

  • An island is one interactive framework component. The rest of the page stays HTML.
  • Add a framework with npx astro add. No directive means no JavaScript.
  • Choose the laziest directive: visible or idle before load. Use only for browser-only code.
  • Props must be data. Children arrive as HTML.
  • Share state between islands with a store module.

Next: Page Navigation, where pages swap smoothly without a full reload.