Islands: Triggers

Hydration Triggers

Control exactly when your islands become interactive.

Available Triggers

Luna provides four hydration triggers:

TriggerWhenUse Case
LoadImmediately on page loadCritical UI, above-fold content
IdleWhen browser is idleNon-critical features
VisibleWhen element enters viewportBelow-fold content
Media(query)When media query matchesResponsive features

Load Trigger

Hydrate immediately when the page loads:

using @server_dom { island }
using @luna { Load }

island(
  id="search",
  url="/static/search.js",
  trigger=Load,
  children=[...],
)

Output:

<div luna:id="search" luna:client-trigger="load">...</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):

using @server_dom { island }
using @luna { Idle }

island(
  id="analytics",
  url="/static/analytics.js",
  trigger=Idle,
  children=[...],
)

Output:

<div luna:id="analytics" luna:client-trigger="idle">...</div>

Use for:

  • Analytics tracking

  • Non-essential widgets

  • Background features

  • Low-priority interactions

Visible Trigger

Hydrate when the element scrolls into view (using IntersectionObserver):

using @server_dom { island }
using @luna { Visible }

island(
  id="comments",
  url="/static/comments.js",
  trigger=Visible,
  children=[...],
)

Output:

<div luna:id="comments" luna:client-trigger="visible">...</div>

Use for:

  • Comments sections

  • Image galleries

  • Infinite scroll

  • Footer widgets

  • Any below-fold content

Media Trigger

Hydrate when a media query matches:

using @server_dom { island }
using @luna { Media }

island(
  id="sidebar",
  url="/static/sidebar.js",
  trigger=Media("(min-width: 768px)"),
  children=[...],
)

Output:

<div luna:id="sidebar" luna:client-trigger="media:(min-width: 768px)">...</div>

Use for:

  • Desktop-only features

  • Mobile-specific components

  • Responsive interactions

  • Orientation-dependent UI

Media Query Examples

QueryUse Case
(min-width: 768px)Desktop only
(max-width: 767px)Mobile only
(prefers-color-scheme: dark)Dark mode
(prefers-reduced-motion: no-preference)Animations
(orientation: landscape)Landscape

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

TriggerInitial LoadLCP ImpactTTI Impact
LoadHeavyNoneDelayed
IdleLightNoneMinimal
VisibleNoneNoneNone
MediaConditionalNoneMinimal

Combining Strategies

A typical page might use all triggers:

using @server_dom { div }
using @luna { Load, Idle, Visible, Media }

fn blog_page() -> @luna.Node {
  div([
    // Immediate - critical for UX
    search_island(trigger=Load),

    // Idle - nice to have but not urgent
    theme_toggle_island(trigger=Idle),

    // Static article content - no JS
    article_content(),

    // Visible - only load when user scrolls down
    comments_island(trigger=Visible),

    // Media - only on desktop
    sidebar_widget(trigger=Media("(min-width: 1024px)")),
  ])
}

Manual Trigger (None)

For programmatic control:

using @server_dom { island }
using @luna { None }

island(
  id="modal",
  url="/static/modal.js",
  trigger=None,
  children=[...],
)

Output:

<div luna:id="modal" luna:client-trigger="none">...</div>

Trigger from JavaScript:

window.__LUNA_HYDRATE__?.("modal");

Use for:

  • Modals opened by user action

  • Lazy-loaded features

  • On-demand functionality

Try It

Assign triggers to these components:

  1. Site-wide search box

  2. Cookie consent banner

  3. Image lightbox

  4. Live chat widget

  5. Mobile hamburger menu

Suggested Answers
  1. Search box โ†’ Load (critical, above fold)

  2. Cookie consent โ†’ Idle (not critical, can wait)

  3. Image lightbox โ†’ Visible or None (only when viewing images)

  4. Live chat โ†’ Idle (background feature)

  5. Mobile menu โ†’ Media("(max-width: 767px)") (mobile only)

Next

Learn about Web Components Islands โ†’