Skip to main content

Partial hydration

Slinkity lets you control if and when your components use client-side JavaScript. Let's understand the options available.

Slinkity does not render your component client-side by default. In other words, your component will be rendered server-side, but your React useState, Vue refs, and Svelte stores will not work until you opt-in to JS. This keeps your client-side bundle as lean as possible.

You'll need to pass a client: directive to opt-in to hydration.

<!--server-render with hydration-->
{% island 'Component.vue', 'client:load' %}{% endisland %}

<!--client-side rendering only-->
{% clientOnlyIsland 'Component.vue', 'client:load' %}{% endclientOnlyIsland %}

More on component shortcodes →

// about.jsx
import { useState } from 'react'

export const island = {
when: 'client:load',
}

export default function About() {
const [count, setCount] = useState(0)

return (
<>
<p>You've had {count} glasses of water 💧</p>
<button onClick={() => setCount(count + 1)}>Add one</button>
</>
)
}

More on component pages →

Hydration isn't just an on-off switch. Using Astro's hydration modes as a guide, we've added more granular options to choose when your component's JS is loaded. All of these options are available for shortcodes and component pages.

This mirrors how "traditional" component-based frameworks operate. Components using any of these values will be rendered client-side as soon as possible.

<!--server-render with hydration-->
{% island 'Component.vue', 'client:load' %}{% endisland %}

<!--client-side rendering only-->
{% clientOnlyIsland 'Component.vue', 'client:load' %}{% endclientOnlyIsland %}

Whenever you visit /page-with-shortcode, we'll import the component library (Vue in this case) and hydrate Component.vue as soon as the page is done parsing (see MDN's docs on scripts with type="module"). This ensures you component is interactive as soon as possible.

Similar to lazy-loading images, client:visible will only hydrate your component when it is scrolled into view. This uses the Intersection Observer API to figure out whether your component is on the page. Once it is, Slinkity will import the necessary dependencies to hydrate your component.

Note: We still import Slinkity-specific scripts ahead of time using module preloading. This is the bare minimum JS to hydrate your component using the Intersection Observer API.

<!--server-render with hydration-->
{% island 'Component.vue', 'client:visible' %}{% endisland %}

<!--client-side rendering only-->
{% clientOnlyIsland 'Component.vue', 'client:visible' %}{% endclientOnlyIsland %}

client:idle will load your component client-side as soon as the browser's main thread is free. This de-prioritizes your component and allows other JS resources on your page to take precedence.

This relies on the window.requestIdleCallback() method in supported browsers, and sets an arbitrary 200ms timeout for unsupported browsers.

<!--server-render with hydration-->
{% island 'Component.vue', 'client:idle' %}{% endisland %}

<!--client-side rendering only-->
{% clientOnlyIsland 'Component.vue', 'client:idle' %}{% endclientOnlyIsland %}

client:media loads your component client-side when a media query is met. This is helpful for components that only need interactivity at certain screen sizes (ex. a slide-out navigation for mobile devices). This relies on the window.matchMedia() method.

<!--server-render with hydration-->
{% island 'Component.vue', 'client:media="screen (max-width: 400px)"' %}{% endisland %}

<!--client-side rendering only-->
{% clientOnlyIsland 'Component.vue', 'client:media="screen (max-width: 400px)"' %}{% endclientOnlyIsland %}

Don't forget wrapper quotes "..." around your media query!

You may want to hydrate your component once multiple client: directives are met. To do so, pass multiple client: directives to the island shortcode.

This example will only hydrate at screen sizes below 400px and when the component is visible on-screen:

<!--server-render with hydration-->
{% island 'Component.vue', 'client:visible', 'client:media="screen (max-width: 400px)"' %}{% endisland %}

<!--client-side rendering only-->
{% clientOnlyIsland 'Component.vue', 'client:visible', 'client:media="screen (max-width: 400px)"' %}{% endclientOnlyIsland %}