Coherent.js Hydration Guide
This guide explains how to use Coherent.js's client-side hydration utilities to add interactivity to server-rendered components.
What is Hydration?
Hydration is the process of attaching client-side interactivity to server-rendered HTML. When a component is hydrated, event listeners are attached, state is initialized, and the component becomes fully interactive.
Hydration Utilities
Coherent.js provides several utilities for hydrating components:
hydrate(element, component, props, options)
Hydrates a single DOM element with a Coherent component.
import { hydrate } from '@coherent.js/client';
const element = document.getElementById('my-component');
const instance = hydrate(element, MyComponent, { initialProp: 'value' });
hydrateAll(elements, components, propsArray)
Hydrates multiple elements with their corresponding components.
import { hydrateAll } from '@coherent.js/client';
const elements = [document.getElementById('counter'), document.getElementById('todo-list')];
const components = [Counter, TodoList];
const propsArray = [{ count: 0 }, { todos: [] }];
const instances = hydrateAll(elements, components, propsArray);
hydrateBySelector(selector, component, props)
Finds elements by CSS selector and hydrates them with a component.
import { hydrateBySelector } from '@coherent.js/client';
const instances = hydrateBySelector('.counter', Counter, { count: 0 });
makeHydratable(component)
Marks a component as hydratable and adds metadata for server-side rendering.
import { makeHydratable } from '@coherent.js/client';
const HydratableCounter = makeHydratable(Counter);
Creating Hydratable Components
To make a component hydratable, wrap it with the makeHydratable function:
import { makeHydratable } from '@coherent.js/client';
function Counter(props) {
return {
div: {
className: 'counter',
'data-coherent-component': 'Counter',
children: [
{
span: {
text: `Count: ${props.count}`
}
},
{
button: {
'data-action': 'increment',
text: '+'
}
}
]
}
};
}
const HydratableCounter = makeHydratable(Counter);
Server-Side Rendering
When rendering on the server, use render with hydratable components:
import { render } from '@coherent.js/core';
import { makeHydratable } from '@coherent.js/client';
function Counter(props) {
return {
div: {
className: 'counter',
'data-coherent-component': 'Counter',
children: [
{
span: {
text: `Count: ${props.count}`
}
},
{
button: {
'data-action': 'increment',
text: '+'
}
}
]
}
};
}
const HydratableCounter = makeHydratable(Counter);
const html = render(HydratableCounter, { count: 5 });
Client-Side Hydration
On the client side, hydrate the server-rendered HTML:
import { hydrate } from '@coherent.js/client';
// Find the server-rendered element
const element = document.getElementById('counter');
// Hydrate with the component
const instance = hydrate(element, HydratableCounter, { count: 5 });
Event Handling
Coherent.js uses data attributes to handle events during hydration:
function Counter(props) {
return {
div: {
className: 'counter',
children: [
{
button: {
'data-action': 'increment',
text: 'Increment'
}
},
{
button: {
'data-action': 'decrement',
text: 'Decrement'
}
}
]
}
};
}
During hydration, event listeners are automatically attached to elements with data-action attributes.
Component Instance API
When a component is hydrated, it returns an instance object with the following methods:
update(newProps)
Updates the component with new props and re-renders it.
instance.update({ count: 10 });
setState(newState)
Updates the component's state (for components with state management).
instance.setState({ count: 15 });
destroy()
Destroys the component and cleans up event listeners.
instance.destroy();
Complete Example
Here's a complete example showing server-side rendering and client-side hydration:
Server-side (Node.js)
import { render } from '@coherent.js/core';
import { makeHydratable } from '@coherent.js/client';
function Counter(props) {
return {
div: {
className: 'counter',
'data-coherent-component': 'Counter',
children: [
{
h3: {
text: 'Counter'
}
},
{
span: {
text: `Count: ${props.count}`
}
},
{
button: {
'data-action': 'increment',
text: 'Increment'
}
},
{
button: {
'data-action': 'decrement',
text: 'Decrement'
}
}
]
}
};
}
const HydratableCounter = makeHydratable(Counter);
// Render to HTML
const html = render(HydratableCounter, { count: 0 });
// Send HTML to client
res.send(`
<!DOCTYPE html>
<html>
<head>
<title>Hydration Example</title>
</head>
<body>
<div id="counter">${html}</div>
<script type="module" src="/hydration.js"></script>
</body>
</html>
`);
Bundle the browser entrypoint (example using esbuild):
npx esbuild client.js --bundle --format=esm --outfile=public/hydration.js
Client-side (Browser)
import { autoHydrate, makeHydratable } from '@coherent.js/client';
import { Counter } from './components/Counter.js';
window.componentRegistry = {
counter: makeHydratable(Counter, { componentName: 'counter' })
};
// Auto-hydrate when the module runs
autoHydrate(window.componentRegistry);
Best Practices
- Always use
makeHydratablefor components that will be hydrated - Add
data-coherent-componentattributes to mark components for hydration - Use
data-actionattributes for event handling - Clean up by calling
destroy()when components are no longer needed - Handle errors gracefully in component methods
Browser Support
Hydration requires a modern browser with support for ES modules. For older browsers, you may need to transpile the code or provide polyfills.
Troubleshooting
"Hydration can only be performed in a browser environment"
This error occurs when trying to hydrate components in a Node.js environment. Make sure hydration code only runs in the browser.
Event handlers not working
Ensure elements have the correct data-action attributes and that the hydration process completed successfully.
State not updating
Check that the component was properly hydrated and that setState is being called with the correct parameters.