Control when specific components load their JavaScript. Wrap any component in <nk-island> to defer its module loading based on a hydration strategy.
LumenJS loads and hydrates all components on a page by default. When you wrap a component in <nk-island>, that component's module is no longer loaded with the rest of the page. Instead, <nk-island> controls when the module is loaded via a hydration strategy (on page load, on scroll, when idle, or on media query match). Components outside of <nk-island> continue to load and hydrate normally.
<nk-island> anywhere in your templates.Wrap any interactive component in <nk-island> with a hydration strategy and an import attribute pointing to the component module:
<nk-island client:load import="./components/counter.ts"> <my-counter count="5"></my-counter> </nk-island>
The import attribute tells the island which module to load. The module should define and register the custom element (via customElements.define()).
Choose when the island loads its JavaScript:
| Strategy | Loads when... | Best for |
|---|---|---|
client:load | Page loads (immediate) | Above-the-fold interactive content |
client:visible | Scrolled into viewport | Below-the-fold content |
client:idle | Browser is idle | Low-priority interactivity |
client:media | Media query matches | Device-specific components |
Loads the module immediately when the page loads. Use for interactive content visible on first paint.
<nk-island client:load import="./components/hero-carousel.ts"> <hero-carousel></hero-carousel> </nk-island>
Defers loading until the element scrolls into the viewport (uses IntersectionObserver with 200px margin). Great for content below the fold.
<nk-island client:visible import="./components/chart.ts"> <data-chart .data=${this.chartData}></data-chart> </nk-island>
Loads when the browser is idle (uses requestIdleCallback). Good for components that should hydrate soon but are not critical for first interaction.
<nk-island client:idle import="./components/comments.ts"> <user-comments></user-comments> </nk-island>
Only loads when a CSS media query matches. Perfect for responsive components that only exist at certain screen sizes.
<nk-island client:media="(min-width: 768px)" import="./components/sidebar.ts"> <desktop-sidebar></desktop-sidebar> </nk-island>
Try the different hydration strategies below. Each counter is wrapped in <nk-island>. Click the buttons to verify they are interactive.
Scroll inside this box. The counter hydrates when it enters the viewport:
<nk-island> is a plain HTMLElement (not a LitElement). It does not participate in Lit hydration itself. When the chosen strategy triggers:
import is loaded via dynamic import()customElements.define())data-hydrated attribute is set on the <nk-island> elementisland-hydrated event is dispatched (bubbles + composed)<nk-island> runtime is included on every page. It only activates when <nk-island> elements are present.<nk-island> hydrate normally. Islands only control their own children<nk-island> uses display: contents so it adds no extra box in the layoutdisconnectedCallbackA content-heavy page where the reactions widget's module is deferred until scrolled into view:
import { LitElement, html, css } from 'lit'; export class PageBlog extends LitElement { render() { return html` <!-- Normal components: loaded and hydrated with the page --> <h1>My Blog Post</h1> <p>This is content rendered and hydrated normally...</p> <!-- Island: module loading deferred until visible --> <nk-island client:visible import="/components/reactions.ts"> <post-reactions post-id="42"></post-reactions> </nk-island> <footer>Published 2026</footer> `; } }
import attribute accepts project-root-relative paths (starting with /) or relative paths (starting with ./). Relative paths are resolved from the file where the template is defined.