@coherent.js/nextjs 1.0.0-beta.2 → 1.0.0-beta.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,521 @@
1
+ # @coherent.js/nextjs
2
+
3
+ Next.js integration for Coherent.js - Bring Coherent.js components to your Next.js applications.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @coherent.js/nextjs
9
+ # or
10
+ pnpm add @coherent.js/nextjs
11
+ # or
12
+ yarn add @coherent.js/nextjs
13
+ ```
14
+
15
+ **Note:** You also need to have Next.js and Coherent.js core installed:
16
+
17
+ ```bash
18
+ npm install next @coherent.js/core
19
+ # or
20
+ pnpm add next @coherent.js/core
21
+ ```
22
+
23
+ ## Overview
24
+
25
+ The `@coherent.js/nextjs` package provides seamless integration between Coherent.js and Next.js, allowing you to use Coherent.js components alongside React components in your Next.js applications. This integration enables:
26
+
27
+ - Server-side rendering with Coherent.js
28
+ - Client-side hydration
29
+ - Seamless integration with Next.js routing
30
+ - Performance benefits of both frameworks
31
+
32
+ ## Quick Start
33
+
34
+ ### 1. Create a Coherent.js Component
35
+
36
+ ```javascript
37
+ // components/HelloWorld.js
38
+ export function HelloWorld({ name = 'World' }) {
39
+ return {
40
+ div: {
41
+ className: 'hello-world',
42
+ children: [
43
+ { h1: { text: `Hello, ${name}!` } },
44
+ { p: { text: 'This component is rendered with Coherent.js' } }
45
+ ]
46
+ }
47
+ };
48
+ }
49
+ ```
50
+
51
+ ### 2. Use in a Next.js Page
52
+
53
+ ```javascript
54
+ // pages/index.js
55
+ import { renderCoherentComponent } from '@coherent.js/nextjs';
56
+ import { HelloWorld } from '../components/HelloWorld';
57
+
58
+ export default function Home() {
59
+ // Render Coherent.js component to React element
60
+ const coherentElement = renderCoherentComponent(HelloWorld, {
61
+ name: 'Next.js User'
62
+ });
63
+
64
+ return (
65
+ <div>
66
+ <h1>Welcome to Next.js</h1>
67
+ {coherentElement}
68
+ </div>
69
+ );
70
+ }
71
+ ```
72
+
73
+ ## Features
74
+
75
+ ### Server-Side Rendering
76
+
77
+ Automatic server-side rendering of Coherent.js components:
78
+
79
+ ```javascript
80
+ // pages/profile.js
81
+ import { renderCoherentComponent } from '@coherent.js/nextjs';
82
+ import { UserProfile } from '../components/UserProfile';
83
+
84
+ export async function getServerSideProps(context) {
85
+ // Fetch user data on server
86
+ const user = await fetchUser(context.params.id);
87
+
88
+ return {
89
+ props: {
90
+ user
91
+ }
92
+ };
93
+ }
94
+
95
+ export default function Profile({ user }) {
96
+ const userProfile = renderCoherentComponent(UserProfile, {
97
+ user,
98
+ editable: false // No editing on server-rendered page
99
+ });
100
+
101
+ return (
102
+ <div>
103
+ <h1>User Profile</h1>
104
+ {userProfile}
105
+ </div>
106
+ );
107
+ }
108
+ ```
109
+
110
+ ### Client-Side Hydration
111
+
112
+ Enable full interactivity with client-side hydration:
113
+
114
+ ```javascript
115
+ // components/Counter.js
116
+ import { withState } from '@coherent.js/core';
117
+
118
+ export const Counter = withState({ count: 0 })(({ state, setState }) => {
119
+ const increment = () => setState({ count: state.count + 1 });
120
+ const decrement = () => setState({ count: state.count - 1 });
121
+
122
+ return {
123
+ div: {
124
+ 'data-coherent-component': 'counter', // Required for hydration
125
+ children: [
126
+ { p: { text: `Count: ${state.count}` } },
127
+ { button: { text: '+', onclick: increment } },
128
+ { button: { text: '-', onclick: decrement } }
129
+ ]
130
+ }
131
+ };
132
+ });
133
+ ```
134
+
135
+ ```javascript
136
+ // pages/counter.js
137
+ import { renderCoherentComponent } from '@coherent.js/nextjs';
138
+ import { Counter } from '../components/Counter';
139
+
140
+ export default function CounterPage() {
141
+ const counter = renderCoherentComponent(Counter, {}, {
142
+ hydrate: true // Enable client-side hydration
143
+ });
144
+
145
+ return (
146
+ <div>
147
+ <h1>Interactive Counter</h1>
148
+ {counter}
149
+ </div>
150
+ );
151
+ }
152
+ ```
153
+
154
+ ### Integration with Next.js Data Fetching
155
+
156
+ Seamlessly integrate with Next.js data fetching methods:
157
+
158
+ ```javascript
159
+ // pages/products.js
160
+ import { renderCoherentComponent } from '@coherent.js/nextjs';
161
+ import { ProductList } from '../components/ProductList';
162
+
163
+ export async function getStaticProps() {
164
+ // Fetch products at build time
165
+ const products = await fetchProducts();
166
+
167
+ return {
168
+ props: {
169
+ products
170
+ },
171
+ revalidate: 60 // Revalidate every 60 seconds
172
+ };
173
+ }
174
+
175
+ export default function Products({ products }) {
176
+ const productList = renderCoherentComponent(ProductList, {
177
+ products
178
+ });
179
+
180
+ return (
181
+ <div>
182
+ <h1>Products</h1>
183
+ {productList}
184
+ </div>
185
+ );
186
+ }
187
+ ```
188
+
189
+ ## Configuration
190
+
191
+ ### Custom Hydration Setup
192
+
193
+ Configure hydration behavior:
194
+
195
+ ```javascript
196
+ // pages/_app.js
197
+ import { setupHydration } from '@coherent.js/nextjs';
198
+ import { Counter } from '../components/Counter';
199
+ import { UserProfile } from '../components/UserProfile';
200
+
201
+ // Register components for hydration
202
+ setupHydration({
203
+ counter: Counter,
204
+ userProfile: UserProfile
205
+ });
206
+
207
+ export default function MyApp({ Component, pageProps }) {
208
+ return <Component {...pageProps} />;
209
+ }
210
+ ```
211
+
212
+ ### CSS Integration
213
+
214
+ Handle CSS with Coherent.js components:
215
+
216
+ ```javascript
217
+ // components/StyledComponent.js
218
+ export function StyledComponent() {
219
+ return {
220
+ div: {
221
+ className: 'styled-component',
222
+ style: {
223
+ backgroundColor: '#f0f0f0',
224
+ padding: '20px',
225
+ borderRadius: '8px'
226
+ },
227
+ children: [
228
+ { h2: { text: 'Styled with Coherent.js' } },
229
+ { p: { text: 'This component has inline styles' } }
230
+ ]
231
+ }
232
+ };
233
+ }
234
+ ```
235
+
236
+ ### Template Customization
237
+
238
+ Customize the HTML template for Coherent.js components:
239
+
240
+ ```javascript
241
+ // pages/custom.js
242
+ import { renderCoherentComponent } from '@coherent.js/nextjs';
243
+ import { CustomComponent } from '../components/CustomComponent';
244
+
245
+ export default function CustomPage() {
246
+ const component = renderCoherentComponent(CustomComponent, {
247
+ data: 'example'
248
+ }, {
249
+ template: ({ html, head, body }) => `
250
+ <div class="custom-wrapper">
251
+ <header>Custom Header</header>
252
+ <main>${body}</main>
253
+ <footer>Custom Footer</footer>
254
+ </div>
255
+ `
256
+ });
257
+
258
+ return <div>{component}</div>;
259
+ }
260
+ ```
261
+
262
+ ## Performance Optimizations
263
+
264
+ ### Code Splitting
265
+
266
+ Leverage Next.js code splitting with Coherent.js:
267
+
268
+ ```javascript
269
+ // components/LazyComponent.js
270
+ export function LazyComponent({ message }) {
271
+ return {
272
+ div: {
273
+ className: 'lazy-component',
274
+ children: [
275
+ { h3: { text: 'Lazy Loaded Component' } },
276
+ { p: { text: message } }
277
+ ]
278
+ }
279
+ };
280
+ }
281
+ ```
282
+
283
+ ```javascript
284
+ // pages/lazy.js
285
+ import { renderCoherentComponent } from '@coherent.js/nextjs';
286
+ import dynamic from 'next/dynamic';
287
+
288
+ // Dynamically import Coherent.js component
289
+ const LazyComponent = dynamic(() =>
290
+ import('../components/LazyComponent').then(mod => mod.LazyComponent)
291
+ );
292
+
293
+ export default function LazyPage() {
294
+ return (
295
+ <div>
296
+ <h1>Lazy Loading Example</h1>
297
+ <LazyComponent message="This component was loaded dynamically" />
298
+ </div>
299
+ );
300
+ }
301
+ ```
302
+
303
+ ### Caching Strategies
304
+
305
+ Implement caching for better performance:
306
+
307
+ ```javascript
308
+ // pages/cached.js
309
+ import { renderCoherentComponent } from '@coherent.js/nextjs';
310
+
311
+ export async function getStaticProps() {
312
+ // Cache expensive operations
313
+ const data = await fetchWithCache('expensive-api-call');
314
+
315
+ return {
316
+ props: { data },
317
+ revalidate: 3600 // Cache for 1 hour
318
+ };
319
+ }
320
+
321
+ export default function CachedPage({ data }) {
322
+ const component = renderCoherentComponent(DataDisplay, { data });
323
+ return <div>{component}</div>;
324
+ }
325
+ ```
326
+
327
+ ## Advanced Usage
328
+
329
+ ### State Management Integration
330
+
331
+ Integrate with global state management:
332
+
333
+ ```javascript
334
+ // components/ShoppingCart.js
335
+ import { withState } from '@coherent.js/core';
336
+
337
+ export const ShoppingCart = withState({
338
+ items: [],
339
+ total: 0
340
+ })(({ state, setState }) => {
341
+ const addItem = (item) => {
342
+ const newItems = [...state.items, item];
343
+ const newTotal = newItems.reduce((sum, item) => sum + item.price, 0);
344
+ setState({ items: newItems, total: newTotal });
345
+ };
346
+
347
+ return {
348
+ div: {
349
+ 'data-coherent-component': 'shopping-cart',
350
+ children: [
351
+ { h3: { text: 'Shopping Cart' } },
352
+ { p: { text: `Items: ${state.items.length}` } },
353
+ { p: { text: `Total: $${state.total.toFixed(2)}` } },
354
+ { button: {
355
+ text: 'Add Item',
356
+ onclick: () => addItem({ id: Date.now(), name: 'Product', price: 29.99 })
357
+ }}
358
+ ]
359
+ }
360
+ };
361
+ });
362
+ ```
363
+
364
+ ### API Route Integration
365
+
366
+ Use Coherent.js with Next.js API routes:
367
+
368
+ ```javascript
369
+ // pages/api/render-component.js
370
+ import { renderToString } from '@coherent.js/core';
371
+
372
+ export default async function handler(req, res) {
373
+ const { componentName, props } = req.body;
374
+
375
+ // Dynamically render component
376
+ let html;
377
+ switch (componentName) {
378
+ case 'HelloWorld':
379
+ const { HelloWorld } = await import('../../components/HelloWorld');
380
+ html = await renderToString(HelloWorld, props);
381
+ break;
382
+ default:
383
+ return res.status(400).json({ error: 'Unknown component' });
384
+ }
385
+
386
+ res.status(200).json({ html });
387
+ }
388
+ ```
389
+
390
+ ## API Reference
391
+
392
+ ### renderCoherentComponent(component, props, options)
393
+
394
+ Render a Coherent.js component as a React element.
395
+
396
+ **Parameters:**
397
+ - `component` - Coherent.js component function
398
+ - `props` - Component props object
399
+ - `options.hydrate` - Enable client-side hydration (default: false)
400
+ - `options.template` - Custom HTML template function
401
+ - `options.context` - Additional context data
402
+
403
+ **Returns:** React element
404
+
405
+ ### setupHydration(componentMap)
406
+
407
+ Configure client-side hydration for components.
408
+
409
+ **Parameters:**
410
+ - `componentMap` - Object mapping component names to component functions
411
+
412
+ ### Options
413
+
414
+ - `hydrate` - Boolean to enable hydration
415
+ - `template` - Function to customize HTML template
416
+ - `context` - Additional context data for rendering
417
+
418
+ ## Examples
419
+
420
+ ### E-commerce Product Page
421
+
422
+ ```javascript
423
+ // components/ProductPage.js
424
+ import { withState } from '@coherent.js/core';
425
+
426
+ export const ProductPage = withState({
427
+ quantity: 1,
428
+ selectedVariant: null
429
+ })(({ state, setState, product }) => {
430
+ const updateQuantity = (qty) => setState({ quantity: Math.max(1, qty) });
431
+ const selectVariant = (variant) => setState({ selectedVariant: variant });
432
+
433
+ const selectedVariant = state.selectedVariant || product.variants[0];
434
+ const totalPrice = selectedVariant.price * state.quantity;
435
+
436
+ return {
437
+ div: {
438
+ 'data-coherent-component': 'product-page',
439
+ className: 'product-page',
440
+ children: [
441
+ { h1: { text: product.name } },
442
+ { img: { src: selectedVariant.image, alt: product.name } },
443
+ {
444
+ div: {
445
+ children: product.variants.map(variant => ({
446
+ button: {
447
+ text: variant.name,
448
+ className: variant.id === state.selectedVariant?.id ? 'selected' : '',
449
+ onclick: () => selectVariant(variant)
450
+ }
451
+ }))
452
+ }
453
+ },
454
+ {
455
+ div: {
456
+ children: [
457
+ { label: { text: 'Quantity:' } },
458
+ { input: {
459
+ type: 'number',
460
+ value: state.quantity,
461
+ oninput: (e) => updateQuantity(parseInt(e.target.value))
462
+ }},
463
+ { p: { text: `Total: $${totalPrice.toFixed(2)}` } },
464
+ { button: {
465
+ text: 'Add to Cart',
466
+ onclick: () => addToCart({
467
+ productId: product.id,
468
+ variantId: selectedVariant.id,
469
+ quantity: state.quantity
470
+ })
471
+ }}
472
+ ]
473
+ }
474
+ }
475
+ ]
476
+ }
477
+ };
478
+ });
479
+ ```
480
+
481
+ ```javascript
482
+ // pages/products/[id].js
483
+ import { renderCoherentComponent } from '@coherent.js/nextjs';
484
+ import { ProductPage } from '../../components/ProductPage';
485
+
486
+ export async function getServerSideProps({ params }) {
487
+ const product = await fetchProduct(params.id);
488
+
489
+ return {
490
+ props: {
491
+ product
492
+ }
493
+ };
494
+ }
495
+
496
+ export default function Product({ product }) {
497
+ const productPage = renderCoherentComponent(ProductPage, {
498
+ product
499
+ }, {
500
+ hydrate: true
501
+ });
502
+
503
+ return (
504
+ <div className="container">
505
+ {productPage}
506
+ </div>
507
+ );
508
+ }
509
+ ```
510
+
511
+ ## Related Packages
512
+
513
+ - [@coherent.js/core](../core/README.md) - Core framework
514
+ - [@coherent.js/client](../client/README.md) - Client-side utilities
515
+ - [@coherent.js/express](../express/README.md) - Express.js adapter
516
+ - [@coherent.js/fastify](../fastify/README.md) - Fastify adapter
517
+ - [@coherent.js/koa](../koa/README.md) - Koa adapter
518
+
519
+ ## License
520
+
521
+ MIT