<is-land> for Islands Architecture
This documentation is for <is-land> v5.0.0 and newer.
A plugin to smartly and efficiently load and initialize client-side components to your web site.
Or, more technically: a framework independent partial hydration islands architecture implementation.
- Check out
11ty/is-landon GitHub - View the demos
- Learn more about Islands Architecture
Features:
- Easy to add to existing components
- Zero dependencies
- Small footprint (1.83 kB compressed)
- Not tightly coupled to a server framework or site generator tool.
- Server-rendered (SSR) component examples available for SSR-friendly frameworks (Lit, Svelte, Vue, Preact are provided)
Installation
npm install @11ty/is-land
<is-land> via WebC.
Add is-land.js to your primary bundle. It can be deferred and/or loaded asynchronously.
When using with web components it must be defined before any other custom elements are defined (via customElements.define) on the page.
<script type="module" src="/is-land.js"></script>
Or you can use import "/is-land.js";.
Usage
<is-land>This is an island.</is-land>
Add any number of loading conditions to this tag to control how and when the island is initialized. You can mix and match. All conditions must be satisfied to initialize.
on:visibleon:load(new in v5)on:idleon:interaction(defaults totouchstart,click)- Change events with
on:interaction="mouseenter,focusin"
- Change events with
on:media- When Viewport size matches:
on:media="(min-width: 64em)" - Reduced motion:
- When user prefers reduced motion
on:media="(prefers-reduced-motion)" - When user has no preference on motion
on:media="(prefers-reduced-motion: no-preference)"
- When user prefers reduced motion
- When Viewport size matches:
- Save Data (read about Save Data on MDN)
- When Save Data is active
on:save-data - When Save Data is inactive
on:save-data="false"
- When Save Data is active
<is-land on:visible on:idle>
<!-- your HTML here -->
<is-land on:media="(min-width: 64em)">
<!-- Islands can be nested -->
<!-- Islands inherit all of their parents’ loading conditions -->
</is-land>
</is-land>
Controlling Fallback Content
Pre-JS
<is-land on:visible on:idle>
<vanilla-web-component>
Put your pre-JS fallback content in your web component.
</vanilla-web-component>
</is-land>
Post-JS with <template>
Place any post-JS content inside of one or more <template data-island> elements anywhere in the <is-land>. These will be swapped with their template content. You can nest an <is-land> in there if you want!
<is-land on:visible on:idle>
<template data-island>
<vanilla-web-component>
This component is post-JS.
</vanilla-web-component>
</template>
</is-land>
- Use
data-island="replace"to replace the contents of the<is-land>with the template. - Use
data-island="once"to run a template’s contents once per page (keyed from template contents). (New in v2.0.1)
Run your own custom JavaScript, load your own CSS
Embed a script inside the template to run custom JS when the island’s loading conditions have been satisfied!
<is-land on:visible>
<template data-island>
<!-- CSS -->
<style>
/* My custom CSS */
</style>
<link rel="stylesheet" href="my-css-file.css" />
<!-- JS -->
<script type="module">
console.log("Hydrating!");
</script>
<script type="module" src="my-js-file.js"></script>
</template>
</is-land>
You can also use the ready attribute for styling, added to the <is-land> when the island has been hydrated.
<style>
is-land[ready] {
background-color: lightgreen;
}
</style>
Framework Component Support
type: initialize a framework initialization type, registered by you. Examples included for:alpine,petite-vue,vue,vue-ssr,preact,preact-ssr,svelte, orsvelte-ssr.
Demos, examples, and source code are available for each framework listed here.
Petite Vue
- Examples
- Small library (~9K)
- Rendering modes: Client
- Progressive-enhancement friendly (full control of fallback content)
<script type="module">
// Define once for any number of Petite Vue islands.
Island.addInitType("petite-vue", async (target) => {
const { createApp } = await import("https://unpkg.com/petite-vue@0.4.1/dist/petite-vue.es.js");
createApp().mount(target);
});
</script>
<is-land on:visible type="petite-vue" v-scope="{ name: 'Vue' }">
Hello from <span v-html="name">HTML</span>
</is-land>
Vue
- Examples
- Larger library (~73 kB)
- Rendering modes: Client (shown), Server, Server + Client (Hydration)
<script type="module">
// Define once for any number of Vue islands.
Island.addInitType("vue", async (target) => {
const { createApp } = await import("https://unpkg.com/vue@3.5.13/dist/vue.esm-browser.js");
createApp({
data: () => (target.dataset), // use <is-land data-> attributes as component data
}).mount(target);
});
</script>
<is-land on:visible type="vue" data-name="Vue">
Hello from <span v-text="name"></span>
</is-land>
Svelte
- Examples (using Import Maps)
- Medium-sized library
- Rendering modes: Client, Server, Server + Client (Hydration)
- Requires a compiler for client mode (uncommon)
<script type="module">
// Define once for any number of Svelte islands.
Island.addInitType("svelte", async (target) => {
// requires an Import map and svelte is lazy loaded when island is ready
const { mount } = await import("svelte");
const component = await import(target.getAttribute("import"));
mount(component.default, {
target: target,
props: {},
});
});
</script>
<!-- This example uses an Eleventy `svelte` Universal Filter (see SveltePlugin.cjs) -->
{% assign component = "./lib/svelte/my-component-js-only.svelte" | svelte %}
<is-land on:visible type="svelte" import="{{ component.clientJsUrl }}"></is-land>
Show sample Import Map
<!-- importmap from https://generator.jspm.io/ -->
<script type="importmap">
{
"imports": {
"svelte": "https://unpkg.com/svelte@5.19.10/src/index-client.js",
"svelte/internal/client": "https://unpkg.com/svelte@5.19.10/src/internal/client/index.js",
"svelte/internal/flags/legacy": "https://unpkg.com/svelte@5.19.10/src/internal/flags/legacy.js"
},
"scopes": {
"https://unpkg.com/": {
"clsx": "https://unpkg.com/clsx@2.1.1/dist/clsx.mjs",
"esm-env": "https://unpkg.com/esm-env@1.2.2/index.js",
"esm-env/browser": "https://unpkg.com/esm-env@1.2.2/true.js",
"esm-env/development": "https://unpkg.com/esm-env@1.2.2/false.js",
"esm-env/node": "https://unpkg.com/esm-env@1.2.2/false.js"
}
}
}
</script>
Preact
- Examples
- Small library (~9 kB)
- Rendering modes: Client (shown), Server, Server + Client (Hydration)
- No compiler needed when using
htmrather than JSX.
<script type="module">
// Define once for any number of Preact islands.
Island.addInitType("preact", async (target) => {
const component = await import(target.getAttribute("import"));
component.default(target);
});
</script>
<is-land on:visible type="preact" import="preact-component.js"></is-land>
Example component code for preact-component.js:
import { html, render } from 'https://unpkg.com/htm/preact/index.mjs?module'
function App (props) {
return html`<p><strong>Hello ${props.name}!</strong></p>`;
}
export default function(el) {
render(html`<${App} name="from Preact" />`, el);
}
Lit
- Examples (using Import Maps)
- Small library (~10 kB)
- Rendering modes: Client, Server, Server + Client (Hydration)
<is-land on:visible import="lit-component.js">
<lit-component name="Post-JS">Pre-JS Content</lit-web-component>
</is-land>
Show sample Import Map
<!-- importmap from https://generator.jspm.io/ -->
<script type="importmap">
{
"imports": {
"lit": "https://unpkg.com/lit@3.2.1/index.js"
},
"scopes": {
"https://unpkg.com/": {
"@lit/reactive-element": "https://unpkg.com/@lit/reactive-element@2.0.4/reactive-element.js",
"lit-element/lit-element.js": "https://unpkg.com/lit-element@4.1.1/lit-element.js",
"lit-html": "https://unpkg.com/lit-html@3.2.1/lit-html.js",
"lit-html/is-server.js": "https://unpkg.com/lit-html@3.2.1/is-server.js"
}
}
}
</script>
Example component code lit-component.js:
import {html, css, LitElement} from "lit";
customElements.define('lit-component', class extends LitElement {
static properties = {
name: {type: String},
};
render() {
return html`<p>Hello, ${this.name || "Stranger"}!</p>`;
}
});
Alpine.js
- Examples
- Smaller library (~20 kB)
- Rendering modes: Client
- Progressive-enhancement friendly (control fallback content)
<script type="module">
// Define once for any number of Alpine islands.
Island.addInitType("alpine", async (target) => {
await import("https://unpkg.com/alpinejs@3.14.8/dist/cdn.min.js");
});
// Workaround for Alpine global mount
Island.addFallback("[x-data]", (node) => {
if(node.hasAttribute("x-ignore")) {
return;
}
node.setAttribute("x-ignore", "");
return () => {
node.removeAttribute("x-ignore");
if(Alpine) {
Alpine.nextTick(() => Alpine.initTree(node));
}
};
});
</script>
<is-land on:visible type="alpine">
<div x-data="{ name: 'Alpine.js' }">
Hello from <span x-text="name">HTML</span>!
</div>
</is-land>
Solid.js
- Examples (using Import Maps)
- Medium library (~40 kB)
- Rendering modes: Client