Examples & Demos
Discover the power of Coherent.js through interactive playground examples. From basic components to advanced patterns, see how pure object syntax makes building UIs intuitive and performant.
Getting Started
Basic Usage
JSCoherentGreeting components with conditional rendering and user cards.
▶️ Run:node examples/basic-usage.js/** * @name Basic Usage * @category Getting Started * @description Greeting components with conditional rendering and user cards. */ import { fileURLToPath } from 'node:url'; import { render } from '@coherent.js/core'; export const Greeting = ({ name = 'World', mood = 'happy' }) => ({ div: { className: `greeting greeting--${mood}`, children: [ { h2: { text: `Hello, ${name}!` } }, { p: { text: `You seem ${mood} today` } }, mood === 'fantastic' ? { div: { className: 'celebration', children: [{ span: { text: '🎉 Amazing! 🎉' } }], }, } : null, ].filter(Boolean), }, }); // User profile component with styling export const UserCard = ({ user }) => ({ div: { className: 'user-card', style: 'border: 1px solid #ccc; padding: 10px; margin: 10px;', children: [ { h3: { text: user.name } }, { p: { text: `Email: ${user.email}` } }, { p: { text: `Role: ${user.role}` } }, ], }, }); // List component rendering multiple user cards export const UserList = ({ users = [] }) => ({ div: { className: 'user-list', children: [ { h2: { text: 'User List' } }, users.length > 0 ? { ul: { children: users.map((user) => ({ li: { key: user.id, children: [UserCard({ user })], }, })), }, } : { p: { text: 'No users found' }, }, ], }, }); // Sample data for demonstration const sampleUsers = [ { id: 1, name: 'Alice', email: 'alice@example.com', role: 'Admin' }, { id: 2, name: 'Bob', email: 'bob@example.com', role: 'User' }, { id: 3, name: 'Charlie', email: 'charlie@example.com', role: 'User' }, ]; // Demo component combining everything const Demo = () => ({ div: { style: 'font-family: system-ui, sans-serif; max-width: 800px; padding: 20px;', children: [ { h1: { text: 'Coherent Framework Demo' } }, { p: { text: 'This page demonstrates basic component usage, composition, and styling.', }, }, Greeting({ name: 'Coherent User', mood: 'fantastic' }), UserList({ users: sampleUsers }), ], }, }); // Export for playground preview export default Demo(); // Print the page when this file is the program being run — `node examples/basic-usage.js`, // or the playground runner, which feeds the file on stdin — but not when it is // imported. `import.meta.main` says so exactly from Node 22.18; before that, // compare against argv[1], which is empty when the program came on stdin. const isProgram = import.meta.main ?? (!process.argv[1] || fileURLToPath(import.meta.url) === process.argv[1]); if (isProgram) { console.log(render(Demo())); }
Components
Component Composition
JSCoherentHOCs, state management, and advanced component patterns.
▶️ Run:node examples/component-composition.js/** * @name Component Composition * @category Components * @description HOCs, state management, and advanced component patterns. */ import { fileURLToPath } from 'node:url'; import { withState, render } from '@coherent.js/core'; import { hydrate } from '@coherent.js/client'; // Example 1: Basic component composition export const Header = ({ title, subtitle }) => ({ header: { className: 'app-header', children: [ { h1: { text: title } }, subtitle ? { p: { text: subtitle } } : null, ].filter(Boolean), }, }); export const Footer = ({ copyright }) => ({ footer: { className: 'app-footer', children: [{ p: { text: ` ${new Date().getFullYear()} ${copyright}` } }], }, }); export const Layout = ({ header, footer, children }) => ({ div: { className: 'app-layout', children: [ header, { main: { className: 'app-main', children: Array.isArray(children) ? children : [children], }, }, footer, ], }, }); // Example 2: Higher-order component for loading states export const withLoading = (WrappedComponent) => withState({ loading: false, error: null })( ({ state, setState, ...props }) => { if (state.loading) { return { div: { className: 'loading-container', children: [ { h3: { text: 'Loading...' } }, { div: { className: 'spinner', text: '' } }, ], }, }; } if (state.error) { return { div: { className: 'error-container', children: [ { h3: { text: 'Error Occurred' } }, { p: { text: state.error.message || 'An unknown error occurred' }, }, { button: { text: 'Retry', onclick: () => setState({ error: null }), }, }, ], }, }; } // Add loading controls to props const propsWithLoading = { ...props, setLoading: (loading) => setState({ loading }), setError: (error) => setState({ error }), }; return WrappedComponent(propsWithLoading); } ); // Example 3: Component composition with mixins export const withTimestamp = (Component) => (props) => ({ div: { className: 'timestamp-wrapper', children: [ Component(props), { small: { text: `Last updated: ${new Date().toLocaleTimeString()}`, className: 'timestamp', }, }, ], }, }); export const withBorder = (Component) => (props) => ({ div: { className: 'border-wrapper', style: 'border: 2px solid #ccc; padding: 10px; margin: 10px 0; border-radius: 4px;', children: [Component(props)], }, }); export const SimpleCard = ({ title, content }) => ({ div: { className: 'simple-card', children: [{ h3: { text: title } }, { p: { text: content } }], }, }); // Compose multiple HOCs export const EnhancedCard = withBorder(withTimestamp(SimpleCard)); // Example 4: Form component with state management and hydration support const ContactFormComponent = withState({ name: '', email: '', message: '', submitted: false, })(({ state, stateUtils }) => { const { setState } = stateUtils; const handleSubmit = (event) => { event.preventDefault(); console.log('Form submitted:', { name: state.name, email: state.email, message: state.message, }); setState({ submitted: true }); // Reset form after 2 seconds setTimeout(() => { setState({ name: '', email: '', message: '', submitted: false }); }, 2000); }; const updateField = (field) => (event) => { setState({ [field]: event.target.value }); }; return { div: { 'data-coherent-component': 'contact-form', children: [ { form: { className: 'contact-form', onsubmit: handleSubmit, children: [ { h2: { text: 'Contact Us' } }, state.submitted ? { div: { className: 'success-message', children: [ { p: { text: '✓ Message sent successfully!' } }, ], }, } : null, { div: { className: 'form-field', children: [ { label: { text: 'Name', htmlFor: 'contact-name' } }, { input: { id: 'contact-name', type: 'text', value: state.name, placeholder: 'Your name', oninput: updateField('name'), required: true, }, }, ], }, }, { div: { className: 'form-field', children: [ { label: { text: 'Email', htmlFor: 'contact-email' } }, { input: { id: 'contact-email', type: 'email', value: state.email, placeholder: 'your@email.com', oninput: updateField('email'), required: true, }, }, ], }, }, { div: { className: 'form-field', children: [ { label: { text: 'Message', htmlFor: 'contact-message' } }, { textarea: { id: 'contact-message', value: state.message, placeholder: 'Your message here...', oninput: updateField('message'), rows: 4, required: true, }, }, ], }, }, { button: { className: 'btn btn--primary', type: 'submit', text: state.submitted ? 'Sent!' : 'Send Message', disabled: state.submitted, }, }, ].filter(Boolean), }, }, ], }, }; }); export const ContactForm = ContactFormComponent; // Any pure-object component is hydratable: no wrapper needed // (makeHydratable was removed in 1.0). export const HydratableContactForm = ContactForm; // Complete page demonstrating all composition patterns export const demoPage = { html: { children: [ { head: { children: [ { title: { text: 'Component Composition Demo' } }, { style: { text: ` body { font-family: Arial, sans-serif; max-width: 900px; margin: 0 auto; padding: 20px; line-height: 1.6; } .app-header { background: #f8f9fa; padding: 20px; border-radius: 8px; margin-bottom: 20px; } .app-footer { background: #f8f9fa; padding: 15px; border-radius: 8px; margin-top: 20px; text-align: center; } .app-main { min-height: 400px; } .simple-card { background: white; padding: 15px; margin: 10px 0; } .timestamp { color: #666; font-style: italic; margin-top: 10px; display: block; } .contact-form { background: #f8f9fa; padding: 20px; border-radius: 8px; margin: 20px 0; } .form-field { margin-bottom: 15px; } .form-field label { display: block; margin-bottom: 5px; font-weight: bold; } .form-field input, .form-field textarea { width: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px; font-size: 14px; } .btn { padding: 10px 20px; border: none; border-radius: 4px; cursor: pointer; font-size: 14px; } .btn--primary { background: #007bff; color: white; } .btn--primary:hover { background: #0056b3; } .btn:disabled { background: #6c757d; cursor: not-allowed; opacity: 0.6; } .success-message { background: #d4edda; color: #155724; padding: 10px; border-radius: 4px; margin: 10px 0; border: 1px solid #c3e6cb; } `, }, }, ], }, }, { body: { children: [ Layout({ header: Header({ title: 'Component Composition Demo', subtitle: 'Exploring different composition patterns in Coherent.js', }), footer: Footer({ copyright: 'Coherent.js Examples' }), children: [ { h2: { text: 'Enhanced Card with Multiple HOCs' } }, { p: { text: 'This card demonstrates composition using withBorder and withTimestamp HOCs:', }, }, EnhancedCard({ title: 'Enhanced Card Example', content: 'This card has a border and timestamp automatically added through composition.', }), { h2: { text: 'Interactive Form with State' } }, { p: { text: 'This form demonstrates state management, event handling, and client-side hydration:', }, }, HydratableContactForm(), ], }), ], }, }, ], }, }; // Set up client-side hydration (browser only) if (typeof window !== 'undefined') { // Hydrate the server-rendered form explicitly (autoHydrate was removed in 1.0) document.addEventListener('DOMContentLoaded', () => { const element = document.querySelector( '[data-coherent-component="contact-form"]' ); if (element) { hydrate(HydratableContactForm, element); console.log('✅ Component composition hydration complete!'); } }); } // Export the demo page as default for live preview export default demoPage; // Print the page when this file is the program being run — `node examples/component-composition.js`, // or the playground runner, which feeds the file on stdin — but not when it is // imported. `import.meta.main` says so exactly from Node 22.18; before that, // compare against argv[1], which is empty when the program came on stdin. const isProgram = import.meta.main ?? (!process.argv[1] || fileURLToPath(import.meta.url) === process.argv[1]); if (isProgram) { console.log(render(demoPage)); }Error Boundaries
JSCoherentGraceful error handling with fallback components.
▶️ Run:node examples/error-boundary-demo.js/** * @name Error Boundaries * @category Components * @description Graceful error handling with fallback components. */ import { render, createErrorBoundary, createErrorFallback, withErrorBoundary, createAsyncErrorBoundary, createGlobalErrorHandler, } from '@coherent.js/core'; console.log('\n=== Coherent.js Error Boundary Demo ===\n'); // Example 1: Basic Error Boundary console.log('--- Example 1: Basic Error Boundary ---\n'); const BuggyComponent = () => { throw new Error('Oops! Something went wrong'); }; const basicBoundary = createErrorBoundary({ fallback: { div: { className: 'error', text: 'An error occurred. Please try again.', }, }, onError: (error, errorInfo) => { console.log('Error caught:', error.message); console.log('Error info:', errorInfo); }, }); const SafeBuggyComponent = basicBoundary(BuggyComponent); try { const result = SafeBuggyComponent(); console.log('Rendered fallback:', render(result)); } catch (error) { console.log('Error was not caught!'); } // Example 2: Custom Error Fallback console.log('\n--- Example 2: Custom Error Fallback ---\n'); const customFallback = createErrorFallback({ title: 'Oops! Something went wrong', showError: true, showStack: false, showReset: true, className: 'custom-error', }); const customBoundary = createErrorBoundary({ fallback: customFallback, onError: (error) => console.log('Custom boundary caught:', error.message), }); const AnotherBuggyComponent = () => { throw new Error('Custom error message'); }; const SafeCustomComponent = customBoundary(AnotherBuggyComponent); const customResult = SafeCustomComponent(); console.log('Custom fallback rendered'); // Example 3: Error Boundary with Reset console.log('\n--- Example 3: Error Boundary with Reset ---\n'); let shouldFail = true; const SometimesBuggyComponent = () => { if (shouldFail) { throw new Error('Component failed!'); } return { div: { text: 'Component rendered successfully!' } }; }; const resetBoundary = createErrorBoundary({ fallback: (error, errorInfo, context) => ({ div: { children: [ { p: { text: `Error: ${error.message}` } }, { button: { text: 'Reset and Try Again', onclick: () => { console.log('Resetting error boundary...'); shouldFail = false; context.reset(); }, }, }, ], }, }), onReset: () => console.log('Error boundary reset!'), }); const SafeSometimesBuggyComponent = resetBoundary(SometimesBuggyComponent); console.log('First render (will fail):'); const result1 = SafeSometimesBuggyComponent(); console.log(render(result1)); console.log('\nAfter reset (should succeed):'); shouldFail = false; const result2 = SafeSometimesBuggyComponent(); console.log(render(result2)); // Example 4: Error Boundary with Reset Keys console.log('\n--- Example 4: Error Boundary with Reset Keys ---\n'); const UserComponent = ({ userId }) => { if (userId === 'bad-user') { throw new Error('Invalid user!'); } return { div: { text: `User: ${userId}` } }; }; const userBoundary = createErrorBoundary({ fallback: { div: { text: 'Failed to load user' } }, resetKeys: ['userId'], resetOnPropsChange: true, onReset: () => console.log('User changed, resetting error boundary'), }); const SafeUserComponent = userBoundary(UserComponent); console.log('Rendering with bad user:'); const badUserResult = SafeUserComponent({ userId: 'bad-user' }); console.log(render(badUserResult)); console.log('\nRendering with good user (auto-reset):'); const goodUserResult = SafeUserComponent({ userId: 'good-user' }); console.log(render(goodUserResult)); // Example 5: Max Errors console.log('\n--- Example 5: Max Errors ---\n'); let attemptCount = 0; const AlwaysBuggyComponent = () => { attemptCount++; throw new Error(`Attempt ${attemptCount} failed`); }; const maxErrorsBoundary = createErrorBoundary({ fallback: (error, errorInfo, context) => { if (context.permanent) { return { div: { className: 'permanent-error', text: 'Too many errors. Component disabled.', }, }; } return { div: { text: `Error ${context.errorCount}/3: ${error.message}`, }, }; }, maxErrors: 3, onError: (error) => console.log('Error caught:', error.message), }); const SafeAlwaysBuggyComponent = maxErrorsBoundary(AlwaysBuggyComponent); console.log('Attempt 1:'); console.log(render(SafeAlwaysBuggyComponent())); console.log('\nAttempt 2:'); console.log(render(SafeAlwaysBuggyComponent())); console.log('\nAttempt 3:'); console.log(render(SafeAlwaysBuggyComponent())); console.log('\nAttempt 4 (permanent fallback):'); console.log(render(SafeAlwaysBuggyComponent())); // Example 6: Auto-Reset Timeout console.log('\n--- Example 6: Auto-Reset Timeout ---\n'); const timeoutBoundary = createErrorBoundary({ fallback: { div: { text: 'Error! Will auto-reset in 2 seconds...' } }, resetTimeout: 2000, onReset: () => console.log('Auto-reset triggered!'), onError: (error) => console.log('Error caught, timer started'), }); // Example 7: Wrapping Multiple Components console.log('\n--- Example 7: Wrapping Multiple Components ---\n'); const Header = () => ({ header: { text: 'Header' } }); const Content = () => { throw new Error('Content failed'); }; const Footer = () => ({ footer: { text: 'Footer' } }); const safeComponents = withErrorBoundary( { fallback: (error) => ({ div: { className: 'component-error', text: `Component error: ${error.message}`, }, }), }, { Header, Content, Footer } ); console.log('Safe Header:', render(safeComponents.Header())); console.log('Safe Content:', render(safeComponents.Content())); console.log('Safe Footer:', render(safeComponents.Footer())); // Example 8: Async Error Boundary console.log('\n--- Example 8: Async Error Boundary ---\n'); const AsyncComponent = async () => { await new Promise((resolve) => setTimeout(resolve, 100)); return { div: { text: 'Async content loaded!' } }; }; const FailingAsyncComponent = async () => { await new Promise((resolve) => setTimeout(resolve, 100)); throw new Error('Async load failed!'); }; const asyncBoundary = createAsyncErrorBoundary({ fallback: { div: { text: 'Loading...' } }, errorFallback: { div: { text: 'Failed to load async content' } }, timeout: 5000, onError: (error) => console.log('Async error:', error.message), }); const SafeAsyncComponent = asyncBoundary(AsyncComponent); const SafeFailingAsyncComponent = asyncBoundary(FailingAsyncComponent); console.log('Loading async component...'); SafeAsyncComponent().then((result) => { console.log('Success:', render(result)); }); console.log('Loading failing async component...'); SafeFailingAsyncComponent().then((result) => { console.log('Fallback:', render(result)); }); // Example 9: Global Error Handler console.log('\n--- Example 9: Global Error Handler ---\n'); const globalHandler = createGlobalErrorHandler({ maxErrors: 50, onError: (error, context) => { console.log(`Global handler caught: ${error.message}`); console.log(`Context:`, context); }, }); // Simulate some errors globalHandler.captureError(new Error('Error 1'), { component: 'Header' }); globalHandler.captureError(new Error('Error 2'), { component: 'Content' }); globalHandler.captureError(new Error('Error 3'), { component: 'Footer' }); console.log('\nGlobal error stats:', globalHandler.getStats()); console.log('Captured errors:', globalHandler.getErrors().length); // Example 10: Nested Error Boundaries console.log('\n--- Example 10: Nested Error Boundaries ---\n'); const InnerBuggyComponent = () => { throw new Error('Inner component failed'); }; const OuterComponent = () => { const innerBoundary = createErrorBoundary({ fallback: { div: { className: 'inner-error', text: 'Inner error caught' } }, }); const SafeInner = innerBoundary(InnerBuggyComponent); return { div: { className: 'outer', children: [{ h2: { text: 'Outer Component' } }, SafeInner()], }, }; }; const outerBoundary = createErrorBoundary({ fallback: { div: { text: 'Outer error caught' } }, }); const SafeOuterComponent = outerBoundary(OuterComponent); console.log('Nested boundaries result:'); console.log(render(SafeOuterComponent())); // Example 11: Error Boundary with Component Tree console.log('\n--- Example 11: Error Boundary with Component Tree ---\n'); const Page = () => ({ html: { children: [ { head: { children: [{ title: { text: 'Error Boundary Demo' } }], }, }, { body: { children: [ { h1: { text: 'My Application' } }, (() => { throw new Error('Body content failed'); })(), ], }, }, ], }, }); const pageBoundary = createErrorBoundary({ fallback: createErrorFallback({ title: 'Page Error', showError: true, showReset: true, }), }); const SafePage = pageBoundary(Page); console.log('Page with error boundary:'); const pageResult = SafePage(); console.log(render(pageResult).substring(0, 200) + '...'); console.log('\n=== Demo Complete ===\n'); console.log( 'Error boundaries provide robust error handling for your components!' ); console.log( 'Use them to prevent entire application crashes from single component failures.' );State Management
JSCoherentReactive state with observables, computed properties, and persistence.
▶️ Run:node examples/state-management-demo.js/** * @name State Management * @category Components * @description Reactive state with observables, computed properties, and persistence. */ import { render } from '@coherent.js/core'; import { observable, computed, createReactiveState, withLocalStorage, createValidatedState, validators, provideContext, useContext, runWithContext, } from '@coherent.js/state'; // ============================================================================= // Example 1: Basic Observables // ============================================================================= console.log('\n📦 Example 1: Basic Observables'); const count = observable(0); const doubled = computed(() => count.value * 2); // Watch for changes count.watch((newValue, oldValue) => { console.log(`Count changed from ${oldValue} to ${newValue}`); console.log(`Doubled is now: ${doubled.value}`); }); count.value = 5; // Logs: "Count changed from 0 to 5", "Doubled is now: 10" count.value = 10; // Logs: "Count changed from 5 to 10", "Doubled is now: 20" // ============================================================================= // Example 2: Reactive State Object // ============================================================================= console.log('\n📦 Example 2: Reactive State Object'); const appState = createReactiveState({ user: { name: 'Guest', isLoggedIn: false }, theme: 'dark', notifications: [], }); // Watch specific paths appState.watch('user.name', (newName, oldName) => { console.log(`User name changed from "${oldName}" to "${newName}"`); }); appState.watch('theme', (newTheme) => { console.log(`Theme changed to: ${newTheme}`); }); // Update state appState.set('user.name', 'John Doe'); appState.set('user.isLoggedIn', true); appState.set('theme', 'light'); // Get values console.log('Current user:', appState.get('user')); console.log('Current theme:', appState.get('theme')); // ============================================================================= // Example 3: State Persistence with LocalStorage // ============================================================================= console.log('\n📦 Example 3: State Persistence'); // Note: This example is for browser environments // In Node.js, this will use a mock localStorage const userPrefs = withLocalStorage( { theme: 'dark', language: 'en', fontSize: 14 }, 'user-preferences' ); // Restoring from storage is asynchronous: wait for it before reading. await userPrefs.ready; console.log('Loaded preferences:', userPrefs.getState()); // Updates are automatically persisted (without browser storage, as here in // Node, the store keeps working in memory) userPrefs.setState({ theme: 'light', fontSize: 16 }); console.log('Updated preferences:', userPrefs.getState()); // These changes are now saved to localStorage! // ============================================================================= // Example 4: State Validation // ============================================================================= console.log('\n📦 Example 4: State Validation'); // A validator returns true, or an error message. `strict` makes invalid // updates throw instead of being ignored. const userForm = createValidatedState( { email: '', age: 0, username: '' }, { strict: true, validators: { email: validators.email, age: validators.range(18, 120), username: validators.length(3, 30), }, } ); // Valid updates try { userForm.setState({ email: 'user@example.com', age: 25, username: 'johndoe', }); console.log('✓ All validations passed!'); console.log('Form data:', userForm.getState()); } catch (error) { console.error('✗ Validation error:', error.message); } // Invalid update: rejected, state unchanged try { userForm.setState({ email: 'invalid-email' }); } catch (error) { console.log( '✗ Rejected:', error.validationErrors.map((e) => `${e.path}: ${e.message}`).join(', ') ); console.log(' email is still:', userForm.getState('email')); } // ============================================================================= // Example 5: SSR-Compatible State (Context API) // ============================================================================= console.log('\n📦 Example 5: SSR Context API'); // Simulate a request handler: each request runs in its own context scope function handleRequest(userId) { return runWithContext(() => { const requestState = { userId, timestamp: new Date().toISOString(), theme: 'dark', }; // Provide context for this request (visible to nothing outside it) provideContext('request', requestState); // Render components that use the context const html = render(UserDashboard()); console.log('Rendered HTML:', html); return html; }); } function UserDashboard() { const requestState = useContext('request'); return { div: { className: `dashboard theme-${requestState.theme}`, children: [ { h1: { text: 'User Dashboard' }, }, { p: { text: `User ID: ${requestState.userId}` }, }, { p: { text: `Request time: ${requestState.timestamp}` }, }, ], }, }; } // Simulate requests handleRequest(123); handleRequest(456); // ============================================================================= // Example 6: Complete Application State // ============================================================================= console.log('\n📦 Example 6: Complete Application State'); // Combining features: reactive state for the app, validation at the edge, // and persistence of the parts worth keeping. const shopState = createReactiveState({ user: { id: null, name: '', email: '' }, settings: { theme: 'dark', notifications: true }, cart: [], }); const savedSettings = withLocalStorage( shopState.get('settings'), 'app-settings' ); // Watch whole keys or nested paths shopState.watch('user', (newUser) => { console.log('User updated:', newUser); }); shopState.watch('settings.theme', (newTheme) => { console.log('Theme changed to:', newTheme); savedSettings.setState({ theme: newTheme }); }); function updateUser(user) { const emailCheck = validators.email(user.email); if (emailCheck !== true) throw new Error(emailCheck); shopState.set('user', user); } try { updateUser({ id: 123, name: 'John Doe', email: 'john@example.com' }); shopState.set('settings.theme', 'light'); shopState.set('cart', [ { id: 1, name: 'Product A', price: 29.99 }, { id: 2, name: 'Product B', price: 39.99 }, ]); console.log('\n✓ All state updated successfully!'); console.log('Final state:', JSON.stringify(shopState.toObject(), null, 2)); console.log('Persisted settings:', savedSettings.getState()); } catch (error) { console.error('✗ Error:', error.message); } // ============================================================================= // Example 7: Reactive UI Component // ============================================================================= console.log('\n📦 Example 7: Reactive UI Component'); // Create reactive counter const counter = observable(0); function CounterComponent() { // This would normally re-render automatically on changes return { div: { className: 'counter', children: [ { h2: { text: 'Counter Example' }, }, { p: { text: `Count: ${counter.value}` }, }, { button: { text: 'Increment', onclick: () => { counter.value++; console.log(`Counter incremented to: ${counter.value}`); }, }, }, ], }, }; } // Render initial state console.log('Initial render:', render(CounterComponent())); // Simulate clicks counter.value++; counter.value++; counter.value++; console.log('After increments:', render(CounterComponent())); // ============================================================================= // Summary // ============================================================================= console.log('\n' + '='.repeat(80)); console.log('📝 Summary of @coherent.js/state Features:'); console.log('='.repeat(80)); console.log(''); console.log('✓ Observable values with automatic dependency tracking'); console.log('✓ Computed properties that auto-update'); console.log('✓ Reactive state objects with path-based watching'); console.log('✓ LocalStorage/SessionStorage/IndexedDB persistence'); console.log('✓ Built-in validation with customizable validators'); console.log('✓ SSR-compatible context API'); console.log('✓ Type-safe with TypeScript definitions'); console.log('✓ Zero dependencies (except @coherent.js/core peer)'); console.log(''); console.log('🚀 Ready for production use in v1.0.0-beta.2!'); console.log('='.repeat(80));
Features
Event Bus
JSCoherentPublish-subscribe event system for component communication.
▶️ Run:node examples/event-bus-demo.js/** * @name Event Bus * @category Features * @description Publish-subscribe event system for component communication. */ import { render, eventSystem, withEventBus, createActionHandlers, } from '@coherent.js/core'; // Example: Todo Application with Event-Driven Architecture function createTodoApp() { // Register action handlers for todo operations eventSystem.registerActions({ 'add-todo': ({ data, state, setState, emit }) => { const text = data.text || data.todoText; if (!text || !text.trim()) return; const newTodo = { id: Date.now(), text: text.trim(), completed: false, }; setState({ todos: [...(state.todos || []), newTodo], newTodoText: '', }); emit('notification:success', { message: `Todo "${text}" added!`, }); }, 'toggle-todo': ({ data, state, setState, emit }) => { const todoId = parseInt(data.todoId); setState({ todos: state.todos.map((todo) => todo.id === todoId ? { ...todo, completed: !todo.completed } : todo ), }); emit('todo:toggled', { todoId, completed: !state.todos.find((t) => t.id === todoId)?.completed, }); }, 'delete-todo': ({ data, state, setState, emit }) => { const todoId = parseInt(data.todoId); const todo = state.todos.find((t) => t.id === todoId); setState({ todos: state.todos.filter((todo) => todo.id !== todoId), }); emit('notification:info', { message: `Todo "${todo?.text}" deleted`, }); }, 'clear-completed': ({ state, setState, emit }) => { const completedCount = state.todos.filter( (todo) => todo.completed ).length; setState({ todos: state.todos.filter((todo) => !todo.completed), }); emit('notification:info', { message: `${completedCount} completed todos cleared`, }); }, 'filter-todos': ({ data, state, setState }) => { setState({ filter: data.filter || 'all', }); }, }); // Register modal actions eventSystem.registerActions(createActionHandlers.modal('todo-help')); // Register notification handlers eventSystem.on('notification:*', (data, eventName) => { console.log(`[Notification] ${eventName}:`, data.message); // In a real app, this would update a notification component }); // Register todo event handlers eventSystem.on('todo:toggled', (data) => { console.log( `[Todo] Toggled todo ${data.todoId} to ${data.completed ? 'completed' : 'pending'}` ); }); // Component using event bus integration const TodoApp = withEventBus({ scope: 'todo-app', events: { 'app:mounted': () => { console.log('[TodoApp] Application mounted'); }, }, debug: true, })(({ eventBus, eventUtils, ...props }) => { const state = props.state || { todos: [ { id: 1, text: 'Learn Coherent.js Event Bus', completed: false }, { id: 2, text: 'Build an awesome app', completed: false }, ], filter: 'all', newTodoText: '', }; const filteredTodos = state.todos.filter((todo) => { switch (state.filter) { case 'active': return !todo.completed; case 'completed': return todo.completed; default: return true; } }); const stats = { total: state.todos.length, completed: state.todos.filter((todo) => todo.completed).length, active: state.todos.filter((todo) => !todo.completed).length, }; return { div: { className: 'todo-app', 'data-coherent-component': 'TodoApp', 'data-coherent-state': JSON.stringify(state), children: [ // Header { header: { className: 'todo-header', children: [ { h1: { text: 'Event-Driven Todos', }, }, { button: { 'data-action': 'open-modal', 'data-modal-id': 'todo-help', className: 'help-btn', text: '?', }, }, ], }, }, // Add todo form { form: { className: 'add-todo-form', 'data-action': 'add-todo', children: [ { input: { type: 'text', name: 'todoText', placeholder: 'What needs to be done?', value: state.newTodoText, 'data-action': 'update-input', required: true, }, }, { button: { type: 'submit', text: 'Add Todo', }, }, ], }, }, // Filter buttons { div: { className: 'filter-controls', children: ['all', 'active', 'completed'].map((filter) => ({ button: { 'data-action': 'filter-todos', 'data-filter': filter, className: `filter-btn ${state.filter === filter ? 'active' : ''}`, text: filter.charAt(0).toUpperCase() + filter.slice(1), }, })), }, }, // Todo list { ul: { className: 'todo-list', children: filteredTodos.map((todo) => ({ li: { className: `todo-item ${todo.completed ? 'completed' : ''}`, 'data-todo-id': todo.id, children: [ { input: { type: 'checkbox', checked: todo.completed, 'data-action': 'toggle-todo', 'data-todo-id': todo.id, }, }, { span: { className: 'todo-text', text: todo.text, }, }, { button: { 'data-action': 'delete-todo', 'data-todo-id': todo.id, className: 'delete-btn', text: '×', }, }, ], }, })), }, }, // Stats and actions { footer: { className: 'todo-footer', children: [ { div: { className: 'todo-stats', children: [ { span: { text: `${stats.total} total, ${stats.active} active, ${stats.completed} completed`, }, }, ], }, }, stats.completed > 0 ? { button: { 'data-action': 'clear-completed', className: 'clear-completed-btn', text: 'Clear Completed', }, } : null, ].filter(Boolean), }, }, // Help modal (hidden by default) { div: { id: 'todo-help', className: 'modal hidden', children: [ { div: { className: 'modal-content', children: [ { h3: { text: 'Event-Driven Todo Help' }, }, { p: { text: 'This todo app demonstrates declarative event handling:', }, }, { ul: { children: [ { li: { text: 'Click checkboxes to toggle completion', }, }, { li: { text: 'Click × to delete todos' } }, { li: { text: 'Use filter buttons to view different states', }, }, { li: { text: 'All actions use data-action attributes', }, }, ], }, }, { button: { 'data-action': 'close-modal', 'data-modal-id': 'todo-help', text: 'Close', }, }, ], }, }, ], }, }, ], }, }; }); return TodoApp; } // Example: Product Catalog with CRUD operations function createProductCatalog() { // Register CRUD actions for products eventSystem.registerActions( createActionHandlers.crud({ entityName: 'product', onCreate: (data) => console.log('Creating product:', data), onUpdate: (data) => console.log('Updating product:', data), onDelete: (data) => console.log('Deleting product:', data), onRead: (data) => console.log('Reading product:', data), }) ); // Register form actions eventSystem.registerActions( createActionHandlers.form({ onSubmit: (formData) => { eventSystem.emit('product:create', formData); }, onValidate: (formData) => { return formData.name && formData.name.trim().length > 0; }, }) ); const ProductCatalog = ({ products = [] }) => ({ div: { className: 'product-catalog', children: [ { h2: { text: 'Product Catalog' }, }, // Add product form { form: { className: 'add-product-form', 'data-action': 'submit-form', children: [ { input: { type: 'text', name: 'name', placeholder: 'Product name', required: true, }, }, { input: { type: 'number', name: 'price', placeholder: 'Price', min: '0', step: '0.01', }, }, { button: { type: 'submit', text: 'Add Product', }, }, ], }, }, // Product list { div: { className: 'product-list', children: products.map((product) => ({ div: { className: 'product-card', 'data-product-id': product.id, children: [ { h3: { text: product.name }, }, { p: { text: `$${product.price}` }, }, { button: { 'data-action': 'update-product', 'data-product-id': product.id, text: 'Edit', }, }, { button: { 'data-action': 'delete-product', 'data-product-id': product.id, className: 'danger', text: 'Delete', }, }, ], }, })), }, }, ], }, }); return ProductCatalog; } // Demo runner function runDemo() { console.log('🚀 Coherent.js Event Bus Demo'); console.log('===============================\n'); // Create components const TodoApp = createTodoApp(); const ProductCatalog = createProductCatalog(); // Render todo app console.log('📝 Todo Application:'); const todoHTML = render(TodoApp()); console.log('Rendered HTML length:', todoHTML.length, 'characters'); // Render product catalog console.log('\n🛍️ Product Catalog:'); const catalogHTML = render( ProductCatalog({ products: [ { id: 1, name: 'Coherent.js Book', price: 29.99 }, { id: 2, name: 'Event Bus Guide', price: 19.99 }, ], }) ); console.log('Rendered HTML length:', catalogHTML.length, 'characters'); // Demonstrate event system stats console.log('\n📊 Event Bus Statistics:'); console.log(eventSystem.getStats()); // Demonstrate scoped events console.log('\n🎯 Scoped Events Demo:'); const userScope = eventSystem.createScope('user'); const adminScope = eventSystem.createScope('admin'); userScope.on('action', (data) => { console.log('[User Scope] Action:', data); }); adminScope.on('action', (data) => { console.log('[Admin Scope] Action:', data); }); userScope.emitSync('action', { type: 'user-click' }); adminScope.emitSync('action', { type: 'admin-action' }); // Test wildcard events console.log('\n🌟 Wildcard Events Demo:'); eventSystem.on('demo:*', (data, event) => { console.log(`[Wildcard] Caught ${event}:`, data); }); eventSystem.emitSync('demo:test1', { message: 'First test' }); eventSystem.emitSync('demo:test2', { message: 'Second test' }); console.log('\n✅ Demo completed successfully!'); console.log('\nIn a real application, you would:'); console.log('1. Include the event system in your main app'); console.log('2. Use data-action attributes in your HTML'); console.log('3. Register action handlers for your use cases'); console.log('4. Leverage event communication between components'); } // HTML/CSS for complete demo const demoCSS = ` <style> .todo-app { max-width: 600px; margin: 0 auto; padding: 20px; font-family: Arial, sans-serif; } .todo-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px; } .help-btn { width: 30px; height: 30px; border-radius: 50%; border: 1px solid #ccc; background: #f0f0f0; cursor: pointer; } .add-todo-form { display: flex; margin-bottom: 20px; } .add-todo-form input { flex: 1; padding: 10px; border: 1px solid #ccc; border-radius: 4px 0 0 4px; } .add-todo-form button { padding: 10px 20px; border: 1px solid #007bff; background: #007bff; color: white; border-radius: 0 4px 4px 0; cursor: pointer; } .filter-controls { margin-bottom: 20px; } .filter-btn { margin-right: 10px; padding: 5px 15px; border: 1px solid #ccc; background: white; cursor: pointer; } .filter-btn.active { background: #007bff; color: white; } .todo-list { list-style: none; padding: 0; } .todo-item { display: flex; align-items: center; padding: 10px; border-bottom: 1px solid #eee; } .todo-item.completed .todo-text { text-decoration: line-through; color: #888; } .todo-text { flex: 1; margin: 0 10px; } .delete-btn { background: #dc3545; color: white; border: none; width: 25px; height: 25px; border-radius: 50%; cursor: pointer; } .todo-footer { margin-top: 20px; display: flex; justify-content: space-between; align-items: center; } .clear-completed-btn { padding: 5px 15px; border: 1px solid #dc3545; background: #dc3545; color: white; border-radius: 4px; cursor: pointer; } .modal { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.5); display: flex; justify-content: center; align-items: center; } .modal.hidden { display: none; } .modal-content { background: white; padding: 20px; border-radius: 8px; max-width: 500px; } .product-catalog { max-width: 800px; margin: 0 auto; padding: 20px; } .add-product-form { display: flex; gap: 10px; margin-bottom: 20px; } .add-product-form input { padding: 10px; border: 1px solid #ccc; border-radius: 4px; } .product-list { display: grid; grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); gap: 20px; } .product-card { border: 1px solid #ccc; padding: 15px; border-radius: 8px; } .product-card button { margin-right: 10px; padding: 5px 10px; border: 1px solid #ccc; border-radius: 4px; cursor: pointer; } .product-card button.danger { background: #dc3545; color: white; border-color: #dc3545; } </style> `; export { runDemo, demoCSS, createTodoApp, createProductCatalog }; // Run the demo if this file is executed directly if (import.meta.main) { runDemo(); }Streaming SSR
JSCoherentStream HTML responses for faster time-to-first-byte.
▶️ Run:node examples/streaming.js/** * @name Streaming SSR * @category Features * @description Stream HTML responses for faster time-to-first-byte. */ import { fileURLToPath } from 'node:url'; import { renderToStream } from '@coherent.js/core'; // Note: Streaming renderer is a separate feature - using render for now // For true streaming, use the streaming-renderer package when available // Large list component optimized for streaming const StreamingList = ({ itemCount = 100, title = 'Streaming List' }) => ({ div: { class: 'streaming-list', children: [ { h4: { text: title } }, { p: { text: `${itemCount} items rendered progressively` } }, { div: { class: 'list-container', children: Array.from({ length: itemCount }, (_, i) => ({ div: { key: i, class: 'list-item', children: [ { span: { text: `Item ${i + 1}` } }, { small: { text: ` (Batch ${Math.floor(i / 10) + 1})` } }, ], }, })), }, }, ], }, }); // Streaming data table component const StreamingDataTable = ({ rows = [], showProgress = false }) => ({ div: { class: 'streaming-table-container', children: [ { h4: { text: 'Data Table Streaming' } }, showProgress && { div: { class: 'progress-info', children: [ { p: { text: `Streaming ${rows.length} records` } }, { div: { class: 'progress-bar', text: '████████████ 100%' } }, ], }, }, { table: { class: 'streaming-table', children: [ { thead: { children: [ { tr: { children: [ { th: { text: 'ID' } }, { th: { text: 'Name' } }, { th: { text: 'Department' } }, { th: { text: 'Status' } }, ], }, }, ], }, }, { tbody: { children: rows.map((row) => ({ tr: { key: row.id, class: row.status === 'active' ? 'active-row' : '', children: [ { td: { text: row.id } }, { td: { text: row.name } }, { td: { text: row.department } }, { td: { text: row.status, class: `status-${row.status}` }, }, ], }, })), }, }, ], }, }, ].filter(Boolean), }, }); // Progressive content streaming component const ProgressiveContent = ({ sections = [] }) => ({ div: { class: 'progressive-content', children: [ { h4: { text: 'Progressive Content Streaming' } }, { p: { text: 'Content sections loaded incrementally' } }, ...sections.map((section, index) => ({ div: { key: index, class: `content-section section-${index}`, children: [ { h5: { text: section.title } }, { p: { text: section.content } }, section.highlight && { div: { class: 'highlight', text: section.highlight, }, }, ].filter(Boolean), }, })), ], }, }); // Generate sample data for streaming demos const generateStreamingData = (count = 50) => Array.from({ length: count }, (_, i) => ({ id: i + 1, name: `Employee ${i + 1}`, department: ['Engineering', 'Marketing', 'Sales', 'Support'][i % 4], status: ['active', 'pending', 'inactive'][i % 3], })); // Generate progressive content sections const generateContentSections = () => [ { title: 'Introduction', content: 'This section introduces the streaming capabilities of Coherent.js.', highlight: 'Streams render content progressively for better performance.', }, { title: 'Benefits', content: 'Streaming provides improved perceived performance and reduced memory usage.', highlight: 'Large datasets can be processed without blocking the main thread.', }, { title: 'Implementation', content: 'Use renderToStream() to enable streaming for any component.', highlight: 'Works seamlessly with existing component architecture.', }, ]; // Real-time streaming feed component const StreamingFeed = ({ items = [], isLive = false }) => ({ div: { class: 'streaming-feed', children: [ { h4: { text: '📡 Live Data Stream' } }, { div: { class: 'feed-status', children: [ { span: { text: isLive ? '🟢 Live' : '🔴 Offline', class: 'status-indicator', }, }, { span: { text: `${items.length} items` } }, ], }, }, { div: { class: 'feed-container', children: items.map((item) => ({ div: { key: item.id, class: 'feed-item', children: [ { h6: { text: item.title } }, { p: { text: item.content } }, { small: { text: `${item.timestamp}ms ago` } }, ], }, })), }, }, ], }, }); // Streaming utilities const createStreamingDemo = async (component, options = {}) => { const { delay = 0 } = options; const stream = renderToStream(component); const chunks = []; for await (const chunk of stream) { chunks.push(chunk); if (delay > 0) { await new Promise((resolve) => setTimeout(resolve, delay)); } } return { totalChunks: chunks.length, totalSize: chunks.join('').length, averageChunkSize: chunks.reduce((sum, chunk) => sum + chunk.length, 0) / chunks.length, }; }; // Streaming demo component const StreamingDemo = () => { const styles = ` .demo { max-width: 1200px; margin: 0 auto; padding: 20px; font-family: system-ui, sans-serif; } .demo h2 { color: #333; border-bottom: 2px solid #667eea; padding-bottom: 10px; } .demo .section { margin: 30px 0; padding: 20px; background: #f8f9fa; border-radius: 8px; } .demo .grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(400px, 1fr)); gap: 20px; } .streaming-list, .streaming-table-container, .progressive-content, .streaming-feed { background: white; padding: 15px; border-radius: 5px; border: 1px solid #ddd; height: 100%; } .list-container { max-height: 200px; overflow-y: auto; } .list-item { padding: 5px; border-bottom: 1px solid #eee; } .streaming-table { width: 100%; border-collapse: collapse; font-size: 0.9em; } .streaming-table th, .streaming-table td { border: 1px solid #ddd; padding: 6px; text-align: left; } .streaming-table th { background: #f8f9fa; } .active-row { background: #d4edda; } .status-active { color: #155724; } .status-pending { color: #856404; } .status-inactive { color: #721c24; } .progress-info { margin-bottom: 10px; } .progress-bar { background: #e9ecef; padding: 5px; border-radius: 3px; font-family: monospace; } .content-section { margin: 15px 0; padding: 10px; border-left: 3px solid #667eea; } .highlight { background: #fff3cd; padding: 8px; border-radius: 3px; margin-top: 5px; font-style: italic; } .feed-status { margin-bottom: 10px; } .status-indicator { margin-right: 10px; } .feed-item { margin: 10px 0; padding: 10px; background: #f8f9fa; border-radius: 3px; } .api-example { background: #e3f2fd; padding: 15px; border-radius: 5px; margin: 20px 0; } .api-example pre { background: #263238; color: #eee; padding: 10px; border-radius: 3px; overflow-x: auto; } `; const sampleData = generateStreamingData(25); const contentSections = generateContentSections(); const feedItems = [ { id: 1, title: 'System Update', content: 'New streaming features deployed', timestamp: 1200, }, { id: 2, title: 'Performance Alert', content: 'Render time improved by 40%', timestamp: 800, }, { id: 3, title: 'Cache Status', content: 'Cache hit rate: 95%', timestamp: 400, }, ]; return { html: { children: [ { head: { children: [ { title: { text: 'Streaming Rendering Demo' } }, { style: { text: styles } }, ], }, }, { body: { children: [ { div: { class: 'demo', children: [ { h2: { text: '🌊 Streaming Rendering in Coherent.js' } }, { div: { class: 'section', children: [ { h3: { text: 'Live Streaming Examples' } }, { p: { text: 'These components demonstrate progressive rendering and real-time data streaming:', }, }, { div: { class: 'grid', children: [ StreamingList({ itemCount: 1000, title: 'Progressive List', }), StreamingDataTable({ rows: sampleData.slice(0, 15), showProgress: true, }), ], }, }, ], }, }, { div: { class: 'section', children: [ { h3: { text: 'Content & Data Streams' } }, { div: { class: 'grid', children: [ ProgressiveContent({ sections: contentSections, }), StreamingFeed({ items: feedItems, isLive: true, }), ], }, }, ], }, }, { div: { class: 'api-example', children: [ { h3: { text: '🔧 Streaming API Usage' } }, { ul: { children: [ { li: { text: 'Use renderToStream() for progressive rendering', }, }, { li: { text: 'Automatic chunking for optimal performance', }, }, { li: { text: 'Compatible with Express, Fastify, and Node.js HTTP', }, }, { li: { text: 'Real-time updates with WebSocket integration', }, }, ], }, }, { h4: { text: 'Example Implementation:' } }, { pre: { text: `import { renderToStream } from '@coherent.js/core'; // Express route with streaming app.get('/data', async (req, res) => { const stream = renderToStream(LargeDataComponent()); res.setHeader('Transfer-Encoding', 'chunked'); for await (const chunk of stream) { res.write(chunk); } res.end(); });`, }, }, ], }, }, ], }, }, ], }, }, ], }, }; }; export default StreamingDemo; export { StreamingList, StreamingDataTable, ProgressiveContent, StreamingFeed, generateStreamingData, createStreamingDemo, }; // Stream the page to stdout when this file is the program being run — `node examples/streaming.js`, // or the playground runner, which feeds the file on stdin — but not when it is // imported. `import.meta.main` says so exactly from Node 22.18; before that, // compare against argv[1], which is empty when the program came on stdin. const isProgram = import.meta.main ?? (!process.argv[1] || fileURLToPath(import.meta.url) === process.argv[1]); if (isProgram) { for await (const chunk of renderToStream(StreamingDemo())) process.stdout.write(chunk); process.stdout.write('\n'); }
Client-Side
Client Router
JSCoherentClient-side routing with prefetching, transitions, and guards.
▶️ Run:node examples/client-router-demo.js/** * @name Client Router * @category Client-Side * @description Client-side routing with prefetching, transitions, and guards. */ import { createRouter } from '@coherent.js/client/router'; import { render } from '@coherent.js/core'; // ============================================================================= // Page Components // ============================================================================= function HomePage() { return { div: { className: 'page home-page', children: [ { h1: { text: 'Home Page' } }, { p: { text: 'Welcome to the Coherent.js router demo!' } }, { nav: { children: [ { a: { href: '/about', text: 'About', className: 'nav-link' } }, { a: { href: '/products', text: 'Products', className: 'nav-link', }, }, { a: { href: '/users/123', text: 'User Profile', className: 'nav-link', }, }, ], }, }, ], }, }; } function AboutPage() { return { div: { className: 'page about-page', children: [ { h1: { text: 'About Us' } }, { p: { text: 'Learn more about our application' } }, { a: { href: '/', text: '← Back to Home' } }, ], }, }; } function ProductsPage() { const products = [ { id: 1, name: 'Product A', price: 29.99 }, { id: 2, name: 'Product B', price: 39.99 }, { id: 3, name: 'Product C', price: 49.99 }, ]; return { div: { className: 'page products-page', children: [ { h1: { text: 'Products' } }, { ul: { children: products.map((product) => ({ li: { children: [ { a: { href: `/products/${product.id}`, text: `${product.name} - $${product.price}`, }, }, ], }, })), }, }, { a: { href: '/', text: '← Back to Home' } }, ], }, }; } function ProductDetailPage({ params }) { const productId = params.id; return { div: { className: 'page product-detail-page', children: [ { h1: { text: `Product #${productId}` } }, { p: { text: `Details for product ${productId}` } }, { a: { href: '/products', text: '← Back to Products' } }, ], }, }; } function UserProfilePage({ params }) { const userId = params.id; return { div: { className: 'page user-profile-page', children: [ { h1: { text: `User Profile` } }, { p: { text: `User ID: ${userId}` } }, { a: { href: '/', text: '← Back to Home' } }, ], }, }; } function NotFoundPage() { return { div: { className: 'page not-found-page', children: [ { h1: { text: '404 - Not Found' } }, { p: { text: 'The page you are looking for does not exist.' } }, { a: { href: '/', text: '← Back to Home' } }, ], }, }; } // ============================================================================= // Router Configuration // ============================================================================= console.log('📦 Creating Router...\n'); // In a browser, call router.start() to follow popstate/hashchange and // intercept same-origin link clicks; push()/replace() then update the URL. // This demo runs in Node, so it drives the router directly. const router = createRouter({ mode: 'history', base: '/' }); let loggedIn = true; // `component` is a loader, called once on first navigation: return the // component, or use `() => import('./pages/About.js')` to code-split. router.addRoute('/', { component: () => HomePage }); router.addRoute('/about', { component: () => AboutPage, meta: { title: 'About Us' }, }); router.addRoute('/products', { component: () => ProductsPage }); router.addRoute('/products/:id', { component: () => ProductDetailPage }); router.addRoute('/users/:id', { component: () => UserProfilePage, // Guards may be async; returning false cancels the navigation. beforeEnter: (to) => { console.log( `🔒 Checking access to user ${to.params.id}: ${loggedIn ? 'granted' : 'denied'}` ); return loggedIn; }, }); router.addRoute('*', { component: () => NotFoundPage }); // ============================================================================= // Navigation Examples // ============================================================================= function describe(result) { const route = router.getCurrentRoute(); const html = route?.component ? render(route.component({ params: route.params, query: route.query })) : ''; console.log(` navigated: ${result} → ${route?.fullPath}`); console.log( ` params: ${JSON.stringify(route?.params)} query: ${JSON.stringify(route?.query)}` ); console.log(` html: ${html.slice(0, 80)}${html.length > 80 ? '…' : ''}`); } async function main() { console.log('='.repeat(80)); console.log('🧭 Router Navigation Examples'); console.log('='.repeat(80)); console.log('\nExample 1: Basic navigation'); describe(await router.push('/about')); console.log('\nExample 2: Dynamic route parameters'); describe(await router.push('/users/456')); console.log('\nExample 3: Query parameters and hash'); describe(await router.push('/products?category=electronics&sort=price#top')); console.log('\nExample 4: A guard cancelling a navigation'); loggedIn = false; console.log( ` navigated: ${await router.push('/users/789')} (still on ${router.getCurrentRoute().fullPath})` ); loggedIn = true; console.log('\nExample 5: Back and forward'); router.back(); // in a browser after start(), the popstate event drives this await new Promise((resolve) => setTimeout(resolve, 0)); describe('back'); router.forward(); await new Promise((resolve) => setTimeout(resolve, 0)); describe('forward'); console.log('\nExample 6: Replace the current entry'); describe(await router.replace('/products/123')); console.log('\nExample 7: Unknown paths fall back to the "*" route'); describe(await router.push('/does/not/exist')); console.log('\nExample 8: The last navigation wins'); const [slow, fast] = await Promise.all([ router.push('/about'), router.push('/'), ]); console.log( ` /about → ${slow}, / → ${fast}, current: ${router.getCurrentRoute().fullPath}` ); console.log('\n📊 Stats:', JSON.stringify(router.getStats())); } await main();Hydration
JSCoherentClient-side hydration with Islands Architecture.
▶️ Run:node examples/hydration-demo.js/** * @name Hydration * @category Client-Side * @description Client-side hydration with Islands Architecture. */ import { hydrate } from '@coherent.js/client'; import { fileURLToPath } from 'node:url'; import { withState, render } from '@coherent.js/core'; // Interactive counter with hydration support // Create a simple counter component that works with hydration const CounterComponent = withState({ count: 0, step: 1 })(({ state, props = {}, }) => { // Extract initial values from props with defaults const initialCount = props.initialCount !== undefined ? props.initialCount : 0; const initialStep = props.initialStep !== undefined ? props.initialStep : 1; // Initialize state with initial values if not already set if (state.count === undefined) { state.count = initialCount; } if (state.step === undefined) { state.step = initialStep; } return { div: { class: 'counter-widget', 'data-coherent-component': 'counter', // Add data attribute to identify component children: [ { h4: { text: 'Interactive Counter', class: 'widget-title', }, }, { div: { class: 'counter-display', children: [ { span: { text: `Count: ${state.count}`, class: 'count-value', 'data-ref': 'count', }, }, { span: { text: `Step: ${state.step}`, class: 'step-value', 'data-ref': 'step', }, }, ], }, }, { div: { class: 'counter-controls', children: [ { button: { text: '-', class: 'btn btn-secondary', onclick: (event, state, setState) => setState({ count: state.count - state.step }), }, }, { button: { text: '+', class: 'btn btn-primary', onclick: (event, state, setState) => setState({ count: state.count + state.step }), }, }, { button: { text: 'Reset Test', class: 'btn btn-outline', onclick: (event, state, setState) => setState({ count: initialCount }), }, }, ], }, }, { div: { class: 'step-controls', children: [ { label: { text: 'Step size:', class: 'step-label' } }, { input: { type: 'number', value: state.step, min: 1, max: 10, class: 'step-input', oninput: (event, state, setState) => setState({ step: parseInt(event.target.value) || 1 }), }, }, ], }, }, ], }, }; }); // Any pure-object component is hydratable: no wrapper needed (makeHydratable was removed in 1.0). const HydratableCounter = CounterComponent; // Interactive todo list with hydration support // Interactive user profile form with hydration support const HydratableUserProfile = withState({ firstName: 'John', lastName: 'Doe', age: 30, email: 'john.doe@example.com', bio: 'Software developer passionate about web technologies.', newsletter: true, })(({ state }) => { return { div: { class: 'profile-widget', 'data-coherent-component': 'user-profile', children: [ { h4: { text: 'Interactive User Profile', class: 'widget-title', }, }, { div: { class: 'profile-display', children: [ { p: { text: `Name: ${state.firstName} ${state.lastName}`, class: 'profile-info', }, }, { p: { text: `Age: ${state.age}`, class: 'profile-info' } }, { p: { text: `Email: ${state.email}`, class: 'profile-info' } }, { p: { text: `Status: ${state.age >= 18 ? 'Adult' : 'Minor'}`, class: `profile-status ${state.age >= 18 ? 'adult' : 'minor'}`, }, }, { p: { text: `Newsletter: ${state.newsletter ? 'Subscribed' : 'Not subscribed'}`, class: 'profile-info', }, }, ], }, }, { div: { class: 'profile-form', children: [ { div: { class: 'form-row', children: [ { input: { type: 'text', placeholder: 'First Name', value: state.firstName, class: 'form-input', oninput: (event, state, setState) => setState({ firstName: event.target.value }), }, }, { input: { type: 'text', placeholder: 'Last Name', value: state.lastName, class: 'form-input', oninput: (event, state, setState) => setState({ lastName: event.target.value }), }, }, ], }, }, { div: { class: 'form-row', children: [ { input: { type: 'number', placeholder: 'Age', value: state.age, min: 1, max: 120, class: 'form-input', oninput: (event, state, setState) => setState({ age: parseInt(event.target.value) || 0 }), }, }, { input: { type: 'email', placeholder: 'Email', value: state.email, class: 'form-input', oninput: (event, state, setState) => setState({ email: event.target.value }), }, }, ], }, }, { div: { class: 'form-row full-width', children: [ { textarea: { placeholder: 'Bio', value: state.bio, class: 'form-textarea', rows: 3, oninput: (event, state, setState) => setState({ bio: event.target.value }), }, }, ], }, }, { div: { class: 'form-row checkbox-row', children: [ { label: { class: 'checkbox-label', children: [ { input: { type: 'checkbox', checked: state.newsletter, class: 'form-checkbox', onchange: (event, state, setState) => setState({ newsletter: event.target.checked }), }, }, { span: { text: 'Subscribe to newsletter', class: 'checkbox-text', }, }, ], }, }, ], }, }, { div: { class: 'form-actions', children: [ { button: { text: 'Reset Profile', class: 'btn btn-outline', onclick: (event, state, setState) => setState({ firstName: 'John', lastName: 'Doe', age: 30, email: 'john.doe@example.com', bio: 'Software developer passionate about web technologies.', newsletter: true, }), }, }, ], }, }, ], }, }, ], }, }; }); const HydratableTodoList = withState({ todos: [], newTodo: '', filter: 'all' })( ({ state }) => { // Define functions that accept setState as parameter for hydration compatibility const addTodo = (event, state, setState) => { if (state.newTodo.trim()) { setState({ todos: [ ...state.todos, { id: Date.now(), text: state.newTodo.trim(), completed: false, }, ], newTodo: '', }); } }; const toggleTodo = (id) => (event, state, setState) => { setState({ todos: state.todos.map((todo) => todo.id === id ? { ...todo, completed: !todo.completed } : todo ), }); }; const removeTodo = (id) => (event, state, setState) => { setState({ todos: state.todos.filter((todo) => todo.id !== id), }); }; const setFilter = (filter) => (event, state, setState) => setState({ filter }); const filteredTodos = state.todos.filter((todo) => { if (state.filter === 'active') return !todo.completed; if (state.filter === 'completed') return todo.completed; return true; }); const stats = { total: state.todos.length, completed: state.todos.filter((t) => t.completed).length, active: state.todos.filter((t) => !t.completed).length, }; return { div: { class: 'todo-widget', 'data-coherent-component': 'todo-list', // Add data attribute to identify component children: [ { h4: { text: 'Interactive Todo List', class: 'widget-title', }, }, { div: { class: 'todo-stats', children: [ { span: { text: `Total: ${stats.total}`, class: 'stat-item' } }, { span: { text: `Active: ${stats.active}`, class: 'stat-item' }, }, { span: { text: `Completed: ${stats.completed}`, class: 'stat-item', }, }, ], }, }, { div: { class: 'todo-input', children: [ { input: { type: 'text', value: state.newTodo, placeholder: 'Add new todo...', class: 'todo-input-field', oninput: (e, state, setState) => setState({ newTodo: e.target.value }), onkeypress: (e, state, setState) => { if (e.key === 'Enter') addTodo(e, state, setState); }, }, }, { button: { text: 'Add', class: 'btn btn-primary', onclick: addTodo, }, }, ], }, }, { div: { class: 'todo-filters', children: [ { button: { text: 'All', class: `filter-btn ${state.filter === 'all' ? 'active' : ''}`, onclick: setFilter('all'), }, }, { button: { text: 'Active', class: `filter-btn ${state.filter === 'active' ? 'active' : ''}`, onclick: setFilter('active'), }, }, { button: { text: 'Completed', class: `filter-btn ${state.filter === 'completed' ? 'active' : ''}`, onclick: setFilter('completed'), }, }, ], }, }, { ul: { class: 'todo-list', children: filteredTodos.map((todo) => ({ li: { key: todo.id, class: `todo-item ${todo.completed ? 'completed' : ''}`, children: [ { input: { type: 'checkbox', checked: todo.completed, class: 'todo-checkbox', onchange: toggleTodo(todo.id), }, }, { span: { text: todo.text, class: 'todo-text', }, }, { button: { text: '×', class: 'btn btn-danger btn-small', onclick: removeTodo(todo.id), }, }, ], }, })), }, }, ], }, }; } ); // Complete hydration demo page export const hydrationDemo = { html: { children: [ { head: { children: [ { title: { text: 'Hydration Demo - Coherent.js' } }, { style: { text: ` body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; margin: 0; padding: 20px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); min-height: 100dvh; color: #2d3748; } .demo-container { max-width: 1200px; margin: 0 auto; background: white; border-radius: 12px; box-shadow: 0 10px 30px rgba(0,0,0,0.1); overflow: hidden; } .demo-header { background: linear-gradient(135deg, #4299e1 0%, #3182ce 100%); color: white; padding: 30px; text-align: center; } .demo-header h1 { margin: 0 0 10px 0; font-size: 2.5rem; font-weight: 700; } .demo-header p { margin: 0; font-size: 1.1rem; opacity: 0.9; } .demo-content { padding: 40px; } .demo-section { margin-bottom: 40px; padding: 30px; background: #f7fafc; border-radius: 8px; border-left: 4px solid #4299e1; } .section-title { color: #2b6cb0; margin: 0 0 15px 0; font-size: 1.5rem; font-weight: 600; } .section-description { color: #4a5568; margin-bottom: 25px; line-height: 1.6; } .widget-title { color: #2d3748; margin: 0 0 20px 0; font-size: 1.25rem; font-weight: 600; } .counter-widget, .todo-widget, .profile-widget { background: white; padding: 25px; border-radius: 8px; border: 1px solid #e2e8f0; box-shadow: 0 2px 4px rgba(0,0,0,0.05); } .counter-display { text-align: center; margin: 20px 0; padding: 15px; background: #f7fafc; border-radius: 6px; display: flex; justify-content: center; gap: 20px; } .count-value, .step-value { font-size: 1.25rem; font-weight: 600; color: #2b6cb0; } .counter-controls, .step-controls { display: flex; justify-content: center; gap: 10px; margin: 15px 0; flex-wrap: wrap; } .step-controls { align-items: center; } .step-label { margin-right: 10px; font-weight: 500; } .step-input { width: 60px; padding: 5px 8px; border: 1px solid #e2e8f0; border-radius: 4px; text-align: center; } .btn { padding: 10px 20px; border: none; border-radius: 6px; font-weight: 500; cursor: pointer; transition: all 0.2s ease; font-size: 14px; } .btn:hover { transform: translateY(-1px); box-shadow: 0 4px 8px rgba(0,0,0,0.15); } .btn-primary { background: #4299e1; color: white; } .btn-primary:hover { background: #3182ce; } .btn-secondary { background: #718096; color: white; } .btn-secondary:hover { background: #4a5568; } .btn-outline { background: transparent; color: #4299e1; border: 2px solid #4299e1; } .btn-outline:hover { background: #4299e1; color: white; } .btn-danger { background: #e53e3e; color: white; } .btn-danger:hover { background: #c53030; } .btn-small { padding: 5px 8px; font-size: 12px; min-width: 24px; } .todo-stats { display: flex; gap: 15px; margin-bottom: 20px; padding: 10px; background: #f7fafc; border-radius: 6px; } .stat-item { font-weight: 500; color: #4a5568; } .todo-input { display: flex; gap: 10px; margin-bottom: 20px; } .todo-input-field { flex: 1; padding: 10px; border: 1px solid #e2e8f0; border-radius: 6px; font-size: 14px; } .todo-input-field:focus { outline: none; border-color: #4299e1; box-shadow: 0 0 0 3px rgba(66, 153, 225, 0.1); } .todo-filters { display: flex; gap: 5px; margin-bottom: 20px; } .filter-btn { padding: 8px 16px; border: 1px solid #e2e8f0; background: white; color: #4a5568; border-radius: 4px; cursor: pointer; transition: all 0.2s ease; font-size: 13px; } .filter-btn:hover { border-color: #4299e1; color: #4299e1; } .filter-btn.active { background: #4299e1; color: white; border-color: #4299e1; } .todo-list { list-style: none; padding: 0; margin: 0; } .todo-item { display: flex; align-items: center; gap: 10px; padding: 12px; margin: 8px 0; background: white; border: 1px solid #e2e8f0; border-radius: 6px; transition: all 0.2s ease; } .todo-item:hover { border-color: #cbd5e0; box-shadow: 0 2px 4px rgba(0,0,0,0.05); } .todo-item.completed { opacity: 0.7; background: #f7fafc; } .todo-item.completed .todo-text { text-decoration: line-through; color: #718096; } .todo-checkbox { width: 16px; height: 16px; cursor: pointer; } .todo-text { flex: 1; color: #2d3748; } .profile-display { background: #f7fafc; padding: 20px; border-radius: 6px; margin-bottom: 25px; } .profile-info { margin: 8px 0; color: #4a5568; font-weight: 500; } .profile-status.adult { color: #38a169; font-weight: 600; } .profile-status.minor { color: #ed8936; font-weight: 600; } .profile-form { display: flex; flex-direction: column; gap: 15px; } .form-row { display: flex; gap: 15px; align-items: center; } .form-row.full-width { flex-direction: column; align-items: stretch; } .form-row.checkbox-row { justify-content: flex-start; } .form-input { padding: 10px 12px; border: 1px solid #e2e8f0; border-radius: 6px; flex: 1; min-width: 0; font-size: 14px; transition: border-color 0.2s ease; } .form-input:focus { outline: none; border-color: #4299e1; box-shadow: 0 0 0 3px rgba(66, 153, 225, 0.1); } .form-textarea { padding: 10px 12px; border: 1px solid #e2e8f0; border-radius: 6px; width: 100%; font-size: 14px; font-family: inherit; resize: vertical; transition: border-color 0.2s ease; } .form-textarea:focus { outline: none; border-color: #4299e1; box-shadow: 0 0 0 3px rgba(66, 153, 225, 0.1); } .checkbox-label { display: flex; align-items: center; gap: 8px; cursor: pointer; } .form-checkbox { width: 16px; height: 16px; cursor: pointer; } .checkbox-text { color: #4a5568; font-size: 14px; } .form-actions { display: flex; justify-content: flex-end; margin-top: 10px; } .hydration-info { background: #e6fffa; border: 1px solid #81e6d9; border-radius: 8px; padding: 20px; margin-top: 30px; } .hydration-info h3 { color: #234e52; margin: 0 0 10px 0; } .hydration-info p { color: #285e61; margin: 0; line-height: 1.6; } `, }, }, ], }, }, { body: { children: [ { div: { class: 'demo-container', children: [ { div: { class: 'demo-header', children: [ { h1: { text: 'Hydration Demo' } }, { p: { text: 'Interactive components with server-side rendering and client-side hydration', }, }, ], }, }, { div: { class: 'demo-content', children: [ { div: { class: 'demo-section', children: [ { h2: { text: 'Interactive Counter', class: 'section-title', }, }, { p: { text: 'A stateful counter with step control. State is preserved during hydration and updates are reactive.', class: 'section-description', }, }, HydratableCounter({ initialCount: 5 }), ], }, }, { div: { class: 'demo-section', children: [ { h2: { text: 'Interactive User Profile', class: 'section-title', }, }, { p: { text: 'A form component with various input types, computed properties, and real-time validation.', class: 'section-description', }, }, HydratableUserProfile(), ], }, }, { div: { class: 'demo-section', children: [ { h2: { text: 'Interactive Todo List', class: 'section-title', }, }, { p: { text: 'A complex stateful component with filtering, statistics, and real-time interactions.', class: 'section-description', }, }, HydratableTodoList(), ], }, }, { div: { class: 'hydration-info', children: [ { h3: { text: 'About Hydration' } }, { p: { text: 'This page demonstrates client-side hydration where server-rendered HTML becomes interactive on the client. Components maintain their state and event handlers are attached seamlessly.', }, }, ], }, }, ], }, }, ], }, }, ], }, }, ], }, }; const HydrationDemoPage = () => hydrationDemo; // In the browser, hydrate each server-rendered root explicitly // (autoHydrate was removed in 1.0). if (typeof window !== 'undefined') { const roots = { counter: [HydratableCounter, { initialCount: 5 }], 'user-profile': [HydratableUserProfile, {}], 'todo-list': [HydratableTodoList, {}], }; for (const [name, [Component, props]] of Object.entries(roots)) { const element = document.querySelector( `[data-coherent-component="${name}"]` ); if (element) hydrate(Component, element, { props }); } } // Export the demo page as default for live preview export default HydrationDemoPage; // Print the page when this file is the program being run — `node examples/hydration-demo.js`, // or the playground runner, which feeds the file on stdin — but not when it is // imported. `import.meta.main` says so exactly from Node 22.18; before that, // compare against argv[1], which is empty when the program came on stdin. const isProgram = import.meta.main ?? (!process.argv[1] || fileURLToPath(import.meta.url) === process.argv[1]); if (isProgram) { console.log(render(HydrationDemoPage())); }
Integrations
Express Integration
JSCoherentUsing Coherent.js with Express.js for SSR.
▶️ Run:node examples/express-integration.js/** * @name Express Integration * @category Integrations * @description Using Coherent.js with Express.js for SSR. */ import express from 'express'; import { createCoherentHandler, setupCoherent, } from '@coherent.js/integrations/express'; const app = express(); const PORT = process.env.PORT || 3000; // Setup Coherent.js with Express setupCoherent(app, { useMiddleware: true, useEngine: false, enablePerformanceMonitoring: true, }); // Express integration home page component function ExpressHomePage({ name = 'Express Developer' }) { const styles = ` body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; max-width: 900px; margin: 0 auto; padding: 40px 20px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); min-height: 100dvh; color: white; } .container { background: rgba(255,255,255,0.1); border-radius: 20px; padding: 40px; backdrop-filter: blur(10px); } .header { text-align: center; margin-bottom: 40px; } .header h1 { font-size: 3em; margin: 0; text-shadow: 2px 2px 4px rgba(0,0,0,0.3); } .header p { font-size: 1.2em; margin: 10px 0; opacity: 0.9; } .features { display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 20px; margin: 30px 0; } .feature { background: rgba(255,255,255,0.1); padding: 20px; border-radius: 10px; } .feature h3 { margin: 0 0 10px 0; color: #ffd700; } .links { text-align: center; margin-top: 30px; } .links a { color: #ffd700; text-decoration: none; margin: 0 15px; font-weight: bold; } .links a:hover { text-decoration: underline; } `; return { html: { children: [ { head: { children: [ { title: { text: 'Coherent.js + Express Integration' } }, { style: { text: styles } }, ], }, }, { body: { children: [ { div: { class: 'container', children: [ { div: { class: 'header', children: [ { h1: { text: '🚀 Express + Coherent.js' } }, { p: { text: `Welcome, ${name}!` } }, { p: { text: 'Server-side rendering made simple' } }, ], }, }, { div: { class: 'features', children: [ { div: { class: 'feature', children: [ { h3: { text: '⚡ Fast Rendering' } }, { p: { text: 'Server-side rendering with automatic optimization', }, }, ], }, }, { div: { class: 'feature', children: [ { h3: { text: '🔧 Easy Integration' } }, { p: { text: 'Drop-in middleware for existing Express apps', }, }, ], }, }, { div: { class: 'feature', children: [ { h3: { text: '📊 Performance Monitoring' } }, { p: { text: 'Built-in performance tracking and metrics', }, }, ], }, }, { div: { class: 'feature', children: [ { h3: { text: '🛡️ Type Safety' } }, { p: { text: 'Full TypeScript support and type safety', }, }, ], }, }, ], }, }, { div: { class: 'links', children: [ { a: { href: '/user/demo', text: '👤 User Profile Demo', }, }, { a: { href: '/api/status', text: '📡 API Status' } }, ], }, }, ], }, }, ], }, }, ], }, }; } // User profile component using Express request data function ExpressUserPage(req) { const { username } = req.params; const userAgent = req.get('User-Agent') || 'Unknown'; const timestamp = new Date().toLocaleString(); const styles = ` body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; max-width: 800px; margin: 0 auto; padding: 40px 20px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); min-height: 100dvh; color: white; } .profile { background: rgba(255,255,255,0.1); border-radius: 20px; padding: 40px; backdrop-filter: blur(10px); } .profile h1 { text-align: center; margin-bottom: 30px; text-shadow: 2px 2px 4px rgba(0,0,0,0.3); } .info { background: rgba(255,255,255,0.1); padding: 20px; border-radius: 10px; margin: 20px 0; } .info h3 { margin: 0 0 10px 0; color: #ffd700; } .info p { margin: 5px 0; opacity: 0.9; } .back-link { display: inline-block; margin-top: 20px; color: #ffd700; text-decoration: none; font-weight: bold; } .back-link:hover { text-decoration: underline; } `; return { html: { children: [ { head: { children: [ { title: { text: `User Profile - ${username}` } }, { style: { text: styles } }, ], }, }, { body: { children: [ { div: { class: 'profile', children: [ { h1: { text: `👤 ${username}` } }, { div: { class: 'info', children: [ { h3: { text: '🔗 Request Info' } }, { p: { text: `Path: ${req.path}` } }, { p: { text: `Method: ${req.method}` } }, { p: { text: `Timestamp: ${timestamp}` } }, ], }, }, { div: { class: 'info', children: [ { h3: { text: '🌐 Browser Info' } }, { p: { text: `User Agent: ${userAgent.substring(0, 80)}${userAgent.length > 80 ? '...' : ''}`, }, }, { p: { text: `IP: ${req.ip || 'Unknown'}` } }, ], }, }, { a: { href: '/', text: '← Back to Home', class: 'back-link', }, }, ], }, }, ], }, }, ], }, }; } // Routes app.get('/', (req, res) => { res.coherent(ExpressHomePage({ name: 'Express Developer' })); }); app.get( '/user/:username', createCoherentHandler((req) => { return ExpressUserPage(req); }) ); app.get('/api/status', (req, res) => { res.json({ status: 'ok', framework: 'Coherent.js with Express', timestamp: new Date().toISOString(), uptime: process.uptime(), }); }); // Error handling. Express only treats middleware as an error handler when it // declares four parameters; with three, this ran on every unmatched request // and failed on `res.status` (res was really `next`), turning 404s into 500s. app.use((err, req, res, _next) => { res.status(500).send({ error: 'Internal Server Error', message: err.message, }); }); // Start server only if run directly (not imported as module) if (import.meta.url === `file://${process.argv[1]}`) { app.listen(PORT, () => { if (process.env.NODE_ENV !== 'production') { console.log(`Express + Coherent.js server: http://localhost:${PORT}`); } }); } // Demo component for live preview const ExpressIntegrationDemo = () => { const styles = ` .demo { max-width: 800px; margin: 0 auto; padding: 20px; font-family: system-ui, sans-serif; } .demo h2 { color: #333; border-bottom: 2px solid #667eea; padding-bottom: 10px; } .demo .section { margin: 30px 0; padding: 20px; background: #f8f9fa; border-radius: 8px; } .demo pre { background: #2d3748; color: #e2e8f0; padding: 15px; border-radius: 5px; overflow-x: auto; } .demo .highlight { background: #ffd700; padding: 2px 4px; border-radius: 3px; } `; return { html: { children: [ { head: { children: [ { title: { text: 'Express.js Integration Demo' } }, { style: { text: styles } }, ], }, }, { body: { children: [ { div: { class: 'demo', children: [ { h2: { text: '🚀 Express.js Integration with Coherent.js', }, }, { div: { class: 'section', children: [ { h3: { text: 'Setup' } }, { p: { text: 'Install the Express integration:' } }, { pre: { text: 'npm install @coherent/express' } }, { p: { text: 'Configure your Express app:' } }, { pre: { text: `import { setupCoherent } from '@coherent.js/integrations/express'; setupCoherent(app, { useMiddleware: true, enablePerformanceMonitoring: true });`, }, }, ], }, }, { div: { class: 'section', children: [ { h3: { text: 'Features' } }, { ul: { children: [ { li: { text: 'Automatic component rendering with Express middleware', }, }, { li: { text: 'Request data injection into components', }, }, { li: { text: 'Performance monitoring and metrics', }, }, { li: { text: 'Error handling and debugging support', }, }, ], }, }, ], }, }, { div: { class: 'section', children: [ { h3: { text: 'Usage Example' } }, { p: { text: 'Create routes that return Coherent.js components:', }, }, { pre: { text: `app.get('/', (req, res) => { res.coherent(HomePage({ user: req.user })); }); app.get('/user/:id', createCoherentHandler((req) => { return UserProfile(req.params.id); }));`, }, }, ], }, }, ], }, }, ], }, }, ], }, }; }; export default ExpressIntegrationDemo; export { app };Next.js Integration
JSCoherentUsing Coherent.js components inside Next.js applications.
▶️ Run:node examples/nextjs-integration.js/** * @name Next.js Integration * @category Integrations * @description Using Coherent.js components inside Next.js applications. */ import { createCoherentNextHandler } from '@coherent.js/integrations/nextjs'; // Enhanced Next.js home page component export const NextHomePage = ({ name = 'World', timestamp = new Date().toISOString(), }) => ({ html: { children: [ { head: { children: [ { title: { text: 'Coherent.js + Next.js Integration' } }, { style: { text: ` body { font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; max-width: 900px; margin: 0 auto; padding: 20px; background: #f8fafc; line-height: 1.6; } .container { background: white; padding: 40px; border-radius: 12px; box-shadow: 0 4px 20px rgba(0,0,0,0.1); } .header { text-align: center; margin-bottom: 40px; padding-bottom: 20px; border-bottom: 2px solid #e2e8f0; } .header h1 { color: #1a202c; margin-bottom: 10px; font-size: 2.5em; font-weight: 300; } .header p { color: #4a5568; font-size: 1.2em; } .content { margin: 30px 0; } .features-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 20px; margin: 30px 0; } .feature-card { padding: 20px; background: #f7fafc; border-radius: 8px; border-left: 4px solid #3182ce; } .feature-card h3 { color: #2d3748; margin-bottom: 10px; } .feature-card p { color: #4a5568; margin: 0; } .info-section { background: #edf2f7; padding: 20px; border-radius: 8px; margin: 30px 0; } .info-item { margin: 10px 0; padding: 10px; background: white; border-radius: 6px; border-left: 4px solid #38a169; } .footer { margin-top: 40px; text-align: center; padding-top: 20px; border-top: 1px solid #e2e8f0; color: #718096; font-size: 0.9em; } `, }, }, ], }, }, { body: { children: [ { div: { className: 'container', children: [ { div: { className: 'header', children: [ { h1: { text: 'Coherent.js + Next.js' } }, { p: { text: `Welcome, ${name}! Experience seamless Next.js integration with server-side rendering.`, }, }, ], }, }, { div: { className: 'content', children: [ { h2: { text: 'Integration Features' } }, { div: { className: 'features-grid', children: [ { div: { className: 'feature-card', children: [ { h3: { text: '🚀 API Route Integration' }, }, { p: { text: 'Seamless integration with Next.js API routes for server-side rendering.', }, }, ], }, }, { div: { className: 'feature-card', children: [ { h3: { text: '📊 Performance Monitoring' }, }, { p: { text: 'Built-in performance tracking and optimization for Next.js applications.', }, }, ], }, }, { div: { className: 'feature-card', children: [ { h3: { text: '🔧 Zero Configuration' } }, { p: { text: 'Drop-in integration with existing Next.js projects without configuration.', }, }, ], }, }, { div: { className: 'feature-card', children: [ { h3: { text: '⚡ Static Generation' } }, { p: { text: 'Compatible with Next.js static generation and incremental static regeneration.', }, }, ], }, }, ], }, }, { div: { className: 'info-section', children: [ { h3: { text: 'Request Information' } }, { div: { className: 'info-item', children: [ { strong: { text: 'Rendered At: ' } }, { span: { text: new Date( timestamp ).toLocaleString(), }, }, ], }, }, { div: { className: 'info-item', children: [ { strong: { text: 'Framework: ' } }, { span: { text: 'Next.js with Coherent.js', }, }, ], }, }, { div: { className: 'info-item', children: [ { strong: { text: 'Rendering: ' } }, { span: { text: 'Server-Side Rendering (SSR)', }, }, ], }, }, ], }, }, ], }, }, { div: { className: 'footer', children: [ { p: { text: 'Powered by Coherent.js and Next.js • Built for modern web applications', }, }, ], }, }, ], }, }, ], }, }, ], }, }); // Enhanced user profile component for Next.js integration export async function getServerSideProps(_context) { const { username } = _context.query; const userAgent = _context.req.headers['user-agent'] || 'Unknown'; const timestamp = new Date().toLocaleString(); const method = _context.req.method || 'GET'; return { html: { children: [ { head: { children: [ { title: { text: `${username}'s Profile - Next.js + Coherent.js`, }, }, { style: { text: ` body { font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; max-width: 800px; margin: 0 auto; padding: 20px; background: #f8fafc; } .profile-container { background: white; padding: 30px; border-radius: 12px; box-shadow: 0 4px 20px rgba(0,0,0,0.1); } .profile-header { text-align: center; margin-bottom: 30px; padding-bottom: 20px; border-bottom: 2px solid #e2e8f0; } .profile-info { background: #f7fafc; padding: 20px; border-radius: 8px; margin: 20px 0; } .info-item { margin: 10px 0; padding: 10px; background: white; border-radius: 6px; border-left: 4px solid #3182ce; } .back-link { display: inline-block; padding: 10px 20px; background: #3182ce; color: white; text-decoration: none; border-radius: 6px; margin-top: 20px; } .next-badge { background: #000; color: white; padding: 4px 8px; border-radius: 4px; font-size: 0.8em; margin-left: 10px; } `, }, }, ], }, }, { body: { children: [ { div: { className: 'profile-container', children: [ { div: { className: 'profile-header', children: [ { h1: { children: [ { span: { text: `👤 ${username}'s Profile` } }, { span: { text: 'Next.js', className: 'next-badge', }, }, ], }, }, { p: { text: 'User profile rendered with Next.js API routes and Coherent.js integration', }, }, ], }, }, { div: { className: 'profile-info', children: [ { h3: { text: 'Request Information' } }, { div: { className: 'info-item', children: [ { strong: { text: 'Username: ' } }, { span: { text: username } }, ], }, }, { div: { className: 'info-item', children: [ { strong: { text: 'Request Method: ' } }, { span: { text: method } }, ], }, }, { div: { className: 'info-item', children: [ { strong: { text: 'Request Path: ' } }, { span: { text: _context.req.url || '/api/user/[username]', }, }, ], }, }, { div: { className: 'info-item', children: [ { strong: { text: 'User Agent: ' } }, { span: { text: userAgent.substring(0, 100) + (userAgent.length > 100 ? '...' : ''), }, }, ], }, }, { div: { className: 'info-item', children: [ { strong: { text: 'Rendered At: ' } }, { span: { text: timestamp } }, ], }, }, { div: { className: 'info-item', children: [ { strong: { text: 'Framework: ' } }, { span: { text: 'Next.js + Coherent.js' } }, ], }, }, ], }, }, { a: { href: '/api/home', text: '← Back to Home', className: 'back-link', }, }, ], }, }, ], }, }, ], }, }; } // Create optimized Next.js API route handlers export const homeHandler = createCoherentNextHandler( () => { return NextHomePage({ name: 'Next.js Developer', timestamp: new Date().toISOString(), }); }, { enablePerformanceMonitoring: true, cacheComponents: true, } ); export const userHandler = createCoherentNextHandler( (req) => { return NextUserPage(req); }, { enablePerformanceMonitoring: true, } ); // API status handler for health checks export const statusHandler = createCoherentNextHandler((req, res) => { // Return JSON response for API status res.status(200).json({ status: 'ok', framework: 'Next.js + Coherent.js', timestamp: new Date().toISOString(), version: '1.0.0', }); }); // Export as default for Next.js API routes export default function handler(_req, _res) { // Return JSON response for API status _res.status(200).json({ status: 'ok', framework: 'Next.js + Coherent.js', timestamp: new Date().toISOString(), version: '1.0.0', }); } /** * Usage Instructions: * * To use these handlers in your Next.js application: * * 1. Create pages/api/home.js: * export { homeHandler as default } from '../../examples/nextjs-integration.js'; * * 2. Create pages/api/user/[username].js: * export { userHandler as default } from '../../../examples/nextjs-integration.js'; * * 3. Create pages/api/status.js: * export { statusHandler as default } from '../../examples/nextjs-integration.js'; */
Full Apps
DevTools
JSCoherentDeveloper tools with component inspector, profiler, and logger.
▶️ Run:node examples/devtools-demo.js/** * @name DevTools * @category Full Apps * @description Developer tools with component inspector, profiler, and logger. */ import { createInspector, inspect, validateComponent, createProfiler, measure, createLogger, LogLevel, } from '@coherent.js/devtools'; console.log('\n=== Coherent.js DevTools Demo ===\n'); // Example 1: Component Inspector console.log('--- Example 1: Component Inspector ---\n'); const testComponent = { div: { className: 'container', id: 'main', children: [ { h1: { text: 'Hello World' } }, { p: { className: 'description', text: 'This is a test component', }, }, { ul: { children: [ { li: { text: 'Item 1' } }, { li: { text: 'Item 2' } }, { li: { text: 'Item 3' } }, ], }, }, ], }, }; const inspector = createInspector({ verbose: true }); const inspection = inspector.inspect(testComponent, { name: 'TestComponent' }); console.log('Inspection Results:'); console.log(`- ID: ${inspection.id}`); console.log(`- Valid: ${inspection.analysis.valid}`); console.log(`- Root Elements: ${inspection.analysis.rootElements.join(', ')}`); console.log(`- Element Count: ${inspection.stats.elementCount}`); console.log(`- Tree Depth: ${inspection.stats.depth}`); console.log(`- Text Nodes: ${inspection.stats.textNodes}`); console.log(`- Has Styles: ${inspection.stats.hasStyles}`); console.log(`- Has Classes: ${inspection.stats.hasClasses}`); if (inspection.analysis.issues.length > 0) { console.log('\nIssues:'); inspection.analysis.issues.forEach((issue) => console.log(` - ${issue}`)); } if (inspection.analysis.warnings.length > 0) { console.log('\nWarnings:'); inspection.analysis.warnings.forEach((warning) => console.log(` - ${warning}`) ); } // Example 2: Component Validation console.log('\n--- Example 2: Component Validation ---\n'); const validComponent = { div: { children: [{ h1: { text: 'Valid' } }], }, }; const invalidComponent = { div: { children: 'Should be array', // Invalid! }, }; console.log('Validating valid component:'); const validation1 = validateComponent(validComponent); console.log(`- Valid: ${validation1.valid}`); console.log(`- Issues: ${validation1.issues.length}`); console.log('\nValidating invalid component:'); const validation2 = validateComponent(invalidComponent); console.log(`- Valid: ${validation2.valid}`); console.log(`- Issues: ${validation2.issues.join(', ')}`); // Example 3: Performance Profiler console.log('\n--- Example 3: Performance Profiler ---\n'); // Profilers are opt-in: one that is merely constructed records nothing. const profiler = createProfiler({ enabled: true, slowThreshold: 10, trackMemory: true, }); const sessionId = profiler.start('demo-session'); console.log(`Started profiling session: ${sessionId}`); // Simulate some renders for (let i = 0; i < 5; i++) { const measureId = profiler.startRender(`Component${i}`); // Simulate work const start = Date.now(); while (Date.now() - start < Math.random() * 20) { // Busy wait } profiler.endRender(measureId); } const sessionAnalysis = profiler.stop(sessionId); console.log('\nSession Analysis:'); console.log(`- Duration: ${sessionAnalysis.duration}ms`); console.log(`- Measurements: ${sessionAnalysis.measurements}`); console.log(`- Average: ${sessionAnalysis.analysis.average.toFixed(2)}ms`); console.log(`- Median: ${sessionAnalysis.analysis.median.toFixed(2)}ms`); console.log(`- Min: ${sessionAnalysis.analysis.min.toFixed(2)}ms`); console.log(`- Max: ${sessionAnalysis.analysis.max.toFixed(2)}ms`); console.log(`- P95: ${sessionAnalysis.analysis.p95.toFixed(2)}ms`); console.log(`- Slow Renders: ${sessionAnalysis.analysis.slowRenders}`); console.log(`- Slow %: ${sessionAnalysis.analysis.slowPercentage.toFixed(1)}%`); console.log('\nBy Component:'); Object.entries(sessionAnalysis.byComponent).forEach(([name, stats]) => { console.log( ` ${name}: ${stats.count} renders, avg ${stats.average.toFixed(2)}ms` ); }); // Example 4: Measure Function console.log('\n--- Example 4: Measure Function ---\n'); const slowFunction = async () => { return new Promise((resolve) => { setTimeout(() => resolve('Done'), 50); }); }; console.log('Measuring async function...'); await measure('SlowFunction', slowFunction, profiler); const summary = profiler.getSummary(); console.log(`\nTotal measurements: ${summary.totalMeasurements}`); console.log(`Slow renders: ${summary.slowRenders}`); // Example 5: Development Logger console.log('\n--- Example 5: Development Logger ---\n'); const logger = createLogger({ level: LogLevel.DEBUG, prefix: '[Demo]', timestamp: true, colors: true, }); logger.debug('Debug message', { detail: 'This is debug info' }); logger.info('Info message', { status: 'ok' }); logger.warn('Warning message', { code: 'WARN_001' }); logger.error('Error message', { error: 'Something failed' }); // Example 6: Log Grouping console.log('\n--- Example 6: Log Grouping ---\n'); logger.group('Rendering Phase'); logger.info('Starting render'); logger.debug('Processing components'); logger.info('Render complete'); logger.groupEnd(); logger.group('API Calls'); logger.info('Fetching data'); logger.debug('Request sent', { url: '/api/data' }); logger.info('Data received'); logger.groupEnd(); // Example 7: Log Filtering console.log('\n--- Example 7: Log Filtering ---\n'); const filteredLogger = createLogger({ level: LogLevel.WARN, // Only warnings and errors }); filteredLogger.debug('This will not be logged'); filteredLogger.info('This will not be logged'); filteredLogger.warn('This WILL be logged'); filteredLogger.error('This WILL be logged'); // Example 8: Custom Log Handler console.log('\n--- Example 8: Custom Log Handler ---\n'); const handlerLogger = createLogger(); const errorLogs = []; handlerLogger.addHandler((logEntry) => { if (logEntry.level >= LogLevel.ERROR) { errorLogs.push(logEntry); } }); handlerLogger.info('Normal log'); handlerLogger.error('Error 1'); handlerLogger.error('Error 2'); console.log(`Captured ${errorLogs.length} error logs`); // Example 9: Log Statistics console.log('\n--- Example 9: Log Statistics ---\n'); const stats = logger.getStats(); console.log('Logger Statistics:'); console.log(`- Total Logs: ${stats.total}`); console.log('- By Level:'); Object.entries(stats.byLevel).forEach(([level, count]) => { if (count > 0) { console.log(` ${level}: ${count}`); } }); // Example 10: Inspector Report console.log('\n--- Example 10: Inspector Report ---\n'); // Inspect multiple components inspector.inspect({ div: { text: 'Component 1' } }); inspector.inspect({ div: { text: 'Component 2' } }); inspector.inspect({ div: { children: [] } }); // Empty children const report = inspector.generateReport(); console.log('Inspector Report:'); console.log(`- Total Inspections: ${report.totalInspections}`); console.log(`- Components Tracked: ${report.componentsTracked}`); console.log(`- History Size: ${report.historySize}`); console.log(`- Total Elements: ${report.summary.totalElements}`); console.log(`- Average Depth: ${report.summary.averageDepth.toFixed(2)}`); console.log(`- With Issues: ${report.summary.componentsWithIssues}`); console.log(`- With Warnings: ${report.summary.componentsWithWarnings}`); // Example 11: Component Comparison console.log('\n--- Example 11: Component Comparison ---\n'); const componentA = { div: { children: [{ h1: { text: 'Title' } }, { p: { text: 'Content' } }], }, }; const componentB = { div: { children: [ { h1: { text: 'Title' } }, { p: { text: 'Content' } }, { footer: { text: 'Footer' } }, ], }, }; const comparison = inspector.compare(componentA, componentB); console.log('Component Comparison:'); console.log( `- Element Count Diff: ${comparison.statsComparison.elementCount.diff}` ); console.log(`- Depth Diff: ${comparison.statsComparison.depth.diff}`); console.log(`- Structure Match: ${comparison.structureMatch}`); // Example 12: Search Components console.log('\n--- Example 12: Search Components ---\n'); const searchResults = inspector.search({ minElements: 2, hasWarnings: false, }); console.log(`Found ${searchResults.length} components matching criteria`); // Example 13: Export Logs console.log('\n--- Example 13: Export Logs ---\n'); const jsonExport = logger.export('json'); console.log(`Exported ${jsonExport.length} characters as JSON`); const textExport = logger.export('text'); console.log(`Exported ${textExport.split('\n').length} lines as text`); console.log('\n=== Demo Complete ===\n'); console.log('DevTools Features:'); console.log('✅ Component Inspector - Analyze component structure'); console.log('✅ Component Validation - Check for issues'); console.log('✅ Performance Profiler - Measure render times'); console.log('✅ Session Analysis - Track profiling sessions'); console.log('✅ Development Logger - Structured logging'); console.log('✅ Log Grouping - Organize logs'); console.log('✅ Log Filtering - Control verbosity'); console.log('✅ Custom Handlers - Extend functionality'); console.log('✅ Statistics & Reports - Analyze data'); console.log('✅ Export Capabilities - Save logs');Forms & Validation
JSCoherentUser registration form with validation, built from pure objects.
▶️ Run:node examples/forms-complete-example.js/** * @name Forms & Validation * @category Full Apps * @description User registration form with validation, built from pure objects. */ import { render } from '@coherent.js/core'; import { createFormBuilder, validate, validators } from '@coherent.js/forms'; // ============================================================================ // Validation schema // ============================================================================ const registrationSchema = { username: [ validators.required(), validators.minLength(3), validators.maxLength(20), ], email: [validators.required(), validators.email()], password: [validators.required(), validators.minLength(8)], website: [validators.url()], }; // ============================================================================ // Form component // ============================================================================ export function RegistrationForm() { const form = createFormBuilder({ fields: [ { name: 'username', type: 'text', label: 'Username', required: true }, { name: 'email', type: 'email', label: 'Email', required: true }, { name: 'password', type: 'password', label: 'Password', required: true }, { name: 'website', type: 'url', label: 'Website (optional)' }, ], }); return form.buildForm({ submitText: 'Create account' }); } export function App() { return { div: { className: 'registration-page', children: [ { h1: { text: 'Create your account' } }, { p: { text: 'All components are pure JavaScript objects — no JSX.' } }, RegistrationForm(), ], }, }; } // ============================================================================ // Demo — render the form and run the validators // ============================================================================ console.log('='.repeat(80)); console.log('Forms & Validation example'); console.log('='.repeat(80)); console.log('\n📝 Rendering registration form...'); const htmlOut = render(App()); console.log(`✅ Rendered ${htmlOut.length} characters`); console.log( htmlOut.includes('name="username"') ? ' Contains username field ✓' : ' Missing username field ✗' ); console.log('\n🔍 Validating good data...'); const good = await validate( { username: 'ada', email: 'ada@example.com', password: 'correct-horse-battery', website: 'https://example.com', }, registrationSchema ); console.log(`✅ isValid: ${good.isValid}`); console.log('\n🔍 Validating bad data...'); const bad = await validate( { username: 'ab', email: 'not-an-email', password: 'short', website: 'nope' }, registrationSchema ); console.log(`❌ isValid: ${bad.isValid}`); for (const [field, message] of Object.entries(bad.errors)) { console.log(` ${field}: ${message}`); } console.log(`\n${'='.repeat(80)}`); console.log('✅ Forms example complete!'); console.log('='.repeat(80)); export { registrationSchema };Internationalization
JSCoherentMulti-language blog page with locale switching and RTL support.
▶️ Run:node examples/i18n-complete-example.js/** * @name Internationalization * @category Full Apps * @description Multi-language blog page with locale switching and RTL support. */ import { render } from '@coherent.js/core'; import { createTranslator, createFormatters, getLocaleDirection, } from '@coherent.js/i18n'; // ============================================================================ // Translations // ============================================================================ const translations = { en: { app: { title: 'My Blog', tagline: 'Thoughts on web development' }, nav: { home: 'Home', about: 'About', contact: 'Contact' }, post: { published: 'Published on {{date}}', readMore: 'Read more' }, }, fr: { app: { title: 'Mon Blog', tagline: 'Réflexions sur le développement web' }, nav: { home: 'Accueil', about: 'À propos', contact: 'Contact' }, post: { published: 'Publié le {{date}}', readMore: 'Lire la suite' }, }, es: { app: { title: 'Mi Blog', tagline: 'Reflexiones sobre desarrollo web' }, nav: { home: 'Inicio', about: 'Acerca de', contact: 'Contacto' }, post: { published: 'Publicado el {{date}}', readMore: 'Leer más' }, }, ar: { app: { title: 'مدونتي', tagline: 'أفكار حول تطوير الويب' }, nav: { home: 'الرئيسية', about: 'حول', contact: 'اتصل' }, post: { published: 'نُشر في {{date}}', readMore: 'اقرأ المزيد' }, }, }; const translator = createTranslator({ defaultLocale: 'en', fallbackLocale: 'en', }); for (const [locale, messages] of Object.entries(translations)) { translator.addTranslations(locale, messages); } // ============================================================================ // Components — pure objects, translator passed as a prop // ============================================================================ function Nav({ t }) { return { nav: { children: [ { a: { href: '/', text: t('nav.home') } }, { a: { href: '/about', text: t('nav.about') } }, { a: { href: '/contact', text: t('nav.contact') } }, ], }, }; } function Post({ t, fmt, title, body, date }) { return { article: { children: [ { h2: { text: title } }, { p: { className: 'meta', text: t('post.published', { date: fmt.date.format(date) }), }, }, { p: { text: body } }, { a: { href: '#', text: t('post.readMore') } }, ], }, }; } export function App({ locale = 'en' } = {}) { translator.setLocale(locale); const t = (key, params) => translator.t(key, params); const fmt = createFormatters(locale); return { html: { lang: locale, dir: getLocaleDirection(locale), children: [ { body: { children: [ { h1: { text: t('app.title') } }, { p: { text: t('app.tagline') } }, Nav({ t }), Post({ t, fmt, title: t('app.title'), body: t('app.tagline'), date: new Date(2026, 0, 15), }), ], }, }, ], }, }; } // ============================================================================ // Demo — render the page in several locales // ============================================================================ console.log('='.repeat(80)); console.log('Internationalization example'); console.log('='.repeat(80)); for (const locale of ['en', 'fr', 'es', 'ar']) { const out = render(App({ locale })); const direction = getLocaleDirection(locale); console.log( `\n📝 ${locale} (${direction}): rendered ${out.length} characters` ); console.log(` Title: ${translations[locale].app.title}`); } console.log(`\n${'='.repeat(80)}`); console.log('✅ All languages rendered successfully!'); console.log('='.repeat(80)); export { translations, translator };