npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

sapling-island

v0.2.3

Published

A lightweight Web Component for lazy loading content with multiple loading strategies

Readme

Sapling Island

A lightweight Web Component for implementing islands architecture in your web applications. Islands architecture allows you to progressively hydrate parts of your page, loading JavaScript and styles only when needed for optimal performance.

Installation

NPM

npm install sapling-island
import "sapling-island";
// or
import SaplingIsland from "sapling-island";

CDN (jsDelivr)

<script
  type="module"
  src="https://cdn.jsdelivr.net/npm/[email protected]"
></script>

Recommended CSS

Add this CSS to ensure proper rendering:

sapling-island {
  display: contents;
}

/* Optional: Style hydrated islands */
sapling-island[hydrated] {
  /* Styles for hydrated state */
}

Overview

Islands architecture is a pattern where most of your page remains static HTML, with interactive "islands" that get hydrated with JavaScript when needed. This approach significantly improves initial page load performance by deferring the loading of non-critical JavaScript.

Basic Usage

Wrap any content that requires JavaScript or additional styles in a <sapling-island> component. Place scripts and styles inside the <template> element to prevent them from loading until needed:

<sapling-island>
  <template>
    <script src="/scripts/interactive.js" type="module"></script>
    <style>
      /* Styles that are only needed for this interactive component */
    </style>
  </template>
  <div>Your interactive content here</div>
</sapling-island>

Loading Strategies

The component supports several loading strategies through the loading attribute:

Load (Default)

If no loading attribute is specified, or when set to "load", the island loads immediately when the page loads:

<!-- Without loading attribute (default) -->
<sapling-island>
  <template>
    <script src="/scripts/immediate.js" type="module"></script>
  </template>
  <div>Content that loads immediately</div>
</sapling-island>

<!-- Explicitly set to load -->
<sapling-island loading="load">
  <template>
    <script src="/scripts/immediate.js" type="module"></script>
  </template>
  <div>Content that loads immediately</div>
</sapling-island>

Visible

Loads when the component becomes visible in the viewport:

<sapling-island loading="visible">
  <template>
    <script src="/scripts/lazy.js" type="module"></script>
  </template>
  <div>This content loads when it becomes visible</div>
</sapling-island>

Idle

Loads when the browser is idle:

<sapling-island loading="idle">
  <template>
    <script src="/scripts/low-priority.js" type="module"></script>
  </template>
  <div>This content loads when the browser is idle</div>
</sapling-island>

Media Query

Loads when a media query condition is met:

<sapling-island loading="(min-width: 768px)">
  <template>
    <script src="/scripts/desktop-only.js" type="module"></script>
  </template>
  <div>This content loads only on desktop screens</div>
</sapling-island>

<sapling-island loading="(prefers-reduced-motion: no-preference)">
  <template>
    <script src="/scripts/animation.js" type="module"></script>
  </template>
  <div>This content loads only if user allows motion</div>
</sapling-island>

Timeout Support

You can specify a timeout for loading strategies using the timeout attribute. The island will hydrate when either the loading strategy condition is met OR the timeout is reached, whichever comes first:

<!-- Load when visible, but force load after 5 seconds -->
<sapling-island loading="visible" timeout="5000">
  <template>
    <script src="/scripts/important.js" type="module"></script>
  </template>
  <div>
    This will load when visible or after 5 seconds, whichever comes first
  </div>
</sapling-island>

<!-- Load when idle, but force load after 3 seconds -->
<sapling-island loading="idle" timeout="3000">
  <template>
    <script src="/scripts/feature.js" type="module"></script>
  </template>
  <div>This will load when idle or after 3 seconds</div>
</sapling-island>

Events

The component dispatches a custom island:hydrated event when hydration is complete:

document.querySelector("sapling-island").addEventListener(
  "island:hydrated",
  () => {
    console.log("Island has been hydrated");
  },
);

// Or listen globally
document.addEventListener("island:hydrated", (event) => {
  console.log("Island hydrated:", event.target);
});

Best Practices

Template Content Only

Place <script> and <style> tags inside the <template> element to prevent them from loading until needed:

<sapling-island loading="visible">
  <template>
    <!-- Scripts and styles go here -->
    <script src="/scripts/feature.js" type="module"></script>
    <style>
      /* Feature styles */
    </style>
  </template>
  <!-- Actual content goes outside template -->
  <div class="feature">...</div>
</sapling-island>

Progressive Enhancement

Design your islands to enhance existing static content rather than being required for basic functionality:

<!-- Static content works without JavaScript -->
<div class="content">Static content that works without JS</div>

<!-- Interactive features are added through islands -->
<sapling-island loading="visible">
  <template>
    <script src="/scripts/enhance.js" type="module"></script>
  </template>
  <div class="enhancement">Interactive features</div>
</sapling-island>

Performance Considerations

  • Use visible for below-the-fold content
  • Use idle for non-critical enhancements
  • Use media queries for device-specific features
  • Set appropriate timeouts for critical features

Complete Example: Interactive Time Display

<sapling-island loading="visible">
  <template>
    <script>
      const time = document.querySelector("time");
    setInterval(() => {
      time.textContent = new Date().toLocaleTimeString();
    }, 1000);
    </script>
  </template>
  <div class="content">
    <p>The time updates every second after hydration:</p>
    <p>Current time: <time>00:00</time></p>
  </div>
</sapling-island>

Attributes Reference

  • loading - Loading strategy: "load" (default), "visible", "idle", or any CSS media query
  • timeout - Maximum wait time in milliseconds before forcing load
  • hydrated - Added automatically when content is loaded (useful for CSS styling)

Browser Support

The sapling-island component uses standard web APIs and includes fallbacks for broader browser support:

  • Uses IntersectionObserver for visibility detection
  • Falls back to setTimeout when requestIdleCallback is not available
  • Supports all modern browsers that implement Custom Elements v1
  • Modern browsers with ES6 module support

Why Sapling Island?

  • Zero dependencies - Pure vanilla JavaScript
  • Tiny footprint - Under 5KB
  • Framework agnostic - Works with any framework or vanilla HTML
  • Progressive enhancement - Graceful fallbacks for older browsers
  • Flexible loading strategies - Choose what works best for your content
  • Islands architecture - Improve performance with selective hydration

You can read more about advanced usage in the sapling docs.

License

MIT License