lazy-hydration

Defers custom element JavaScript out of the initial bundle until the element nears the viewport.

Updated Sep 6, 2026
One-click install
npx skills add https://github.com/malikkotb/shellpluscore --skill lazy-hydration-malikkotb
Or copy as Structured Prompt for Agent
Please help me install this Agent Skill.
Skill: lazy-hydration
Source: https://github.com/malikkotb/shellpluscore/tree/main/.agents/skills/lazy-hydration
Command: npx skills add https://github.com/malikkotb/shellpluscore --skill lazy-hydration-malikkotb

SYSTEM DOCUMENTATION & REQUIREMENTS

What problem does it solve? Vanilla custom elements in Astro pages bundle their JavaScript into the page's entry chunk and run it on load, even for interaction-gated UI like modals, dropdowns, and media players. This bloats the initial JavaScript of public pages, and Astro's client:* directives do not apply to vanilla custom elements. ## Core Features & Use Cases - Code-split hydration: Replaces eager import "./NameElement" with lazyCustomElement(hostSelector, () => import("./NameElement")) from src/lib/lazy-hydrate.ts, splitting the element and heavy dependencies like GSAP or Mux into a separate chunk. - Visibility-based loading: Uses an IntersectionObserver with a 256px rootMargin so the module loads before a tap lands, which is touch-safe unlike hover intent. - View-transition support: Re-observes on astro:page-load so deferred elements keep working across navigations without extra wiring. - Use Case: Defer the @mux/mux-player bundle in SanityMuxVideo.astro so the heaviest media dependency loads only when the video approaches the viewport. ## Quick Start Ask the AI to convert a component's eager custom element import into a lazyCustomElement call in its .astro script so the module loads only when its trigger nears the viewport.

Frequently Asked Questions about lazy-hydration

High-intent search queries and answers about installing and using this skill.

FAQPage Schema
How do I lazy load a custom element in Astro?

Replace the eager `import "./NameElement"` in the component's .astro script with `lazyCustomElement("name-tag", () => import("./NameElement"))` from src/lib/lazy-hydrate.ts. The dynamic import code-splits the element into its own chunk, loaded by an IntersectionObserver when the host nears the viewport.

Why don't Astro client:visible directives work on custom elements?

Astro's client:* directives only apply to UI-framework components like React, Vue, Svelte, or Lit that use a renderer. Vanilla custom elements have no renderer, so hydration must be handled manually with a helper like lazyCustomElement.

When should I not defer a custom element's JavaScript?

Load eagerly when behavior must be live before any interaction: above-the-fold reveal animations, always-running marquees, smooth scroll, page transitions, and sticky chrome. Deferring these would leave them unarmed at first paint.

Why use IntersectionObserver instead of hover intent for hydration?

Hover intent fails on touch devices where there is no hover, and pointerdown races the first tap. Observing visibility with a 256px rootMargin guarantees the module is loaded before a tap can land on the trigger.

Does lazy hydration still work after Astro view transitions?

Yes. The lazyCustomElement helper re-observes on astro:page-load, so deferred elements keep working across view-transition navigations even though a bundled module script executes only once per session. Do not add your own astro:page-load wiring.