Menu
- Why Eleventy?
- Get Started
- Community
- Working with Templates
- Using Data
- Configuration
- Template Languages
- Plugins
- API Services
- Release History
- Advanced
5.81s
40.14s
Partial Hydration
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.
- Check out
11ty/is-land
on GitHub - View the demos
- Learn more about Islands Architecture
Features:
- Easy to add to existing components
- Zero dependencies
- Small footprint (4.56 kB minimized; 1.47 kB with Brotli compression)
- 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 Jump to heading
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 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:
on:visible
on:idle
on:interaction
(defaults totouchstart,click
)- Change events with
on:interaction="mouseenter,focusin"
- Change events with
on:media
- Viewport size:
on:media="(min-width: 64em)"
- Reduced motion:
- Opt-in with
on:media="(prefers-reduced-motion)"
- Opt-out with
on:media="(prefers-reduced-motion: no-preference)"
- Opt-in with
- Viewport size:
- Save Data (read about Save Data on MDN)
- Opt-in with
on:save-data
- Opt-out with
on:save-data="false"
- Opt-in with
<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
- Small library (8K)
- Rendering modes: Client
- Progressive-enhancement friendly (full control of fallback content)
- Support for
autoinit
<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
- Larger library (51 kB)
- Rendering modes: Client-only, Server-only, Server + Client (Hydration)
- Support for
autoinit
<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
- Smaller library (12 kB)
- Rendering modes: Client-only, Server-only, Server + Client (Hydration)
- Requires a compiler for both client and server modes (tighter server coupling)
- Support for
autoinit
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
- Very small library (~5 kB)
- Rendering modes: Client-only, Server-only, Server + Client (Hydration)
- Support for
autoinit
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
- Small library (~7 kB)
- Rendering modes: Client-only, Server + Client (Hydration)
- Note: Server-only is not supported: it requires Declarative Shadow DOM support to work without JS.
- No support for
autoinit
<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
- Smaller library (15 kB)
- Rendering modes: Client-only
- No
autoinit
but it is not needed (functionality included for-free by Alpine.js)
<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>