> [Agent navigation index](/llms.txt)

# Islands

Brix renders Preact pages on the server and hydrates only components marked as islands, keeping static page content outside the browser interaction boundary.

## Create an island

Add a directive to a PascalCase component module and export exactly one named PascalCase runtime component:

```tsx
"use island";

import { useSignal } from "brix/hooks";

export function QuantityPicker() {
  const quantity = useSignal(1);

  return (
    <button type="button" onClick={() => quantity.value++}>
      Quantity: {quantity}
    </button>
  );
}

```

Type-only exports are allowed. Default exports and unrelated runtime exports fail discovery and build validation. Import component hooks from `brix/hooks`, not directly from Preact or the signals package.

## Render it from a page

An ordinary server-rendered component can include the island normally:

```tsx
import { QuantityPicker } from "#/features/cart/QuantityPicker.tsx";

export function ProductPage({ name }: { name: string }) {
  return (
    <main>
      <h1>{name}</h1>
      <QuantityPicker />
    </main>
  );
}

```

Brix server-renders the complete HTML, serializes supported island props, and hydrates the marked boundary in the browser.

## Choose hydration timing

The directive controls scheduling:

- `"use island"` hydrates immediately
- `"use island:idle"` schedules hydration after immediate work, yielding between modules
- `"use island:lazy"` hydrates each boundary as it enters the viewport

Immediate modules retain encounter order and run in short, time-bounded batches. Brix checks its budget between modules, yielding through `scheduler.yield()` or a timer fallback rather than waiting for an idle callback. A single large island or repeated-instance module is not interrupted mid-hydration.

Idle work uses `scheduler.yield()` when available, then an idle callback or timer fallback. Keep important controls immediate and explicitly defer nonessential independent islands; Brix does not automatically reorder them by viewport or business priority.

For example, a below-the-fold control can defer hydration:

```tsx
"use island:lazy";

export function ReviewFilters() {
  return <button type="button">Filter reviews</button>;
}

```

Choose timing from actual interaction needs. Content that never becomes interactive should remain an ordinary component.

## Understand nested islands

When one island renders another island normally, the outer island owns that interactive subtree. Brix emits and hydrates only the outer boundary; the inner directive does not create a second hydration boundary.

An island passed through server-owned JSX slot content remains independent. Prefer straightforward composition and avoid wrappers created only to force another boundary.

## Keep boundaries safe

Island modules are browser code. Do not import `#brix/server`, `brix/server`, environment secrets, databases, or other server-only modules. Move data loading into a [page route](/docs/pages) or call a typed [API route](/docs/api-routes).

Props cross a serialization boundary. Pass data rather than request contexts, services, or functions. Brix's generated client and island manifest own hydration startup; application code does not call `init` manually.

## Verify interaction

Use browser component tests for event handling and route harness tests for server rendering:

```sh
brix test --project browser
brix test --project node
brix build

```

See [testing](/docs/testing) for the distinction between browser tests, route tests, and HTTP contract mocks.
