CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-rehype-react

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

Pending
Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Pending

The risk profile of this skill

Overview
Eval results
Files

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.

Workspace
tessl
Visibility
Public
Created
Last updated
Describes
npmpkg:npm/rehype-react@8.0.x
Publish Source
CLI
Badge
tessl/npm-rehype-react badge