# 200. Islands and hydration

**For:** app developers. Shipping the least JavaScript that still works.

## Server-rendered components

Most components never reach the browser. The build lowers them and the host renders them, so they only cost markup. A component qualifies when it is a function of its props: it may branch, map over arrays, compose other lowered components and read what it was given.

It may not hold state, reach for a browser API or call a hook. Any of those makes a component an island.

## Declaring an island

```tsx
import { Island } from "@snapfire/fsr-client/react";
import { Cart } from "@src/ui/Cart";

<Island when="visible">
  <Cart items={items} />
</Island>;
```

`when` decides when it hydrates:

| `when` | Hydrates |
|---|---|
| `load` | As soon as the entry module runs |
| `visible` | When it scrolls into view |
| `idle` | When the browser is idle |

A page and a layout are each an island in their own right. They hydrate in separate roots, which is why a navigation can swap the page and leave the layout's DOM and state untouched.

## Island keys and state

Every placement carries a key the build gave it and the server writes the same key on the markup it rendered there. The browser pairs the two by that key rather than by the order they appear in.

Because of this an island is safe inside a `.map` or behind a condition. If you reorder the list, drop an entry or flip the condition, each island still finds the markup that belongs to it. An open menu or a half-typed field stays with its own row instead of moving to its neighbour.

## Server islands

`mode="server"` is the other kind. The component's interactions round-trip to Rust, which answers with a patch. The browser ships no component code at all.

```tsx
<Island when="visible" mode="server">
  <TelemetryBadge />
</Island>
```

Use it for a disclosure whose content is expensive or for anything whose state belongs on the server. Every interaction is a request, so do not use it for anything that has to feel instant, such as a hover, a drag or a text field.

## Islands in something other than React

An island's registry entry names a mounter. Nothing above the entry depends on which one. A `.vue` component, a custom element definition or an htmx region can each take the place of a React island. More than one kind can sit in the same document. See [Vue, Web Components, htmx and Tera](https://snapfirers.com/fsr/docs/204-other-frameworks-and-none.md).

## What an island costs

The islands and the client runtime are the only JavaScript in the bundle, plus whatever `app/vendor/` holds. The import map is the allowlist that limits it: an import with no entry fails the build.

Vendoring is explicit and committed:

```
fsr add app lodash-es@4.17.21
```

That writes an ESM bundle under `app/vendor/` and adds the import-map entry. The tree is committed, so a clone builds offline and nothing is fetched at deploy time.

## Try it

Take a component that uses `useState` for something the server already knows and delete the state. Watch it drop out of the bundle and into the `rendered` list `fsr build --verbose` prints. That list shows how much of your application the browser has to run.
