@punkestu/react-html 0.0.0-stage โ 0.1.0
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/.react-html/cache/page-fefe905ae5d2.mjs +9 -0
- package/DOCS.md +317 -0
- package/README.md +215 -2
- package/bin/react-html.js +8 -0
- package/example/public/styles.css +184 -0
- package/example/src/components/Card.tsx +17 -0
- package/example/src/components/Counter.island.tsx +35 -0
- package/example/src/components/Footer.tsx +13 -0
- package/example/src/components/Header.tsx +20 -0
- package/example/src/components/Layout.tsx +39 -0
- package/example/src/components/Tabs.island.tsx +59 -0
- package/example/src/pages/index.tsx +49 -0
- package/example/src/pages/pure.tsx +56 -0
- package/package.json +44 -3
- package/src/cli/docs-content.ts +268 -0
- package/src/cli/index.ts +113 -0
- package/src/cli/tui-docs.ts +214 -0
- package/src/client/hydrate.ts +84 -0
- package/src/core/bundler.ts +122 -0
- package/src/core/formatter.ts +92 -0
- package/src/core/island-client.ts +23 -0
- package/src/core/island-registry.ts +35 -0
- package/src/core/island.tsx +110 -0
- package/src/core/loader.ts +89 -0
- package/src/core/renderer.ts +80 -0
- package/src/index.ts +138 -0
- package/src/server/dev-server.ts +125 -0
- package/src/server/engine.ts +270 -0
- package/src/server/index.ts +5 -0
- package/tests/engine.test.mjs +288 -0
- package/tsconfig.json +21 -0
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
// .server-test-fixture/src/pages/index.tsx
|
|
2
|
+
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
function Home() {
|
|
4
|
+
return /* @__PURE__ */ jsx("h1", { children: "UPDATED LIVE IN DEV" });
|
|
5
|
+
}
|
|
6
|
+
export {
|
|
7
|
+
Home as default
|
|
8
|
+
};
|
|
9
|
+
//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsiLi4vLi4vLnNlcnZlci10ZXN0LWZpeHR1cmUvc3JjL3BhZ2VzL2luZGV4LnRzeCJdLAogICJzb3VyY2VzQ29udGVudCI6IFsiXG4gICAgICAgIGltcG9ydCBSZWFjdCBmcm9tICdyZWFjdCc7XG4gICAgICAgIGV4cG9ydCBkZWZhdWx0IGZ1bmN0aW9uIEhvbWUoKSB7XG4gICAgICAgICAgcmV0dXJuIDxoMT5VUERBVEVEIExJVkUgSU4gREVWPC9oMT47XG4gICAgICAgIH1cbiAgICAgICAgIl0sCiAgIm1hcHBpbmdzIjogIjtBQUdpQjtBQURNLFNBQVIsT0FBd0I7QUFDN0IsU0FBTyxvQkFBQyxRQUFHLGlDQUFtQjtBQUNoQzsiLAogICJuYW1lcyI6IFtdCn0K
|
package/DOCS.md
ADDED
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
# ๐ react-html Complete Manual & Documentation
|
|
2
|
+
|
|
3
|
+
A high-performance static engine to compile React/JSX to **pure, semantic HTML** with selective reactive compilation for interactive components via an **Islands Architecture**.
|
|
4
|
+
|
|
5
|
+
> ๐ก **Terminal UI Access**: You can also browse this documentation interactively in your terminal by running:
|
|
6
|
+
> ```bash
|
|
7
|
+
> npx react-html docs
|
|
8
|
+
> # Or print a specific section:
|
|
9
|
+
> npx react-html docs islands
|
|
10
|
+
> ```
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## Table of Contents
|
|
15
|
+
|
|
16
|
+
1. [Overview & Philosophy](#1-overview--philosophy)
|
|
17
|
+
2. [Getting Started & Setup](#2-getting-started--setup)
|
|
18
|
+
3. [Modular HTML with React](#3-modular-html-with-react)
|
|
19
|
+
4. [Reactive Islands Architecture](#4-reactive-islands-architecture)
|
|
20
|
+
5. [Hydration Directives](#5-hydration-directives)
|
|
21
|
+
6. [CLI Commands & Options](#6-cli-commands--options)
|
|
22
|
+
7. [Programmatic API Reference](#7-programmatic-api-reference)
|
|
23
|
+
8. [Server App Integration (Express, Fastify, Node HTTP)](#8-server-app-integration-express-fastify-node-http)
|
|
24
|
+
9. [Best Practices & Troubleshooting](#9-best-practices--troubleshooting)
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## 1. Overview & Philosophy
|
|
29
|
+
|
|
30
|
+
Modern web development often forces a painful choice:
|
|
31
|
+
- **Write raw HTML/handlebars/nunjucks**: You lose type safety, component modularity, and rich JSX composition.
|
|
32
|
+
- **Use modern React frameworks (Next.js, CRA, Remix)**: You inadvertently ship massive megabytes of JavaScript, client routers, and heavy hydration runtimes even for mostly static pages.
|
|
33
|
+
|
|
34
|
+
**`react-html` gives you the best of both worlds**:
|
|
35
|
+
1. **Zero JavaScript by Default**:
|
|
36
|
+
Pages without interactive islands output 100% pure, clean HTML5. No client-side React runtime, no `data-reactroot` attributes, and 0 KB JavaScript sent to visitors.
|
|
37
|
+
2. **Modular & DRY Components**:
|
|
38
|
+
Compose your pages with `<Layout>`, `<Header>`, `<Card>`, `<Footer>`, loops, and conditionals using standard React and TypeScript.
|
|
39
|
+
3. **Selective Reactivity**:
|
|
40
|
+
Interactive components (counters, carousels, modals, filters) are marked as **Islands**. The engine pre-renders their initial markup for instant paint and bundles only that component into a standalone client script using `esbuild`.
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## 2. Getting Started & Setup
|
|
45
|
+
|
|
46
|
+
### Prerequisites
|
|
47
|
+
- Standard **Node.js v20.0.0 or higher** with `npm` (no Bun dependencies).
|
|
48
|
+
|
|
49
|
+
### Installation
|
|
50
|
+
```bash
|
|
51
|
+
npm install react-html
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Or clone and link locally:
|
|
55
|
+
```bash
|
|
56
|
+
git clone https://github.com/microapes/react-html.git
|
|
57
|
+
cd react-html
|
|
58
|
+
npm install
|
|
59
|
+
npm run build
|
|
60
|
+
npm link
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Recommended Directory Structure
|
|
64
|
+
|
|
65
|
+
```text
|
|
66
|
+
my-project/
|
|
67
|
+
โโโ package.json
|
|
68
|
+
โโโ public/ # Static files copied to dist/ verbatim
|
|
69
|
+
โ โโโ favicon.ico
|
|
70
|
+
โ โโโ styles.css
|
|
71
|
+
โโโ src/
|
|
72
|
+
โโโ components/ # Reusable modular UI components
|
|
73
|
+
โ โโโ Layout.tsx # Master HTML wrapper (<head>, <body>)
|
|
74
|
+
โ โโโ Header.tsx # Navigation header
|
|
75
|
+
โ โโโ Footer.tsx # Footer component
|
|
76
|
+
โ โโโ Counter.island.tsx # Interactive island component
|
|
77
|
+
โโโ pages/ # Page entrypoints
|
|
78
|
+
โโโ index.tsx # -> dist/index.html
|
|
79
|
+
โโโ about.tsx # -> dist/about.html
|
|
80
|
+
โโโ docs/
|
|
81
|
+
โโโ guide.tsx # -> dist/docs/guide.html
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
## 3. Modular HTML with React
|
|
87
|
+
|
|
88
|
+
Eliminate repetitive HTML across your project by creating reusable layout components:
|
|
89
|
+
|
|
90
|
+
```tsx
|
|
91
|
+
// src/components/Layout.tsx
|
|
92
|
+
import React, { ReactNode } from 'react';
|
|
93
|
+
import { Header } from './Header.js';
|
|
94
|
+
import { Footer } from './Footer.js';
|
|
95
|
+
|
|
96
|
+
export interface LayoutProps {
|
|
97
|
+
title?: string;
|
|
98
|
+
description?: string;
|
|
99
|
+
children: ReactNode;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export function Layout({
|
|
103
|
+
title = 'Default Title',
|
|
104
|
+
description = 'My static site',
|
|
105
|
+
children
|
|
106
|
+
}: LayoutProps) {
|
|
107
|
+
return (
|
|
108
|
+
<html lang="en">
|
|
109
|
+
<head>
|
|
110
|
+
<meta charSet="UTF-8" />
|
|
111
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
112
|
+
<title>{title}</title>
|
|
113
|
+
<meta name="description" content={description} />
|
|
114
|
+
<link rel="stylesheet" href="/styles.css" />
|
|
115
|
+
</head>
|
|
116
|
+
<body>
|
|
117
|
+
<Header />
|
|
118
|
+
<main className="container">{children}</main>
|
|
119
|
+
<Footer />
|
|
120
|
+
</body>
|
|
121
|
+
</html>
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Now, every page simply wraps its content without repeating `<head>`, stylesheets, or `<header>`/`<footer>`:
|
|
127
|
+
|
|
128
|
+
```tsx
|
|
129
|
+
// src/pages/about.tsx
|
|
130
|
+
import React from 'react';
|
|
131
|
+
import { Layout } from '../components/Layout.js';
|
|
132
|
+
|
|
133
|
+
export const title = 'About Us';
|
|
134
|
+
|
|
135
|
+
export default function AboutPage() {
|
|
136
|
+
return (
|
|
137
|
+
<Layout title={title}>
|
|
138
|
+
<h1>About Our Project</h1>
|
|
139
|
+
<p>This entire page compiles into pure static HTML with 0 KB JavaScript!</p>
|
|
140
|
+
</Layout>
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
---
|
|
146
|
+
|
|
147
|
+
## 4. Reactive Islands Architecture
|
|
148
|
+
|
|
149
|
+
When a component needs client-side state (`useState`, `useEffect`, event listeners):
|
|
150
|
+
|
|
151
|
+
### Step 1: Wrap Component with `island()`
|
|
152
|
+
|
|
153
|
+
```tsx
|
|
154
|
+
// src/components/Counter.island.tsx
|
|
155
|
+
import React, { useState } from 'react';
|
|
156
|
+
import { island } from 'react-html';
|
|
157
|
+
|
|
158
|
+
export interface CounterProps {
|
|
159
|
+
initialCount?: number;
|
|
160
|
+
step?: number;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function CounterComponent({ initialCount = 0, step = 1 }: CounterProps) {
|
|
164
|
+
const [count, setCount] = useState(initialCount);
|
|
165
|
+
|
|
166
|
+
return (
|
|
167
|
+
<div className="counter">
|
|
168
|
+
<p>Current: {count}</p>
|
|
169
|
+
<button onClick={() => setCount(c => c - step)}>- {step}</button>
|
|
170
|
+
<button onClick={() => setCount(c => c + step)}>+ {step}</button>
|
|
171
|
+
</div>
|
|
172
|
+
);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// Wrap with island helper providing Component, source path, and identifier
|
|
176
|
+
export const Counter = island(CounterComponent, import.meta.url, 'Counter');
|
|
177
|
+
export default Counter;
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
### Step 2: Use in Any Page
|
|
181
|
+
|
|
182
|
+
```tsx
|
|
183
|
+
// src/pages/index.tsx
|
|
184
|
+
import React from 'react';
|
|
185
|
+
import { Layout } from '../components/Layout.js';
|
|
186
|
+
import { Counter } from '../components/Counter.island.js';
|
|
187
|
+
|
|
188
|
+
export default function HomePage() {
|
|
189
|
+
return (
|
|
190
|
+
<Layout title="Home">
|
|
191
|
+
<h1>Interactive Page</h1>
|
|
192
|
+
{/* Pre-rendered to HTML for instant paint, then hydrated with tiny JS */}
|
|
193
|
+
<Counter initialCount={10} step={2} />
|
|
194
|
+
</Layout>
|
|
195
|
+
);
|
|
196
|
+
}
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
### What Happens Behind the Scenes:
|
|
200
|
+
1. **Server Phase**: The island is pre-rendered with `renderToString` and placed inside a container `<div data-island="Counter" data-props="...">`.
|
|
201
|
+
2. **Bundler Phase**: `esbuild` extracts and bundles only `CounterComponent` into `dist/assets/islands/Counter.js`.
|
|
202
|
+
3. **Injection Phase**: `<script type="module" src="/assets/islands/Counter.js"></script>` is injected right before `</body>`.
|
|
203
|
+
4. **Client Hydration**: The script queries `[data-island="Counter"]` and attaches listeners using React 19's `hydrateRoot`.
|
|
204
|
+
|
|
205
|
+
---
|
|
206
|
+
|
|
207
|
+
## 5. Hydration Directives
|
|
208
|
+
|
|
209
|
+
Fine-tune client performance using the `client` prop:
|
|
210
|
+
|
|
211
|
+
| Directive | Code Example | When It Hydrates |
|
|
212
|
+
| :--- | :--- | :--- |
|
|
213
|
+
| `load` *(default)* | `<Counter client="load" />` | Immediately upon `DOMContentLoaded`. Ideal for primary UI. |
|
|
214
|
+
| `idle` | `<Counter client="idle" />` | During browser idle time via `requestIdleCallback`. Ideal for background widgets. |
|
|
215
|
+
| `visible` | `<Counter client="visible" />` | When scrolled into view via `IntersectionObserver`. Ideal for below-the-fold content. |
|
|
216
|
+
|
|
217
|
+
---
|
|
218
|
+
|
|
219
|
+
## 6. CLI Commands & Options
|
|
220
|
+
|
|
221
|
+
```bash
|
|
222
|
+
# Build static site to dist/
|
|
223
|
+
react-html build
|
|
224
|
+
|
|
225
|
+
# Customize directories
|
|
226
|
+
react-html build --pages src/pages --out dist --root .
|
|
227
|
+
|
|
228
|
+
# Start local preview server with auto-rebuild on file changes
|
|
229
|
+
react-html dev --port 3000 --host localhost
|
|
230
|
+
|
|
231
|
+
# Terminal UI documentation viewer
|
|
232
|
+
react-html docs
|
|
233
|
+
react-html docs --all
|
|
234
|
+
react-html docs islands
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
---
|
|
238
|
+
|
|
239
|
+
## 7. Programmatic API Reference
|
|
240
|
+
|
|
241
|
+
You can import and trigger `react-html` within custom build scripts or Node.js services:
|
|
242
|
+
|
|
243
|
+
```ts
|
|
244
|
+
import { build, renderPageToHtml, createDevServer } from 'react-html';
|
|
245
|
+
|
|
246
|
+
// 1. Run static build
|
|
247
|
+
const result = await build({
|
|
248
|
+
root: process.cwd(),
|
|
249
|
+
pagesDir: 'src/pages',
|
|
250
|
+
outDir: 'dist',
|
|
251
|
+
minify: true
|
|
252
|
+
});
|
|
253
|
+
console.log(`Generated ${result.pages.length} pages.`);
|
|
254
|
+
|
|
255
|
+
// 2. Programmatic dev server
|
|
256
|
+
const server = await createDevServer({
|
|
257
|
+
port: 8080,
|
|
258
|
+
host: '127.0.0.1'
|
|
259
|
+
});
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
---
|
|
263
|
+
|
|
264
|
+
## 8. Server App Integration (Express, Fastify, Node HTTP)
|
|
265
|
+
|
|
266
|
+
You can import and use `react-html` directly inside your backend server application:
|
|
267
|
+
|
|
268
|
+
```ts
|
|
269
|
+
import express from 'express';
|
|
270
|
+
import { createEngine } from 'react-html';
|
|
271
|
+
// or: import { createEngine } from 'react-html/server';
|
|
272
|
+
|
|
273
|
+
const app = express();
|
|
274
|
+
|
|
275
|
+
const engine = createEngine({
|
|
276
|
+
pagesDir: 'src/pages',
|
|
277
|
+
distDir: 'dist',
|
|
278
|
+
// Automatically detects process.env.NODE_ENV === 'production'
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
// 1. Connect / Express Middleware
|
|
282
|
+
// In Production: finds and serves pre-built static HTML from dist/
|
|
283
|
+
// In Development: dynamically rebuilds TSX on the fly and returns the fresh HTML string!
|
|
284
|
+
app.use(engine.middleware);
|
|
285
|
+
|
|
286
|
+
// 2. Programmatic Route Rendering
|
|
287
|
+
app.get('/dashboard', async (req, res) => {
|
|
288
|
+
const html = await engine.render('dashboard', { user: req.user });
|
|
289
|
+
res.setHeader('Content-Type', 'text/html; charset=utf-8');
|
|
290
|
+
res.send(html);
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
// 3. Native Node HTTP Server
|
|
294
|
+
import http from 'node:http';
|
|
295
|
+
const server = http.createServer(engine.handle);
|
|
296
|
+
server.listen(3000);
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
### Environment Behavior:
|
|
300
|
+
- **`process.env.NODE_ENV === 'production'`**:
|
|
301
|
+
- Automatically searches for pre-built `.html` files in `distDir` (`dist/index.html`, `dist/about.html`).
|
|
302
|
+
- Streams them immediately to clients with 0 rebuild overhead.
|
|
303
|
+
- Serves static assets (`dist/assets/islands/*.js`, `dist/styles.css`).
|
|
304
|
+
- **`process.env.NODE_ENV !== 'production'` (Development)**:
|
|
305
|
+
- Dynamically rebuilds the corresponding `.tsx` page on each request.
|
|
306
|
+
- Bundles any reactive islands on-the-fly.
|
|
307
|
+
- Returns the fresh HTML string directly in the responseโcode edits in `src/pages` or `src/components` are reflected instantly on reload without server restarts!
|
|
308
|
+
|
|
309
|
+
---
|
|
310
|
+
|
|
311
|
+
## 9. Best Practices & Troubleshooting
|
|
312
|
+
|
|
313
|
+
### Preventing Hydration Mismatches (React Error #418)
|
|
314
|
+
- **Do not read browser globals during initial render**: Avoid `window.innerWidth` or `localStorage.getItem()` directly in the component body. Read them inside a `useEffect()`.
|
|
315
|
+
- **Props consistency**: Ensure initial props passed from the page match what the component expects.
|
|
316
|
+
- **Protected formatting**: `react-html` automatically preserves the exact inner markup of islands so whitespace insertion does not invalidate React's DOM reconciliation.
|
|
317
|
+
|
package/README.md
CHANGED
|
@@ -1,3 +1,216 @@
|
|
|
1
|
-
#
|
|
1
|
+
# react-html โ๏ธ โก๏ธ ๐
|
|
2
|
+
|
|
3
|
+
A blazing-fast engine to convert React/JSX components into **pure, semantic HTML** with selective compilation of interactive components into **micro JavaScript islands**.
|
|
4
|
+
|
|
5
|
+
Write DRY, modular HTML using React components without shipping any React runtime or JavaScript to the client by default.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## ๐ Why react-html?
|
|
10
|
+
|
|
11
|
+
- **Zero-JS by Default**: Static pages are rendered to 100% pure HTML. No client-side React runtime, no `<script>` tags, and 0 KB JavaScript overhead.
|
|
12
|
+
- **Modular & DRY**: Stop duplicating headers, footers, meta tags, and layouts. Build reusable components (`<Layout>`, `<Header>`, `<Card>`, `<Footer>`) using familiar React JSX syntax.
|
|
13
|
+
- **Selective Reactivity (Islands Architecture)**: When a component needs client-side interactivity (counters, modals, tabs, forms), the engine bundles only that component into a standalone, micro ESM script using `esbuild`.
|
|
14
|
+
- **Fast Build Pipeline**: Powered by `esbuild` for instantaneous static site generation and sub-100ms builds.
|
|
15
|
+
- **Node.js Native**: Runs strictly on standard Node.js (v20+) with npm.
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## ๐ Quick Start
|
|
20
|
+
|
|
21
|
+
### Installation
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
npm install react-html
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Or install globally / link locally:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
npm link
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### CLI Usage
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
# Build static site to dist/
|
|
37
|
+
react-html build
|
|
38
|
+
|
|
39
|
+
# Build with custom pages or output directory
|
|
40
|
+
react-html build --pages src/pages --out dist
|
|
41
|
+
|
|
42
|
+
# Start dev server with watch & live recompile
|
|
43
|
+
react-html dev --port 3000
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## ๐ How It Works
|
|
49
|
+
|
|
50
|
+
### 1. Pure Modular HTML (Zero JavaScript)
|
|
51
|
+
|
|
52
|
+
Create your reusable layout and page components using standard JSX:
|
|
53
|
+
|
|
54
|
+
```tsx
|
|
55
|
+
// src/components/Layout.tsx
|
|
56
|
+
import React, { ReactNode } from 'react';
|
|
57
|
+
|
|
58
|
+
export function Layout({ title, children }: { title: string; children: ReactNode }) {
|
|
59
|
+
return (
|
|
60
|
+
<html lang="en">
|
|
61
|
+
<head>
|
|
62
|
+
<meta charSet="UTF-8" />
|
|
63
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
64
|
+
<title>{title}</title>
|
|
65
|
+
<link rel="stylesheet" href="/styles.css" />
|
|
66
|
+
</head>
|
|
67
|
+
<body>
|
|
68
|
+
<header>
|
|
69
|
+
<h1>My Modular Site</h1>
|
|
70
|
+
</header>
|
|
71
|
+
<main>{children}</main>
|
|
72
|
+
</body>
|
|
73
|
+
</html>
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
```tsx
|
|
79
|
+
// src/pages/about.tsx
|
|
80
|
+
import React from 'react';
|
|
81
|
+
import { Layout } from '../components/Layout.js';
|
|
82
|
+
|
|
83
|
+
export default function AboutPage() {
|
|
84
|
+
return (
|
|
85
|
+
<Layout title="About Us">
|
|
86
|
+
<h2>About Our Project</h2>
|
|
87
|
+
<p>This entire page compiles into pure static HTML with 0 KB JavaScript!</p>
|
|
88
|
+
</Layout>
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
**Output in `dist/about.html`:**
|
|
94
|
+
```html
|
|
95
|
+
<!DOCTYPE html>
|
|
96
|
+
<html lang="en">
|
|
97
|
+
<head>
|
|
98
|
+
<meta charSet="UTF-8"/>
|
|
99
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
|
100
|
+
<title>About Us</title>
|
|
101
|
+
<link rel="stylesheet" href="/styles.css"/>
|
|
102
|
+
</head>
|
|
103
|
+
<body>
|
|
104
|
+
<header>
|
|
105
|
+
<h1>My Modular Site</h1>
|
|
106
|
+
</header>
|
|
107
|
+
<main>
|
|
108
|
+
<h2>About Our Project</h2>
|
|
109
|
+
<p>This entire page compiles into pure static HTML with 0 KB JavaScript!</p>
|
|
110
|
+
</main>
|
|
111
|
+
</body>
|
|
112
|
+
</html>
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
---
|
|
116
|
+
|
|
117
|
+
### 2. Reactive Components (Islands Architecture)
|
|
118
|
+
|
|
119
|
+
When a component needs client-side state and interactivity, wrap it with `island()`:
|
|
120
|
+
|
|
121
|
+
```tsx
|
|
122
|
+
// src/components/Counter.island.tsx
|
|
123
|
+
import React, { useState } from 'react';
|
|
124
|
+
import { island } from 'react-html';
|
|
125
|
+
|
|
126
|
+
export interface CounterProps {
|
|
127
|
+
initial?: number;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function CounterComponent({ initial = 0 }: CounterProps) {
|
|
131
|
+
const [count, setCount] = useState(initial);
|
|
132
|
+
|
|
133
|
+
return (
|
|
134
|
+
<div className="counter">
|
|
135
|
+
<button onClick={() => setCount(c => c - 1)}>-</button>
|
|
136
|
+
<span>Count: {count}</span>
|
|
137
|
+
<button onClick={() => setCount(c => c + 1)}>+</button>
|
|
138
|
+
</div>
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Export the island with its unique name and source path
|
|
143
|
+
export const Counter = island(CounterComponent, import.meta.url, 'Counter');
|
|
144
|
+
export default Counter;
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
Now import and use it in any page just like a normal React component:
|
|
148
|
+
|
|
149
|
+
```tsx
|
|
150
|
+
// src/pages/index.tsx
|
|
151
|
+
import React from 'react';
|
|
152
|
+
import { Layout } from '../components/Layout.js';
|
|
153
|
+
import { Counter } from '../components/Counter.island.js';
|
|
154
|
+
|
|
155
|
+
export default function HomePage() {
|
|
156
|
+
return (
|
|
157
|
+
<Layout title="Home">
|
|
158
|
+
<h2>Welcome!</h2>
|
|
159
|
+
{/* Pre-rendered to HTML for instant paint, then hydrated with tiny JS */}
|
|
160
|
+
<Counter initial={10} />
|
|
161
|
+
</Layout>
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
**What the engine does:**
|
|
167
|
+
1. Renders the initial static markup during SSR for instant paint.
|
|
168
|
+
2. Compiles a dedicated micro-bundle `dist/assets/islands/Counter.js`.
|
|
169
|
+
3. Injects `<script type="module" src="/assets/islands/Counter.js"></script>` into `index.html`.
|
|
170
|
+
4. Hydrates only the island element in-place using React 19's `hydrateRoot`.
|
|
171
|
+
|
|
172
|
+
---
|
|
173
|
+
|
|
174
|
+
## ๐ ๏ธ Hydration Directives
|
|
175
|
+
|
|
176
|
+
`react-html` supports client loading directives to maximize performance:
|
|
177
|
+
|
|
178
|
+
| Directive | Syntax | Behavior |
|
|
179
|
+
| :--- | :--- | :--- |
|
|
180
|
+
| `load` (default) | `<Counter client="load" />` | Hydrates immediately on DOM ready |
|
|
181
|
+
| `idle` | `<Counter client="idle" />` | Hydrates during browser idle time (`requestIdleCallback`) |
|
|
182
|
+
| `visible` | `<Counter client="visible" />` | Hydrates when scrolled into view (`IntersectionObserver`) |
|
|
183
|
+
|
|
184
|
+
---
|
|
185
|
+
|
|
186
|
+
## ๐ป Programmatic API
|
|
187
|
+
|
|
188
|
+
You can also run `react-html` programmatically from your Node.js scripts:
|
|
189
|
+
|
|
190
|
+
```ts
|
|
191
|
+
import { build } from 'react-html';
|
|
192
|
+
|
|
193
|
+
const result = await build({
|
|
194
|
+
root: process.cwd(),
|
|
195
|
+
pagesDir: 'src/pages',
|
|
196
|
+
outDir: 'dist',
|
|
197
|
+
minify: true
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
console.log(`Compiled ${result.pages.length} pages and ${result.islands.length} reactive islands.`);
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
---
|
|
204
|
+
|
|
205
|
+
## ๐งช Testing
|
|
206
|
+
|
|
207
|
+
Run the automated test suite powered by Node.js test runner:
|
|
208
|
+
|
|
209
|
+
```bash
|
|
210
|
+
npm test
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
## ๐ License
|
|
214
|
+
|
|
215
|
+
MIT
|
|
2
216
|
|
|
3
|
-
This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
|