The Equal Justice Initiative
Eleventy
The possum is Eleventy’s mascot
Eleventy Documentation
Menu
Eleventy 5.81s
Remix 40.14s

Partial Hydration

Contents

A plugin to smartly and efficiently add client-side components to your web site.

Or, more technically: a framework independent partial hydration islands architecture implementation.

Features:

Installation Jump to heading

npm install @11ty/is-land
INFO:
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 the first custom element defined (via customElements.define) on the page. Choose your style:

<script type="module" src="/is-land.js"></script>
<script type="module">
import "/is-land.js";
</script>

Usage Jump to heading

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

<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 Jump to heading

Pre-JS Jump to heading

<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> Jump to heading

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>

Run your own custom JavaScript, load your own CSS Jump to heading

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 Support Jump to heading

Demos and source in the HTML are available for each framework listed here.

autoinit Jump to heading

Use the autoinit and import attributes together to import a third party library (or component code). autoinit can be one of petite-vue, vue, preact, or svelte. It is recommended to use a self-hosted framework library (future Eleventy integrations will automate this for you).

<is-land on:visible autoinit="petite-vue" import="https://unpkg.com/petite-vue@0.4.1/dist/petite-vue.es.js" v-scope="{ name: 'post-JS' }">
Hello from <span v-html="name">pre-JS</span>
</is-land>

<!-- when import maps support is better, this simplifies with an entry for petite-vue in your import map -->
<is-land on:visible import="petite-vue" v-scope="{ name: 'post-JS' }">
Hello from <span v-html="name">pre-JS</span>
</is-land>

Petite Vue Jump to heading

<is-land on:visible autoinit="petite-vue" import="https://unpkg.com/petite-vue@0.4.1/dist/petite-vue.es.js" v-scope="{ name: 'post-JS' }">
Hello from <span v-html="name">pre-JS</span>
</is-land>

Vue Jump to heading

<is-land on:visible>
<div id="vue-app">
Hello from <span v-html="name">pre-JS</span>
</div>

<script type="module/island">
import { createApp } from "https://unpkg.com/vue@3.2.36/dist/vue.esm-browser.prod.js";

createApp({
data: () => ({ name: "post-JS" })
}).mount("#vue-app")
</script>
</is-land>

Svelte Jump to heading

This example uses an Eleventy/Svelte integration to compile a Svelte component.

{% assign component = "./lib/svelte/my-component.svelte" | svelte %}
<is-land on:visible autoinit="svelte" import="{{ component.clientJsUrl }}"></is-land>
Example component code ./lib/svelte/my-component.svelte:
<script>
// using export to allow overrides via props
export let name = 'world';

let count = 0;

function handleClick() {
count += 1;
}
</script>

<style>
h1 { color: red }
</style>

<h1>Hello {name}</h1>

<button on:click={handleClick}>
Clicked {count} {count === 1 ? 'time' : 'times'}
</button>

Preact Jump to heading

This example uses htm instead of JSX.

<is-land on:visible autoinit="preact" import="/lib/preact/preact-component.js"></is-land>
Example component code ./lib/preact/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 Jump to heading

<is-land on:visible import="./lib/lit/lit-component.js">
<lit-component name="Post-JS">Pre-JS Content</lit-web-component>
</is-land>
Example component code ./lib/lit/lit-component.js:
import {html, css, LitElement} from "https://cdn.jsdelivr.net/gh/lit/dist@2/core/lit-core.min.js";

customElements.define('lit-component', class extends LitElement {
static properties = {
name: {type: String},
};

render() {
return html`<p>Hello, ${this.name || "Stranger"}!</p>`;
}
});

Alpine.js Jump to heading

<is-land on:visible import="https://unpkg.com/alpinejs">
<div x-data="{ count: 0 }">
Hello from Alpine.js!

<button @click="count++">⬆️</button> <button @click="count--">⬇️</button> <span x-text="count"></span>
</div>
</is-land>

Other pages in Plugins: