<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 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