Skip to navigation Skip to main content
These docs are for an upcoming version of Build Awesome and are subject to change—take extra care before linking here as URLs may change! Look to the latest stable docs instead (or the full release history).

<is-land> for Islands Architecture

On this page

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.

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
You can skip the next steps if you use <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:visible
  • on:load (new in v5)
  • on:idle
  • on:interaction (defaults to touchstart,click)
    • Change events with on:interaction="mouseenter,focusin"
  • 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)"
  • 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"
<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, or svelte-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 htm rather 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

Other pages in Plugins