Yell supports selective hydration — only interactive regions load JavaScript, everything else stays static.
React hydrates the entire page on the client. Even if 90% of your page is static content, the client must download and execute all the JS for all components.
┌─────────────────────────────────────┐
│ Hero (static text, images) │ ← No JS needed
├─────────────────────────────────────┤
│ Feature Cards (static) │ ← No JS needed
├─────────────────────────────────────┤
│ Interactive Form (buttons, inputs) │ ← JS REQUIRED
└─────────────────────────────────────┘
Full hydration = downloading JS for content that never changes.
Mark interactive regions with hydrate: true:
app:
children:
- type: Hero # Static — rendered as plain HTML
props:
title: "Welcome"
subtitle: "Get started"
- type: Form # Interactive — will be hydrated
hydrate: true
children:
- type: Input
bind:
value: form.email
onChange: form.email = $event
- type: Button
onClick: handleSubmit
SSR marks islands in the HTML:
<!-- Static region -->
<div class="hero">
<h1>Welcome</h1>
</div>
<!-- Island -->
<yell-component id="yell-1" data-yell-hydrate="true" data-yell-type="Form">
<input />
<button>Submit</button>
</yell-component>
The Yell runtime only hydrates nodes marked with data-yell-hydrate="true":
import { hydrateIslands } from '@yell/core';
hydrateIslands({
registry,
hydrationMap,
events: {
handleSubmit: () => console.log('submitted!')
}
});
Static regions remain as plain HTML — no JS executed, no event listeners attached.
Islands can contain other components:
- type: Modal
hydrate: true
slots:
header:
- type: Text
props:
content: "Confirm"
body:
- type: Button
onClick: handleConfirm
Only the root island is hydrated — its children inherit the hydration context.
For nested interactivity, mark each interactive boundary:
- type: Dashboard
hydrate: true # Dashboard island
children:
- type: Chart # Static within island — no extra JS
props:
data: $state.chartData
- type: FilterPanel
hydrate: true # Nested island — has its own interactivity
children:
- type: Select
onChange: handleFilterChange
Not every interactive element needs to be an island. Use islands for:
Don’t mark as islands:
Large islands = more JS to hydrate. Split large interactive regions at natural boundaries (tabs, modals, panels).