> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/withastro/astro/llms.txt
> Use this file to discover all available pages before exploring further.

# Islands Architecture

> Learn about Astro's islands architecture pattern for building fast, interactive web applications with partial hydration

Astro pioneered and popularized the **islands architecture** pattern. This approach allows you to build fast, content-focused websites while maintaining the ability to add interactive UI components where needed.

## What is Islands Architecture?

Islands architecture is a component-based architecture where interactive components (islands) are embedded within otherwise static HTML pages. Think of your page as an ocean of static HTML with scattered islands of interactivity.

<Info>
  **Key Concept**: Only the interactive components are shipped to the client as JavaScript, while the rest of the page remains static HTML. This dramatically reduces the amount of JavaScript sent to the browser.
</Info>

## How It Works

By default, Astro renders all components to static HTML on the server with **zero client-side JavaScript**. When you need interactivity, you opt-in using client directives.

```astro theme={null}
---
import Counter from '../components/Counter.jsx';
import Navigation from '../components/Navigation.astro';
---

<Navigation />
<h1>Welcome to my site</h1>
<Counter client:load />
```

In this example:

* `Navigation` renders as static HTML (no JavaScript)
* `Counter` becomes an interactive island that hydrates on the client

## Client Directives

Client directives tell Astro **when** and **how** to hydrate your interactive components. These directives are added as attributes to your component tags.

### `client:load`

Hydrates the component immediately on page load.

```astro theme={null}
<InteractiveHeader client:load />
```

<Tabs>
  <Tab title="Usage">
    Use `client:load` for high-priority UI that needs to be interactive immediately, like a navigation menu or chat widget.
  </Tab>

  <Tab title="Implementation">
    From the source at `src/runtime/client/load.ts`:

    ```ts theme={null}
    const loadDirective: ClientDirective = async (load) => {
      const hydrate = await load();
      await hydrate();
    };
    ```
  </Tab>
</Tabs>

### `client:idle`

Hydrates the component when the browser is idle (uses `requestIdleCallback`).

```astro theme={null}
<CommentSection client:idle />
```

<Info>
  This is the recommended directive for most interactive components. It waits until the main thread is free before hydrating.
</Info>

<Tabs>
  <Tab title="Usage">
    Ideal for lower-priority UI that doesn't need to be immediately interactive, like comment sections, social media embeds, or analytics widgets.
  </Tab>

  <Tab title="Implementation">
    From `src/runtime/client/idle.ts`:

    ```ts theme={null}
    const idleDirective: ClientDirective = (load, options) => {
      const cb = async () => {
        const hydrate = await load();
        await hydrate();
      };

      if ('requestIdleCallback' in window) {
        window.requestIdleCallback(cb, idleOptions);
      } else {
        setTimeout(cb, idleOptions.timeout || 200);
      }
    };
    ```
  </Tab>
</Tabs>

### `client:visible`

Hydrates the component when it enters the viewport (uses `IntersectionObserver`).

```astro theme={null}
<ImageCarousel client:visible />
```

<Tabs>
  <Tab title="Usage">
    Perfect for components below the fold, like image carousels, infinite scroll loaders, or any content that might not be immediately visible.
  </Tab>

  <Tab title="Options">
    You can customize the visibility threshold:

    ```astro theme={null}
    <HeavyComponent client:visible={{ rootMargin: "200px" }} />
    ```
  </Tab>

  <Tab title="Implementation">
    From `src/runtime/client/visible.ts`:

    ```ts theme={null}
    const visibleDirective: ClientDirective = (load, options, el) => {
      const cb = async () => {
        const hydrate = await load();
        await hydrate();
      };

      const io = new IntersectionObserver((entries) => {
        for (const entry of entries) {
          if (!entry.isIntersecting) continue;
          io.disconnect();
          cb();
          break;
        }
      }, ioOptions);

      for (const child of el.children) {
        io.observe(child);
      }
    };
    ```
  </Tab>
</Tabs>

### `client:media`

Hydrates the component when a CSS media query matches.

```astro theme={null}
<MobileMenu client:media="(max-width: 768px)" />
```

<Tabs>
  <Tab title="Usage">
    Useful for components that only make sense at certain screen sizes, like mobile-specific navigation or desktop-only features.
  </Tab>

  <Tab title="Implementation">
    From `src/runtime/client/media.ts`:

    ```ts theme={null}
    const mediaDirective: ClientDirective = (load, options) => {
      const cb = async () => {
        const hydrate = await load();
        await hydrate();
      };

      if (options.value) {
        const mql = matchMedia(options.value);
        if (mql.matches) {
          cb();
        } else {
          mql.addEventListener('change', cb, { once: true });
        }
      }
    };
    ```
  </Tab>
</Tabs>

### `client:only`

Skips server-side rendering and only renders the component on the client.

```astro theme={null}
<BrowserOnlyWidget client:only="react" />
```

<Warning>
  Use this sparingly! It opts out of Astro's server-side rendering benefits. Only use for components that have browser-only dependencies or cannot run on the server.
</Warning>

You must specify which framework to use:

```astro theme={null}
<ReactComponent client:only="react" />
<VueComponent client:only="vue" />
<SvelteComponent client:only="svelte" />
```

## Practical Example

Here's a complete page showing different hydration strategies:

```astro theme={null}
---
import Header from '../components/Header.astro';
import Hero from '../components/Hero.jsx';
import Newsletter from '../components/Newsletter.jsx';
import Comments from '../components/Comments.jsx';
import Analytics from '../components/Analytics.jsx';
import Footer from '../components/Footer.astro';
---

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>My Page</title>
  </head>
  <body>
    <!-- Static HTML, no JavaScript -->
    <Header />
    
    <!-- Hydrate immediately for critical interactivity -->
    <Hero client:load />
    
    <!-- Hydrate when visible -->
    <Newsletter client:visible />
    
    <!-- Hydrate when browser is idle -->
    <Comments client:idle />
    
    <!-- Only on mobile -->
    <Analytics client:media="(max-width: 768px)" />
    
    <!-- Static HTML -->
    <Footer />
  </body>
</html>
```

## Benefits

<CardGroup cols={2}>
  <Card title="Faster Performance" icon="rocket">
    Less JavaScript means faster page loads and better Core Web Vitals scores.
  </Card>

  <Card title="Better SEO" icon="search">
    Content is rendered as HTML on the server, making it immediately crawlable by search engines.
  </Card>

  <Card title="Progressive Enhancement" icon="sparkles">
    Pages work with JavaScript disabled, then enhance when it loads.
  </Card>

  <Card title="Framework Agnostic" icon="puzzle">
    Mix React, Vue, Svelte, and other frameworks on the same page.
  </Card>
</CardGroup>

## Technical Implementation

Astro wraps hydrated components in custom `<astro-island>` elements. From the source code (`src/runtime/server/hydration.ts`), the system:

1. Extracts client directives from component props
2. Validates the directive is supported
3. Generates hydration metadata
4. Wraps the component in an `<astro-island>` with hydration instructions

```ts theme={null}
// Simplified from src/runtime/server/hydration.ts
export function extractDirectives(inputProps: Props) {
  let extracted = {
    hydration: null,
    props: {},
  };
  
  for (const [key, value] of Object.entries(inputProps)) {
    if (key.startsWith('client:')) {
      extracted.hydration = {
        directive: key.split(':')[1],
        value: value,
      };
    } else {
      extracted.props[key] = value;
    }
  }
  
  return extracted;
}
```

## Best Practices

<Steps>
  <Step title="Start with static">
    Default to Astro components (`.astro` files) which render as static HTML.
  </Step>

  <Step title="Add interactivity selectively">
    Only use framework components (React, Vue, etc.) where you need client-side interactivity.
  </Step>

  <Step title="Choose the right directive">
    * `client:load` for critical UI
    * `client:idle` for most interactive components
    * `client:visible` for below-the-fold content
    * `client:media` for responsive components
    * `client:only` as a last resort
  </Step>

  <Step title="Measure the impact">
    Use browser DevTools to see how much JavaScript each directive loads.
  </Step>
</Steps>

## Learn More

<CardGroup cols={2}>
  <Card title="Components" href="/concepts/components">
    Learn about Astro's component model
  </Card>

  <Card title="Routing" href="/concepts/routing">
    Understand Astro's file-based routing
  </Card>
</CardGroup>
