> ## 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.

# Server Islands

> Mix static and dynamic content with Astro's server islands for optimal performance

Server Islands allow you to embed dynamic, server-rendered components within static pages. This gives you the best of both worlds: fast static page loads with selective server-rendered content for personalization, real-time data, or authentication.

## What are Server Islands?

Server Islands are components that render on the server for each request, even when the rest of the page is statically generated. They load asynchronously after the initial page render, providing dynamic content without blocking the main page load.

<Note>
  Server Islands require an SSR adapter and work in `output: 'static'` or `output: 'hybrid'` modes.
</Note>

## Creating a Server Island

Mark any component as a server island using the `server:defer` directive:

```astro src/pages/index.astro theme={null}
---
import UserProfile from '../components/UserProfile.astro';
import RealtimeStock from '../components/RealtimeStock.astro';
---

<html>
  <head>
    <title>My Site</title>
  </head>
  <body>
    <h1>Welcome to My Site</h1>
    
    <!-- Static content renders immediately -->
    <section class="hero">
      <p>This content is static and super fast!</p>
    </section>

    <!-- Server island renders on each request -->
    <UserProfile server:defer />

    <!-- Another server island with real-time data -->
    <RealtimeStock symbol="ASTRO" server:defer />

    <!-- More static content -->
    <footer>
      <p>© 2026 My Site</p>
    </footer>
  </body>
</html>
```

## Server Island Component

Server islands are just regular Astro components that can access server-side features:

```astro src/components/UserProfile.astro theme={null}
---
const { cookies } = Astro;
const userId = cookies.get('userId')?.value;

let user = null;
if (userId) {
  user = await db.users.find(userId);
}
---

{user ? (
  <div class="profile">
    <img src={user.avatar} alt={user.name} />
    <h2>Welcome back, {user.name}!</h2>
    <p>Last login: {new Date(user.lastLogin).toLocaleString()}</p>
  </div>
) : (
  <div class="login-prompt">
    <p>Please log in to see your profile</p>
    <a href="/login">Log In</a>
  </div>
)}

<style>
  .profile {
    border: 1px solid #ddd;
    padding: 1rem;
    border-radius: 8px;
  }
</style>
```

## How It Works

<Steps>
  <Step title="Initial Page Load">
    The static HTML is served immediately with a placeholder for the server island.

    ```html theme={null}
    <astro-island uid="abc123" component-url="/_server-islands/UserProfile">
      <div>Loading...</div>
    </astro-island>
    ```
  </Step>

  <Step title="Server Rendering">
    The browser requests the server island content via a background fetch:

    ```
    GET /_server-islands/UserProfile?s=encrypted_slots&p=encrypted_props
    ```
  </Step>

  <Step title="Hydration">
    The server responds with rendered HTML, which replaces the placeholder:

    ```html theme={null}
    <div class="profile">
      <img src="/avatar.jpg" alt="John" />
      <h2>Welcome back, John!</h2>
    </div>
    ```
  </Step>
</Steps>

<AccordionGroup>
  <Accordion title="Performance Impact">
    * Initial page loads instantly (static HTML)
    * Server islands load asynchronously
    * No blocking on server-rendered content
    * Main content visible immediately
  </Accordion>

  <Accordion title="Fallback Content">
    Provide fallback content that shows while loading:

    ```astro theme={null}
    <UserProfile server:defer>
      <div slot="fallback">
        <p>Loading profile...</p>
      </div>
    </UserProfile>
    ```
  </Accordion>
</AccordionGroup>

## Use Cases

<CardGroup cols={2}>
  <Card title="Personalization" icon="user">
    Show user-specific content like profiles, recommendations, or preferences
  </Card>

  <Card title="Real-time Data" icon="clock">
    Display live data like stock prices, availability, or analytics
  </Card>

  <Card title="Authentication" icon="lock">
    Render auth-protected content based on session state
  </Card>

  <Card title="A/B Testing" icon="flask">
    Serve different variants for experiments
  </Card>
</CardGroup>

## Passing Props

Pass props to server islands just like regular components:

```astro src/pages/product/[id].astro theme={null}
---
import StockStatus from '../../components/StockStatus.astro';

const { id } = Astro.params;
const product = await getProduct(id);
---

<html>
  <body>
    <h1>{product.name}</h1>
    <p>{product.description}</p>

    <!-- Pass product ID to server island -->
    <StockStatus productId={id} server:defer>
      <div slot="fallback">Checking availability...</div>
    </StockStatus>
  </body>
</html>
```

```astro src/components/StockStatus.astro theme={null}
---
interface Props {
  productId: string;
}

const { productId } = Astro.props;

// Fetch real-time stock data
const stock = await inventory.check(productId);
---

<div class="stock-status">
  {stock.available ? (
    <span class="in-stock">
      ✓ In Stock ({stock.quantity} available)
    </span>
  ) : (
    <span class="out-of-stock">
      ✗ Out of Stock
    </span>
  )}
</div>
```

<Warning>
  Props are encrypted during transmission for security. Don't pass sensitive data that shouldn't be cached.
</Warning>

## Slots

Use slots to pass content to server islands:

```astro src/pages/dashboard.astro theme={null}
---
import AdminPanel from '../components/AdminPanel.astro';
---

<AdminPanel server:defer>
  <div slot="fallback">
    <p>Loading admin panel...</p>
  </div>
  
  <h2>Admin Controls</h2>
  <p>Manage your site here</p>
</AdminPanel>
```

```astro src/components/AdminPanel.astro theme={null}
---
const user = Astro.locals.user;

if (!user?.isAdmin) {
  return Astro.redirect('/forbidden');
}
---

<div class="admin-panel">
  <slot />
  
  <!-- Server-rendered admin data -->
  <div class="stats">
    <p>Active users: {await db.users.countActive()}</p>
    <p>Pending reviews: {await db.reviews.countPending()}</p>
  </div>
</div>
```

## Configuration

Enable server islands in your Astro config:

```js astro.config.mjs theme={null}
import { defineConfig } from 'astro/config';
import node from '@astrojs/node';

export default defineConfig({
  output: 'hybrid',
  adapter: node(),
  experimental: {
    serverIslands: true
  }
});
```

<Tip>
  Server islands work best with `output: 'hybrid'` for mixing static and dynamic content.
</Tip>

## Combining with Client Islands

Mix server islands with client-side framework components:

```astro src/pages/app.astro theme={null}
---
import UserData from '../components/UserData.astro';
import InteractiveChart from '../components/Chart.tsx';
---

<html>
  <body>
    <!-- Server island: personalized data -->
    <UserData server:defer />

    <!-- Client island: interactive UI -->
    <InteractiveChart client:load />

    <!-- Static content -->
    <section class="info">
      <h2>About Our Service</h2>
      <p>Static marketing content...</p>
    </section>
  </body>
</html>
```

<AccordionGroup>
  <Accordion title="Server Islands">
    * Render on server for each request
    * Access databases and sessions
    * Load asynchronously
    * No JavaScript hydration needed
  </Accordion>

  <Accordion title="Client Islands">
    * Render on client
    * Interactive components
    * Require JavaScript
    * Hydrate in the browser
  </Accordion>
</AccordionGroup>

## Caching Strategies

Control caching for server islands:

```astro src/components/CachedWidget.astro theme={null}
---
// Set cache headers for this server island
Astro.response.headers.set('Cache-Control', 'public, max-age=60');

const data = await fetchExpensiveData();
---

<div class="widget">
  <h3>{data.title}</h3>
  <p>Updated: {new Date().toLocaleTimeString()}</p>
</div>
```

Use with `server:defer`:

```astro theme={null}
<CachedWidget server:defer />
```

## Error Handling

Handle errors gracefully in server islands:

```astro src/components/WeatherWidget.astro theme={null}
---
interface Props {
  city: string;
}

const { city } = Astro.props;

let weather;
let error;

try {
  weather = await fetchWeather(city);
} catch (e) {
  error = e.message;
}
---

<div class="weather">
  {error ? (
    <div class="error">
      <p>Failed to load weather: {error}</p>
      <button onclick="location.reload()">Retry</button>
    </div>
  ) : (
    <div class="data">
      <h3>{weather.city}</h3>
      <p>{weather.temperature}°F</p>
      <p>{weather.condition}</p>
    </div>
  )}
</div>
```

## Loading States

Provide meaningful loading states:

```astro src/pages/index.astro theme={null}
---
import RecentActivity from '../components/RecentActivity.astro';
---

<RecentActivity server:defer>
  <div slot="fallback" class="skeleton">
    <div class="skeleton-line"></div>
    <div class="skeleton-line"></div>
    <div class="skeleton-line"></div>
  </div>
</RecentActivity>

<style>
  .skeleton-line {
    height: 20px;
    background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
    background-size: 200% 100%;
    animation: loading 1.5s infinite;
    margin-bottom: 10px;
    border-radius: 4px;
  }

  @keyframes loading {
    0% { background-position: 200% 0; }
    100% { background-position: -200% 0; }
  }
</style>
```

## Advanced Example

A complete example combining multiple concepts:

```astro src/pages/dashboard.astro theme={null}
---
import UserStats from '../components/UserStats.astro';
import RealtimeNotifications from '../components/Notifications.astro';
import ActivityFeed from '../components/ActivityFeed.astro';
---

<html>
  <head>
    <title>Dashboard</title>
  </head>
  <body>
    <header>
      <h1>Dashboard</h1>
    </header>

    <main class="grid">
      <!-- User-specific stats -->
      <UserStats server:defer>
        <div slot="fallback">Loading stats...</div>
      </UserStats>

      <!-- Real-time notifications -->
      <RealtimeNotifications server:defer>
        <div slot="fallback">Loading notifications...</div>
      </RealtimeNotifications>

      <!-- Activity feed -->
      <ActivityFeed server:defer>
        <div slot="fallback">Loading activity...</div>
      </ActivityFeed>
    </main>
  </body>
</html>

<style>
  .grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
    gap: 1rem;
    padding: 1rem;
  }
</style>
```

## Best Practices

<Steps>
  <Step title="Keep islands small">
    Server islands should be focused components. Split large components into multiple islands.
  </Step>

  <Step title="Provide fallbacks">
    Always show something while loading. Use skeleton screens or loading messages.
  </Step>

  <Step title="Handle errors">
    Gracefully handle failures and provide retry mechanisms.
  </Step>

  <Step title="Consider caching">
    Cache server island responses when appropriate to reduce server load.
  </Step>

  <Step title="Monitor performance">
    Track server island response times and optimize slow queries.
  </Step>
</Steps>

## Related Resources

<CardGroup cols={2}>
  <Card title="SSR and SSG" icon="server" href="/features/ssr-and-ssg">
    Understanding rendering modes
  </Card>

  <Card title="Components" icon="cube" href="/concepts/components">
    Building Astro components
  </Card>

  <Card title="Islands Architecture" icon="island-tropical" href="/concepts/islands">
    Learn about islands architecture
  </Card>

  <Card title="Middleware" icon="filter" href="/features/middleware">
    Add server-side logic
  </Card>
</CardGroup>
