or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

index.md
tile.json

tessl/npm-rehype-react

rehype plugin to transform HTML into JSX elements for React, Preact, Solid, Svelte, Vue and other frameworks

Workspace
tessl
Visibility
Public
Created
Last updated
Describes
npmpkg:npm/rehype-react@8.0.x

To install, run

npx @tessl/cli install tessl/npm-rehype-react@8.0.0

index.mddocs/

rehype-react

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.

Package Information

  • Package Name: rehype-react
  • Package Type: npm
  • Language: JavaScript/TypeScript (ESM only)
  • Installation: npm install rehype-react

Core Imports

import rehypeReact from "rehype-react";

For re-exported types:

import rehypeReact, { Components, Options } from "rehype-react";

Basic Usage

import { Fragment, createElement } from "react";
import * as prod from "react/jsx-runtime";
import rehypeParse from "rehype-parse";
import rehypeReact from "rehype-react";
import { unified } from "unified";

// Production JSX runtime configuration
const production = {
  Fragment: prod.Fragment,
  jsx: prod.jsx,
  jsxs: prod.jsxs
};

// Transform HTML to JSX
const processor = unified()
  .use(rehypeParse, { fragment: true })
  .use(rehypeReact, production);

const html = '<h2>Hello, world!</h2><p>Welcome to my page 👀</p>';
const file = await processor.process(html);
const jsxElement = file.result; // JSX.Element ready to render

Architecture

rehype-react operates within the unified ecosystem as a compiler plugin:

  • Unified Integration: Registers as a compiler that transforms hast (HTML AST) to JSX.Element
  • JSX Runtime Abstraction: Works with any JSX runtime (React, Preact, Solid, etc.) through configurable runtime functions
  • Framework Compatibility: Handles framework-specific differences through configuration options
  • Type Safety: Full TypeScript support with proper type inference and module augmentation

Capabilities

Main Plugin Function

Main plugin function that transforms HTML into JSX for various frameworks.

/**
 * Turn HTML into preact, react, solid, svelte, vue, etc.
 * Registers a compiler that returns a JSX.Element where compilers typically return string.
 * When using .stringify on unified, the result is such a JSX.Element.
 * When using .process (or .processSync), the result is available at file.result.
 * 
 * @param options - Configuration (required)
 * @returns Nothing (undefined) - Plugin registers itself on the processor
 */
declare function rehypeReact(options: Options): undefined;
export default rehypeReact;

Configuration Options

Complete configuration interface for the plugin.

interface Options {
  /** Fragment component from JSX runtime (required) */
  Fragment: Fragment;
  
  /** Dynamic JSX function (required in production mode) */
  jsx?: Jsx;
  
  /** Static JSX function (required in production mode) */
  jsxs?: Jsx;
  
  /** Development JSX function (required in development mode) */
  jsxDEV?: JsxDev;
  
  /** Custom component mapping (optional) */
  components?: Partial<Components>;
  
  /** Whether to use jsxDEV (development) or jsx/jsxs (production) */
  development?: boolean;
  
  /** Specify casing for attribute names */
  elementAttributeNameCase?: 'html' | 'react';
  
  /** Pass the hast element node to components */
  passNode?: boolean;
  
  /** HTML or SVG namespace context */
  space?: 'html' | 'svg';
  
  /** Specify casing for CSS property names in style objects */
  stylePropertyNameCase?: 'css' | 'dom';
  
  /** Convert obsolete align props to CSS style props */
  tableCellAlignToStyle?: boolean;
}

Component Mapping

Type definition for custom component mapping.

/**
 * Possible components to use for custom element mapping.
 * Maps HTML element names to custom React/JSX components.
 */
type Components = {
  [TagName in keyof JSX.IntrinsicElements]?: ComponentType<any>;
} & {
  [key: string]: ComponentType<any>;
};

JSX Runtime Types

Core JSX runtime function signatures and component types compatible with multiple frameworks.

/** Global JSX namespace for element types - Framework agnostic */
declare global {
  namespace JSX {
    interface Element {}
    interface IntrinsicElements {
      [elemName: string]: any;
    }
  }
}

/**
 * Component type definition - compatible with React, Preact, Solid, Vue
 * @template P - Props type for the component
 */
type ComponentType<P = {}> = (props: P) => JSX.Element | null;

/**
 * Node type for children and fragment content
 * Used by React, Preact, and other JSX frameworks
 */
type ReactNode = JSX.Element | string | number | boolean | null | undefined | ReactNode[];

/**
 * JSX runtime fragment component
 * Maps to: React.Fragment, preact.Fragment, solid-js.Fragment, vue.Fragment
 */
type Fragment = ComponentType<{ children?: ReactNode }>;

/**
 * Production JSX runtime function for dynamic content
 * Maps to: react/jsx-runtime.jsx, preact.createElement, solid-js/jsx-runtime.jsx
 * @param type - Element type (string for HTML elements, function for components)
 * @param props - Element props/attributes
 * @param key - Optional React key for list items
 */
type Jsx = (
  type: any,
  props: Record<string, any>,
  key?: string | number
) => JSX.Element;

/**
 * Development JSX runtime function with additional debugging info
 * Maps to: react/jsx-dev-runtime.jsxDEV
 * @param type - Element type (string for HTML elements, function for components)
 * @param props - Element props/attributes
 * @param key - Optional React key for list items
 * @param isStaticChildren - Whether children are static (React optimization)
 * @param source - Source location for debugging (React DevTools)
 * @param self - Component instance (React DevTools)
 */
type JsxDev = (
  type: any,
  props: Record<string, any>,
  key?: string | number,
  isStaticChildren?: boolean,
  source?: { fileName: string; lineNumber: number; columnNumber: number },
  self?: any
) => JSX.Element;

Framework Configuration

Different JSX frameworks require specific configuration options:

React Configuration

import { unified } from "unified";
import rehypeParse from "rehype-parse";
import rehypeReact from "rehype-react";
import * as prod from "react/jsx-runtime";

const reactOptions = {
  Fragment: prod.Fragment,
  jsx: prod.jsx,
  jsxs: prod.jsxs,
  elementAttributeNameCase: 'react', // Uses className instead of class
  stylePropertyNameCase: 'dom'       // Uses backgroundColor instead of background-color
};

// Usage with React
const processor = unified()
  .use(rehypeParse, { fragment: true })
  .use(rehypeReact, reactOptions);

Preact Configuration

import { unified } from "unified";
import rehypeParse from "rehype-parse";
import rehypeReact from "rehype-react";
import { Fragment, createElement } from "preact";

const preactOptions = {
  Fragment: Fragment,
  jsx: createElement,
  jsxs: createElement,
  elementAttributeNameCase: 'html',  // Uses class instead of className
  stylePropertyNameCase: 'dom'
};

// Usage with Preact
const processor = unified()
  .use(rehypeParse, { fragment: true })
  .use(rehypeReact, preactOptions);

Solid Configuration

import { unified } from "unified";
import rehypeParse from "rehype-parse";
import rehypeReact from "rehype-react";
import { Fragment } from "solid-js";
import { jsx } from "solid-js/jsx-runtime";

const solidOptions = {
  Fragment: Fragment,
  jsx: jsx,
  jsxs: jsx,
  elementAttributeNameCase: 'html',  // Uses class
  stylePropertyNameCase: 'css'       // Uses kebab-case properties
};

// Usage with Solid
const processor = unified()
  .use(rehypeParse, { fragment: true })
  .use(rehypeReact, solidOptions);

Vue Configuration

import { unified } from "unified";
import rehypeParse from "rehype-parse";
import rehypeReact from "rehype-react";
import { Fragment } from "vue";
import { jsx, jsxs } from "vue/jsx-runtime";

const vueOptions = {
  Fragment: Fragment,
  jsx: jsx,
  jsxs: jsxs,
  elementAttributeNameCase: 'html',
  stylePropertyNameCase: 'dom'
};

// Usage with Vue
const processor = unified()
  .use(rehypeParse, { fragment: true })
  .use(rehypeReact, vueOptions);

Development vs Production

The plugin supports both development and production JSX transforms:

Development Mode

import * as dev from "react/jsx-dev-runtime";

const developmentOptions = {
  Fragment: dev.Fragment,
  jsxDEV: dev.jsxDEV,
  development: true  // Enables development mode
};

Production Mode

import * as prod from "react/jsx-runtime";

const productionOptions = {
  Fragment: prod.Fragment,
  jsx: prod.jsx,
  jsxs: prod.jsxs,
  development: false  // Default: production mode
};

Custom Component Mapping

Replace HTML elements with custom components:

import { MyButton, MyLink } from "./components";

const customOptions = {
  Fragment: prod.Fragment,
  jsx: prod.jsx,
  jsxs: prod.jsxs,
  components: {
    button: MyButton,    // All <button> elements become <MyButton>
    a: MyLink,          // All <a> elements become <MyLink>
    h1: 'h2'           // All <h1> elements become <h2>
  }
};

Passing Node Information to Components

When using custom components, you can access the original hast node:

const MyHeading = ({ children, node, ...props }) => {
  // node contains the original hast element
  console.log('Original element:', node.tagName);
  return <h2 {...props}>{children}</h2>;
};

const nodeOptions = {
  Fragment: prod.Fragment,
  jsx: prod.jsx,
  jsxs: prod.jsxs,
  components: {
    h1: MyHeading
  },
  passNode: true  // Enables node prop
};

Edge Cases and Special Handling

Whitespace Handling

The plugin preserves meaningful whitespace while processing HTML:

// HTML with whitespace
const htmlWithWhitespace = `
<table>
  <tbody>
    <tr>
      <th>
        Header
      </th>
      <td>
        Data
      </td>
    </tr>
  </tbody>
</table>
`;

// Whitespace is preserved in the JSX output
const result = await processor.process(htmlWithWhitespace);

Table Cell Alignment

By default, obsolete align attributes on table cells are converted to CSS styles:

// HTML: <th align="right">Header</th>
// Becomes: <th style={{textAlign: 'right'}}>Header</th>

// To disable this behavior:
const options = {
  Fragment: prod.Fragment,
  jsx: prod.jsx,
  jsxs: prod.jsxs,
  tableCellAlignToStyle: false  // Keep align attribute as-is
};

Doctype Handling

DOCTYPE declarations are automatically filtered out during transformation as they are not valid JSX:

// HTML with doctype
const htmlWithDoctype = '<!DOCTYPE html><h1>Hello</h1>';

// Only the h1 element is transformed; doctype is ignored
const result = await processor.process(htmlWithDoctype);
// Result: <h1>Hello</h1>

Error Handling

The plugin may throw errors in these cases:

  • Missing Runtime: If required JSX runtime functions (Fragment, jsx, jsxs, or jsxDEV) are not provided
  • Invalid Configuration: If development: true but jsxDEV is not provided, or vice versa
  • Transformation Errors: If the underlying hast-util-to-jsx-runtime encounters invalid HTML structures

Security Considerations

⚠️ XSS Warning: Using rehype-react with untrusted HTML can expose applications to cross-site scripting attacks. Always sanitize HTML before transformation:

import rehypeSanitize from "rehype-sanitize";

const safeProcessor = unified()
  .use(rehypeParse, { fragment: true })
  .use(rehypeSanitize)  // Sanitize before transformation
  .use(rehypeReact, options);

Module Declaration Extensions

The plugin extends unified's TypeScript definitions:

declare module 'unified' {
  interface CompileResultMap {
    /** JSX Element result type for rehype-react */
    JsxElement: JSX.Element;
  }
}

This enables proper TypeScript inference when using the plugin with unified processors.