rehype plugin to transform HTML into JSX elements for React, Preact, Solid, Svelte, Vue and other frameworks
npx @tessl/cli install tessl/npm-rehype-react@8.0.00
# rehype-react
1
2
rehype-react is a unified/rehype plugin that transforms HTML (hast syntax trees) into JSX elements compatible with multiple JavaScript frameworks including React, Preact, Solid, Svelte, and Vue. It serves as a bridge between HTML content and component-based applications, enabling developers to render HTML structures as JSX components with framework-specific optimizations.
3
4
## Package Information
5
6
- **Package Name**: rehype-react
7
- **Package Type**: npm
8
- **Language**: JavaScript/TypeScript (ESM only)
9
- **Installation**: `npm install rehype-react`
10
11
## Core Imports
12
13
```javascript
14
import rehypeReact from "rehype-react";
15
```
16
17
For re-exported types:
18
19
```typescript
20
import rehypeReact, { Components, Options } from "rehype-react";
21
```
22
23
## Basic Usage
24
25
```javascript
26
import { Fragment, createElement } from "react";
27
import * as prod from "react/jsx-runtime";
28
import rehypeParse from "rehype-parse";
29
import rehypeReact from "rehype-react";
30
import { unified } from "unified";
31
32
// Production JSX runtime configuration
33
const production = {
34
Fragment: prod.Fragment,
35
jsx: prod.jsx,
36
jsxs: prod.jsxs
37
};
38
39
// Transform HTML to JSX
40
const processor = unified()
41
.use(rehypeParse, { fragment: true })
42
.use(rehypeReact, production);
43
44
const html = '<h2>Hello, world!</h2><p>Welcome to my page 👀</p>';
45
const file = await processor.process(html);
46
const jsxElement = file.result; // JSX.Element ready to render
47
```
48
49
## Architecture
50
51
rehype-react operates within the unified ecosystem as a compiler plugin:
52
53
- **Unified Integration**: Registers as a compiler that transforms hast (HTML AST) to JSX.Element
54
- **JSX Runtime Abstraction**: Works with any JSX runtime (React, Preact, Solid, etc.) through configurable runtime functions
55
- **Framework Compatibility**: Handles framework-specific differences through configuration options
56
- **Type Safety**: Full TypeScript support with proper type inference and module augmentation
57
58
## Capabilities
59
60
### Main Plugin Function
61
62
Main plugin function that transforms HTML into JSX for various frameworks.
63
64
```typescript { .api }
65
/**
66
* Turn HTML into preact, react, solid, svelte, vue, etc.
67
* Registers a compiler that returns a JSX.Element where compilers typically return string.
68
* When using .stringify on unified, the result is such a JSX.Element.
69
* When using .process (or .processSync), the result is available at file.result.
70
*
71
* @param options - Configuration (required)
72
* @returns Nothing (undefined) - Plugin registers itself on the processor
73
*/
74
declare function rehypeReact(options: Options): undefined;
75
export default rehypeReact;
76
```
77
78
### Configuration Options
79
80
Complete configuration interface for the plugin.
81
82
```typescript { .api }
83
interface Options {
84
/** Fragment component from JSX runtime (required) */
85
Fragment: Fragment;
86
87
/** Dynamic JSX function (required in production mode) */
88
jsx?: Jsx;
89
90
/** Static JSX function (required in production mode) */
91
jsxs?: Jsx;
92
93
/** Development JSX function (required in development mode) */
94
jsxDEV?: JsxDev;
95
96
/** Custom component mapping (optional) */
97
components?: Partial<Components>;
98
99
/** Whether to use jsxDEV (development) or jsx/jsxs (production) */
100
development?: boolean;
101
102
/** Specify casing for attribute names */
103
elementAttributeNameCase?: 'html' | 'react';
104
105
/** Pass the hast element node to components */
106
passNode?: boolean;
107
108
/** HTML or SVG namespace context */
109
space?: 'html' | 'svg';
110
111
/** Specify casing for CSS property names in style objects */
112
stylePropertyNameCase?: 'css' | 'dom';
113
114
/** Convert obsolete align props to CSS style props */
115
tableCellAlignToStyle?: boolean;
116
}
117
```
118
119
### Component Mapping
120
121
Type definition for custom component mapping.
122
123
```typescript { .api }
124
/**
125
* Possible components to use for custom element mapping.
126
* Maps HTML element names to custom React/JSX components.
127
*/
128
type Components = {
129
[TagName in keyof JSX.IntrinsicElements]?: ComponentType<any>;
130
} & {
131
[key: string]: ComponentType<any>;
132
};
133
```
134
135
### JSX Runtime Types
136
137
Core JSX runtime function signatures and component types compatible with multiple frameworks.
138
139
```typescript { .api }
140
/** Global JSX namespace for element types - Framework agnostic */
141
declare global {
142
namespace JSX {
143
interface Element {}
144
interface IntrinsicElements {
145
[elemName: string]: any;
146
}
147
}
148
}
149
150
/**
151
* Component type definition - compatible with React, Preact, Solid, Vue
152
* @template P - Props type for the component
153
*/
154
type ComponentType<P = {}> = (props: P) => JSX.Element | null;
155
156
/**
157
* Node type for children and fragment content
158
* Used by React, Preact, and other JSX frameworks
159
*/
160
type ReactNode = JSX.Element | string | number | boolean | null | undefined | ReactNode[];
161
162
/**
163
* JSX runtime fragment component
164
* Maps to: React.Fragment, preact.Fragment, solid-js.Fragment, vue.Fragment
165
*/
166
type Fragment = ComponentType<{ children?: ReactNode }>;
167
168
/**
169
* Production JSX runtime function for dynamic content
170
* Maps to: react/jsx-runtime.jsx, preact.createElement, solid-js/jsx-runtime.jsx
171
* @param type - Element type (string for HTML elements, function for components)
172
* @param props - Element props/attributes
173
* @param key - Optional React key for list items
174
*/
175
type Jsx = (
176
type: any,
177
props: Record<string, any>,
178
key?: string | number
179
) => JSX.Element;
180
181
/**
182
* Development JSX runtime function with additional debugging info
183
* Maps to: react/jsx-dev-runtime.jsxDEV
184
* @param type - Element type (string for HTML elements, function for components)
185
* @param props - Element props/attributes
186
* @param key - Optional React key for list items
187
* @param isStaticChildren - Whether children are static (React optimization)
188
* @param source - Source location for debugging (React DevTools)
189
* @param self - Component instance (React DevTools)
190
*/
191
type JsxDev = (
192
type: any,
193
props: Record<string, any>,
194
key?: string | number,
195
isStaticChildren?: boolean,
196
source?: { fileName: string; lineNumber: number; columnNumber: number },
197
self?: any
198
) => JSX.Element;
199
```
200
201
## Framework Configuration
202
203
Different JSX frameworks require specific configuration options:
204
205
### React Configuration
206
207
```javascript
208
import { unified } from "unified";
209
import rehypeParse from "rehype-parse";
210
import rehypeReact from "rehype-react";
211
import * as prod from "react/jsx-runtime";
212
213
const reactOptions = {
214
Fragment: prod.Fragment,
215
jsx: prod.jsx,
216
jsxs: prod.jsxs,
217
elementAttributeNameCase: 'react', // Uses className instead of class
218
stylePropertyNameCase: 'dom' // Uses backgroundColor instead of background-color
219
};
220
221
// Usage with React
222
const processor = unified()
223
.use(rehypeParse, { fragment: true })
224
.use(rehypeReact, reactOptions);
225
```
226
227
### Preact Configuration
228
229
```javascript
230
import { unified } from "unified";
231
import rehypeParse from "rehype-parse";
232
import rehypeReact from "rehype-react";
233
import { Fragment, createElement } from "preact";
234
235
const preactOptions = {
236
Fragment: Fragment,
237
jsx: createElement,
238
jsxs: createElement,
239
elementAttributeNameCase: 'html', // Uses class instead of className
240
stylePropertyNameCase: 'dom'
241
};
242
243
// Usage with Preact
244
const processor = unified()
245
.use(rehypeParse, { fragment: true })
246
.use(rehypeReact, preactOptions);
247
```
248
249
### Solid Configuration
250
251
```javascript
252
import { unified } from "unified";
253
import rehypeParse from "rehype-parse";
254
import rehypeReact from "rehype-react";
255
import { Fragment } from "solid-js";
256
import { jsx } from "solid-js/jsx-runtime";
257
258
const solidOptions = {
259
Fragment: Fragment,
260
jsx: jsx,
261
jsxs: jsx,
262
elementAttributeNameCase: 'html', // Uses class
263
stylePropertyNameCase: 'css' // Uses kebab-case properties
264
};
265
266
// Usage with Solid
267
const processor = unified()
268
.use(rehypeParse, { fragment: true })
269
.use(rehypeReact, solidOptions);
270
```
271
272
### Vue Configuration
273
274
```javascript
275
import { unified } from "unified";
276
import rehypeParse from "rehype-parse";
277
import rehypeReact from "rehype-react";
278
import { Fragment } from "vue";
279
import { jsx, jsxs } from "vue/jsx-runtime";
280
281
const vueOptions = {
282
Fragment: Fragment,
283
jsx: jsx,
284
jsxs: jsxs,
285
elementAttributeNameCase: 'html',
286
stylePropertyNameCase: 'dom'
287
};
288
289
// Usage with Vue
290
const processor = unified()
291
.use(rehypeParse, { fragment: true })
292
.use(rehypeReact, vueOptions);
293
```
294
295
## Development vs Production
296
297
The plugin supports both development and production JSX transforms:
298
299
### Development Mode
300
301
```javascript
302
import * as dev from "react/jsx-dev-runtime";
303
304
const developmentOptions = {
305
Fragment: dev.Fragment,
306
jsxDEV: dev.jsxDEV,
307
development: true // Enables development mode
308
};
309
```
310
311
### Production Mode
312
313
```javascript
314
import * as prod from "react/jsx-runtime";
315
316
const productionOptions = {
317
Fragment: prod.Fragment,
318
jsx: prod.jsx,
319
jsxs: prod.jsxs,
320
development: false // Default: production mode
321
};
322
```
323
324
## Custom Component Mapping
325
326
Replace HTML elements with custom components:
327
328
```javascript
329
import { MyButton, MyLink } from "./components";
330
331
const customOptions = {
332
Fragment: prod.Fragment,
333
jsx: prod.jsx,
334
jsxs: prod.jsxs,
335
components: {
336
button: MyButton, // All <button> elements become <MyButton>
337
a: MyLink, // All <a> elements become <MyLink>
338
h1: 'h2' // All <h1> elements become <h2>
339
}
340
};
341
```
342
343
### Passing Node Information to Components
344
345
When using custom components, you can access the original hast node:
346
347
```javascript
348
const MyHeading = ({ children, node, ...props }) => {
349
// node contains the original hast element
350
console.log('Original element:', node.tagName);
351
return <h2 {...props}>{children}</h2>;
352
};
353
354
const nodeOptions = {
355
Fragment: prod.Fragment,
356
jsx: prod.jsx,
357
jsxs: prod.jsxs,
358
components: {
359
h1: MyHeading
360
},
361
passNode: true // Enables node prop
362
};
363
```
364
365
## Edge Cases and Special Handling
366
367
### Whitespace Handling
368
369
The plugin preserves meaningful whitespace while processing HTML:
370
371
```javascript
372
// HTML with whitespace
373
const htmlWithWhitespace = `
374
<table>
375
<tbody>
376
<tr>
377
<th>
378
Header
379
</th>
380
<td>
381
Data
382
</td>
383
</tr>
384
</tbody>
385
</table>
386
`;
387
388
// Whitespace is preserved in the JSX output
389
const result = await processor.process(htmlWithWhitespace);
390
```
391
392
### Table Cell Alignment
393
394
By default, obsolete `align` attributes on table cells are converted to CSS styles:
395
396
```javascript
397
// HTML: <th align="right">Header</th>
398
// Becomes: <th style={{textAlign: 'right'}}>Header</th>
399
400
// To disable this behavior:
401
const options = {
402
Fragment: prod.Fragment,
403
jsx: prod.jsx,
404
jsxs: prod.jsxs,
405
tableCellAlignToStyle: false // Keep align attribute as-is
406
};
407
```
408
409
### Doctype Handling
410
411
DOCTYPE declarations are automatically filtered out during transformation as they are not valid JSX:
412
413
```javascript
414
// HTML with doctype
415
const htmlWithDoctype = '<!DOCTYPE html><h1>Hello</h1>';
416
417
// Only the h1 element is transformed; doctype is ignored
418
const result = await processor.process(htmlWithDoctype);
419
// Result: <h1>Hello</h1>
420
```
421
422
## Error Handling
423
424
The plugin may throw errors in these cases:
425
426
- **Missing Runtime**: If required JSX runtime functions (`Fragment`, `jsx`, `jsxs`, or `jsxDEV`) are not provided
427
- **Invalid Configuration**: If `development: true` but `jsxDEV` is not provided, or vice versa
428
- **Transformation Errors**: If the underlying `hast-util-to-jsx-runtime` encounters invalid HTML structures
429
430
## Security Considerations
431
432
⚠️ **XSS Warning**: Using rehype-react with untrusted HTML can expose applications to cross-site scripting attacks. Always sanitize HTML before transformation:
433
434
```javascript
435
import rehypeSanitize from "rehype-sanitize";
436
437
const safeProcessor = unified()
438
.use(rehypeParse, { fragment: true })
439
.use(rehypeSanitize) // Sanitize before transformation
440
.use(rehypeReact, options);
441
```
442
443
## Module Declaration Extensions
444
445
The plugin extends unified's TypeScript definitions:
446
447
```typescript { .api }
448
declare module 'unified' {
449
interface CompileResultMap {
450
/** JSX Element result type for rehype-react */
451
JsxElement: JSX.Element;
452
}
453
}
454
```
455
456
This enables proper TypeScript inference when using the plugin with unified processors.