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/client/hydration';
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/client/hydration';
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/client/hydration';
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/client/hydration';
const HydratableCounter = makeHydratable(Counter);
Creating Hydratable Components
To make a component hydratable, wrap it with the makeHydratable
function:
import { makeHydratable } from 'coherent/client/hydration';
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 renderToString
with hydratable components:
import { renderToString } from 'coherent/rendering/html-renderer';
import { makeHydratable } from 'coherent/client/hydration';
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 = renderToString(HydratableCounter, { count: 5 });
Client-Side Hydration
On the client side, hydrate the server-rendered HTML:
import { hydrate } from 'coherent/client/hydration';
// 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 { renderToString } from 'coherent/rendering/html-renderer';
import { makeHydratable } from 'coherent/client/hydration';
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 = renderToString(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">
import { hydrate } from './coherent/client/hydration.js';
const element = document.getElementById('counter');
hydrate(element, ${HydratableCounter.name}, { count: 0 });
</script>
</body>
</html>
`);
Client-side (Browser)
import { hydrate } from 'coherent/client/hydration';
// Find the server-rendered element
const element = document.getElementById('counter');
// Hydrate the component
const instance = hydrate(element, HydratableCounter, { count: 0 });
// The component is now interactive!
Best Practices
- Always use
makeHydratable
for components that will be hydrated - Add
data-coherent-component
attributes to mark components for hydration - Use
data-action
attributes 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.