Coherent.js Migration Guide
Upgrading from 1.0.0-beta. to 1.0.0?* See
MIGRATION-1.0.mdat the repo root for the authoritative 1.0 breaking-changes guide. This page covers migrating from OTHER frameworks (React, Vue, Express, etc.) to Coherent.js.
This guide helps developers migrate from traditional frameworks (React, Vue, Express, etc.) or template engines (Handlebars, EJS, etc.) to Coherent.js.
Coherent.js focuses on small bundles and high SSR throughput. Bundle sizes are gated per-package in CI (see packages/*/bundle-size.json for the actual numbers); see benchmarks/ for rendering benchmarks.
Getting Started with Migration
Step 1: Install Coherent.js
pnpm add @coherent.js/core @coherent.js/state @coherent.js/api
# Development tools (tree-shakable)
pnpm add -D @coherent.js/devtoolsStep 2: Configure Package.json
{
"coherent": {
"enableTreeShaking": true,
"enableStreaming": true,
"enableLRUCaching": true,
"performance": {
"enableMetrics": true
}
}
}Step 3: Migrate State Management
import { createFormState, createListState } from '@coherent.js/state';
const userForm = createFormState({ name: '', email: '' });
const productList = createListState([], { pageSize: 20 });Step 4: Convert Components
const UserList = () => ({
div: {
className: 'user-list',
children: productList.sortedItems.map(user => UserCard(user))
}
});From React
Component Structure
React JSX:
function Greeting({ name }) {
return (
<div className="greeting">
<h1>Hello, {name}!</h1>
</div>
);
}Coherent.js Object:
function Greeting({ name }) {
return {
div: {
className: 'greeting',
children: [
{ h1: { text: `Hello, ${name}!` } }
]
}
};
}State Management
React with useState:
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
}Coherent.js with withState:
import { withState } from '@coherent.js/core';
const Counter = withState({ count: 0 })(({ state, setState }) => ({
div: {
children: [
{ p: { text: `Count: ${state.count}` } },
{ button: { text: 'Increment', onclick: () => setState({ count: state.count + 1 }) } }
]
}
}));Conditional Rendering
React:
function UserProfile({ user }) {
return (
<div>
{user ? <p>Welcome, {user.name}!</p> : <p>Please log in</p>}
</div>
);
}Coherent.js:
function UserProfile({ user }) {
return {
div: {
children: [
user
? { p: { text: `Welcome, ${user.name}!` } }
: { p: { text: 'Please log in' } }
]
}
};
}List Rendering
React:
function TodoList({ todos }) {
return (
<ul>
{todos.map(todo => (
<li key={todo.id} className={todo.completed ? 'completed' : 'pending'}>
{todo.text}
</li>
))}
</ul>
);
}Coherent.js:
function TodoList({ todos }) {
return {
ul: {
children: todos.map((todo) => ({
li: {
text: todo.text,
className: todo.completed ? 'completed' : 'pending'
}
}))
}
};
}React Performance Benefits
- 100% cacheable pure functional components
- Better memory management with OOP state encapsulation
From Vue
Template System
Vue Template:
<template>
<div class="product-card">
<h3>{{ product.name }}</h3>
<p>${{ product.price }}</p>
<button @click="addToCart">Add to Cart</button>
</div>
</template>Coherent.js:
const ProductCard = (product) => ({
div: {
className: 'product-card',
children: [
{ h3: { text: product.name } },
{ p: { text: `${product.price}` } },
{ button: { text: 'Add to Cart', onclick: () => addToCart(product) } }
]
}
});Vue State Management
Vue Composition API:
import { ref, computed } from 'vue';
const cart = ref([]);
const total = computed(() => cart.value.reduce((sum, item) => sum + item.price, 0));Coherent.js:
const shoppingCart = createListState([]);
shoppingCart.addToCart = (product) => {
shoppingCart.addItem(product);
updateTotal();
};From Template Engines (Handlebars, EJS)
Basic Template
Handlebars:
<div class="greeting">
<h1>Hello, {{name}}!</h1>
<p>You have {{notifications}} notifications</p>
</div>Coherent.js:
function Greeting({ name, notifications }) {
return {
div: {
className: 'greeting',
children: [
{ h1: { text: `Hello, ${name}!` } },
{ p: { text: `You have ${notifications} notifications` } }
]
}
};
}Conditional Blocks
Handlebars:
{{#if user}}
<p>Welcome, {{user.name}}!</p>
{{else}}
<p>Please log in</p>
{{/if}}Coherent.js:
function UserProfile({ user }) {
return {
div: {
children: [
user
? { p: { text: `Welcome, ${user.name}!` } }
: { p: { text: 'Please log in' } }
]
}
};
}From String Concatenation
String Concatenation:
function createGreeting(name) {
return `<div class="greeting"><h1>Hello, ${name}!</h1></div>`;
}Coherent.js:
import { render } from '@coherent.js/core';
function Greeting({ name }) {
return {
div: {
className: 'greeting',
children: [{ h1: { text: `Hello, ${name}!` } }]
}
};
}
const html = render(Greeting({ name: 'World' }));From Express to Coherent.js API
Route Definition
Express:
app.get('/api/users/:id', async (req, res) => {
try {
const user = await getUser(req.params.id);
res.json(user);
} catch (error) {
res.status(500).json({ error: error.message });
}
});Coherent.js API (with LRU caching):
const api = createAPI({
routes: {
'GET /api/users/:id': async ({ params }) => {
const user = await getUser(params.id);
return { status: 200, body: user };
}
},
enableLRUCaching: true,
cacheSize: 1000
});Hydration Migration
One of the most critical aspects when migrating from client-side frameworks is understanding how to make server-rendered components interactive.
From React Hydration
React (automatic):
function Counter() {
const [count, setCount] = useState(0);
return <button onClick={() => setCount(count + 1)}>Count: {count}</button>;
}Coherent.js (explicit hydration):
// Server-side component
const Counter = withState({ count: 0 })(({ state, stateUtils }) => {
const { setState } = stateUtils;
return {
div: {
'data-coherent-component': 'counter',
children: [
{ p: { text: `Count: ${state.count}` } },
{ button: { text: 'Increment', onclick: () => setState({ count: state.count + 1 }) } }
]
}
};
});
// Client-side hydration
import { hydrate } from '@coherent.js/client';
document.addEventListener('DOMContentLoaded', () => {
hydrate(Counter, document.getElementById('counter-root'));
});Key Hydration Differences
Event Handler Serialization: In Coherent.js, event handlers become
data-actionattributes during SSR, then are reconnected during hydration.Component Identification: Uses explicit
data-coherent-componentattributes instead of React's reconciliation.State Initialization: Extract from DOM or pass through props rather than automatic matching.
Explicit mounting: Call
hydrate()for each interactive root — there is no automatic component registry scan.
Progressive Enhancement Pattern
{
form: {
action: '/api/submit', // Fallback for no-JS
method: 'POST',
onsubmit: enhancedSubmit, // Enhanced with hydration
children: [
{ input: { name: 'email', required: true } },
{ button: { type: 'submit', text: 'Submit' } }
]
}
}Common Patterns
Event Handling
Before (React): <button onClick={handleClick}>Click me</button>
After (Coherent.js): { button: { text: 'Click me', onclick: handleClick } }
Styling
Before: <div className="container highlighted">Content</div>
After: { div: { className: 'container highlighted', text: 'Content' } }
Data Attributes
Before: <div data-id="123" data-role="button">Content</div>
After: { div: { 'data-id': '123', 'data-role': 'button', text: 'Content' } }
Performance Optimization
Bundle Size Optimization
// Avoid: Import entire DevTools
import DevTools from '@coherent.js/devtools';
// Recommended: Tree-shakable imports
import { logComponentTree } from '@coherent.js/devtools/visualizer';
import { createPerformanceDashboard } from '@coherent.js/devtools/performance';Production Bundle Results
Bundle sizes are gated per-package in CI. See packages/*/bundle-size.json for current baselines.
Build Configuration
// vite.config.js
export default {
build: {
rollupOptions: {
output: {
manualChunks: {
'coherent-core': ['@coherent.js/core'],
'coherent-state': ['@coherent.js/state']
}
}
},
minify: 'terser',
target: 'es2020'
}
};Key Benefits of Migrating to Coherent.js
- Universal Rendering: Same components work on server and client
- Type Safety: Full TypeScript support with built-in type definitions
- Performance: Built-in performance monitoring and optimization
- Security: Automatic HTML escaping and XSS protection
- No Build Step: Pure JavaScript with no compilation required
- Progressive Enhancement: Forms and interactions work without JavaScript
- Streaming: Native support for streaming large documents
- Memory Efficiency: Smart caching and object pooling
Migration Checklist
Server-Side Migration
- Identify components that need to be converted
- Convert JSX/templates to Coherent.js object structure
- Replace state management with
withState - Update event handling (onclick, etc.)
- Add
data-coherent-componentattributes for interactive components - Test server-side rendering output
Client-Side Hydration Setup
- Install
@coherent.js/clientpackage - Create hydration entry point (
hydration.js) - Mount each interactive root with
hydrate(Component, container) - Bundle hydration script for the browser
- Add hydration script to HTML pages
- Handle timing with
DOMContentLoadedevents - Test interactive features after hydration
- Verify no hydration mismatch warnings
Testing and Optimization
- Verify performance improvements
- Test progressive enhancement (works without JS)
- Implement selective hydration for performance
- Configure tree shaking for production
- Test bundle size (see
packages/*/bundle-size.jsonfor per-package baselines) - Validate performance (target: 240+ renders/sec)
- Update build/deployment processes
For more details, see Advanced Components, Hydration Guide, and Performance Optimizations.