Sapling Islands
The sapling-island
web component is a lightweight solution for implementing islands architecture in your Sapling applications. It allows you to progressively hydrate parts of your page, loading JavaScript and styles only when needed.
Installation
You can enable Sapling Islands in two ways:
1. Layout Configuration (Recommended)
The simplest way is to enable islands in your Layout configuration:
export default async function Layout(props: LayoutProps) {
return await SaplingLayout({
enableIslands: true,
// ... other layout options
});
}
This will automatically add the necessary script and CSS to your page.
2. Manual Installation
Alternatively, you can manually add the required resources. Add the Sapling Islands script to your page via CDN:
<script type="module" src="https://sapling-is.land"></script>
Add the following CSS to ensure proper rendering of the content inside the islands:
sapling-island {
display: contents;
}
There may be some cases where you don't want display: contents
; however, in most cases it's a good idea to use it.
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 can significantly improve 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:
<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 -->
<sapling-island>
<template>
<script src="/scripts/immediate.js" type="module"></script>
</template>
</sapling-island>
<!-- Explicitly set to load -->
<sapling-island loading="load">
<template>
<script src="/scripts/immediate.js" type="module"></script>
</template>
</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>
</sapling-island>
Idle
Loads when the browser is idle:
<sapling-island loading="idle">
<template>
<script src="/scripts/low-priority.js" type="module"></script>
</template>
</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>
</sapling-island>
Hydration State
When an island is hydrated, it receives a hydrated
attribute that you can use for styling:
sapling-island[hydrated] {
/* Styles for hydrated state */
}
Timeout Option
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:
<sapling-island loading="visible" timeout="5000">
<template>
<script src="/scripts/important.js" type="module"></script>
</template>
</sapling-island>
In the example above, the island will hydrate either when it becomes visible OR after 5 seconds, whichever happens first.
Events
The component dispatches a island:hydrated
event when hydration is complete:
document.querySelector('sapling-island').addEventListener('island:hydrated', () => {
console.log('Island has been hydrated');
});
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</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
- Use
Example: Interactive Time Display
Here's a complete example of using sapling-island
for a dynamic 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>
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
whenrequestIdleCallback
is not available - Supports all modern browsers that implement Custom Elements v1
For more examples and use cases, check out our Examples section.