Skip to main content

Partial hydration

Slinkity lets you control if and when your JS-laden components are hydrated. Let's understand the options available.

We do 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 lets you use components for templating guilt free, with the option to hydrate when the need arises.

You'll need to pass the hydrate prop to opt-in to hydration. We'll use "eager" as an example here. Jump to the next section for a full list of hydration modes.

<!--server-render and hydrate client-side-->
{% component 'Component.vue', hydrate=true %}
<!--don't server-render and *only* render client-side-->
{% component 'Component.vue', renderWithoutSSR=true %}

If you prefer the syntax available in nunjucks templates, we do too! We recommend configuring nunjucks as the default templating language for HTML and markdown files to access this syntax everywhere. Head to our configuration docs for more details.

More on component shortcodes →

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

export const frontMatter = {
hydrate: true,
}

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>
</>
)
}

To render only client-side without server rendering, try switching from the hydrate prop to renderWithoutSSR. We only recommend this for components that 100% can't be rendered server-side:

frontMatter: {
- hydrate: true / "onClientIdle" / "onClientMedia(...)"
+ renderWithoutSSR: true / "onClientIdle" / "onClientMedia(...)"
}

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.

Usage

<!-- page-with-shortcode.njk -->

<body>
<!--server-render and hydrate client-side-->
{% component 'Example.jsx', hydrate=true %}
{% component 'Example.jsx', hydrate='eager' %}
{% component 'Example.jsx', hydrate='onClientLoad' %}
<!--don't server-render and *only* render client-side-->
{% component 'Example.jsx', renderWithoutSSR=true %}
{% component 'Example.jsx', renderWithoutSSR='eager' %}
{% component 'Example.jsx', renderWithoutSSR='onClientLoad' %}
</body>

Whenever you visit /page-with-shortcode, we'll import the component library (React in this case) and your Example.jsx's JS bundle 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, we only load your component's JS when your component is scrolled into view. This uses the Intersection Observer API to figure out whether your component is on the page. Once it is, we quickly grab the necessary dependencies to hydrate your component.

Note: We still import Slinkity-specific scripts ahead of time using module preloading. Don't worry, it's a very small bundle! We just use this to mount your component once it scrolls into view.

Usage


<body>
<!--server-render and hydrate client-side-->
{% component 'Example.jsx', hydrate='onComponentVisible' %}
{% component 'Example.jsx', hydrate='lazy' %}
<!--don't server-render and *only* render client-side-->
{% component 'Example.jsx', renderWithoutSSR='onComponentVisible' %}
{% component 'Example.jsx', renderWithoutSSR='lazy' %}
</body>

Here, we load your component client-side as soon as the browser's main thread has calmed down. 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.

Usage


<body>
<!--server-render and hydrate client-side-->
{% component 'Example.jsx', hydrate='onClientIdle' %}
<!--don't server-render and *only* render client-side-->
{% component 'Example.jsx', renderWithoutSSR='onClientIdle' %}
</body>

This helper 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.

Usage


<body>
<!--server-render and hydrate client-side-->
{% component 'SlideOutMenu.vue', hydrate='onClientMedia(screen (max-width: 400px))' %}
<!--don't server-render and *only* render client-side-->
{% component 'SlideOutMenu.vue', renderWithoutSSR='onClientMedia(screen (max-width: 400px))' %}
</body>

Note: don't forget those extra parens (...) when writing your query! You may expect a value like onClientMedia(max-width: 300px) to work, but you'll need an extra wrapper for the media query itself: onClientMedia((max-width: 300px)).

This is the default for component pages and shortcodes. Non-hydrated components are rendered to HTML at build time, but no JavaScript is shipped to the client. This means no interactivity, no state management, nothing. Use this option if you want to use component languages like React as a templating language alone.