Hydration Triggers
Control exactly when your islands become interactive.
Available Triggers
Luna provides four hydration triggers:
| Trigger | When | Use Case |
|---|---|---|
load | Immediately on page load | Critical UI, above-fold content |
idle | When browser is idle | Non-critical features |
visible | When element enters viewport | Below-fold content |
media | When media query matches | Responsive features |
Load Trigger
Hydrate immediately when the page loads:
<div luna:id="search" luna:url="/static/search.js" luna:client-trigger="load">
<!-- Server-rendered content -->
</div>
Use for:
Header search boxes
Navigation menus
Critical interactive elements
Above-the-fold content
Idle Trigger
Hydrate when the browser is idle (using requestIdleCallback):
<div luna:id="analytics" luna:url="/static/analytics.js" luna:client-trigger="idle">
<!-- Server-rendered content -->
</div>
Use for:
Analytics tracking
Non-essential widgets
Background features
Low-priority interactions
Visible Trigger
Hydrate when the element scrolls into view (using IntersectionObserver):
<div luna:id="comments" luna:url="/static/comments.js" luna:client-trigger="visible">
<!-- Server-rendered content -->
</div>
Use for:
Comments sections
Image galleries
Infinite scroll
Footer widgets
Any below-fold content
Media Trigger
Hydrate when a media query matches:
<div luna:id="sidebar" luna:url="/static/sidebar.js" luna:client-trigger="media:(min-width: 768px)">
<!-- Server-rendered content -->
</div>
Use for:
Desktop-only features
Mobile-specific components
Responsive interactions
Orientation-dependent UI
Media Query Examples
<!-- Desktop only (768px+) -->
<div luna:client-trigger="media:(min-width: 768px)">...</div>
<!-- Mobile only (under 768px) -->
<div luna:client-trigger="media:(max-width: 767px)">...</div>
<!-- Dark mode preference -->
<div luna:client-trigger="media:(prefers-color-scheme: dark)">...</div>
<!-- Reduced motion preference -->
<div luna:client-trigger="media:(prefers-reduced-motion: no-preference)">...</div>
<!-- Landscape orientation -->
<div luna:client-trigger="media:(orientation: landscape)">...</div>
Choosing the Right Trigger
Decision Flow
Is it above the fold?
โโโ Yes โ Is it critical for initial interaction?
โ โโโ Yes โ load
โ โโโ No โ idle
โโโ No โ Will users always scroll to it?
โโโ Yes โ visible
โโโ No โ Is it device-specific?
โโโ Yes โ media
โโโ No โ visible or idle
Performance Impact
| Trigger | Initial Load | LCP Impact | TTI Impact |
|---|---|---|---|
load | Heavy | None | Delayed |
idle | Light | None | Minimal |
visible | None | None | None |
media | Conditional | None | Minimal |
Combining Strategies
A typical page might use all triggers:
<div>
<!-- Immediate - critical for UX -->
<div luna:id="search" luna:client-trigger="load">...</div>
<!-- Idle - nice to have but not urgent -->
<div luna:id="theme-toggle" luna:client-trigger="idle">...</div>
<!-- Static article content - no JS -->
<article>...</article>
<!-- Visible - only load when user scrolls down -->
<div luna:id="comments" luna:client-trigger="visible">...</div>
<!-- Media - only on desktop -->
<div luna:id="sidebar" luna:client-trigger="media:(min-width: 1024px)">...</div>
</div>
For server-side rendering with MoonBit, see the MoonBit Tutorial.
Manual Trigger (None)
For programmatic control, use none:
<div luna:id="modal" luna:url="/static/modal.js" luna:client-trigger="none">
<!-- Server-rendered modal content -->
</div>
Then trigger from JavaScript:
// Hydrate manually when needed
window.__LUNA_HYDRATE__?.("modal");
Use for:
Modals opened by user action
Lazy-loaded features
On-demand functionality
Monitoring Hydration
Track hydration timing:
// In your island component
hydrate("myComponent", (props) => {
console.log("Hydrated at:", performance.now());
return <MyComponent {...props} />;
});
Try It
Assign triggers to these components:
Site-wide search box
Cookie consent banner
Image lightbox
Live chat widget
Mobile hamburger menu
Suggested Answers
Search box โ
load(critical, above fold)Cookie consent โ
idle(not critical, can wait)Image lightbox โ
visibleornone(only when viewing images)Live chat โ
idle(background feature)Mobile menu โ
media:(max-width: 767px)(mobile only)
Next
Learn about Server-to-Client State โ