React JSX Renderer

A React component for Rendering JSX

React JSX Renderer

npm (latest) npm (nightlyt)

demo Coverage Status Dependencies Status Install size License: MIT

latest nightly build test lint

A React Component for Rendering JSX.

Description

React JSX Renderer is a React Component for rendering JSX to React nodes.

It has a JavaScript Runtime inside, and can execute the user's JSX with controlled behavior.

Launch Demo

Features

  • Rendering JSX as React nodes
  • TypeScritpt ready
  • Provides CommonJS and ES Modules
  • JavaScript syntax and featues
    • without async, await and generator
  • Injectable custom React components
  • Pass binding variables
  • Applicable filters to parsed nodes
    • You can create allowlist / denylist filters to tagName, attributes or properties
  • Avoid user's call expressions
  • Avoid user's new expressions
  • Parse with meriyah

Installation

  1. npm install -s react-jsx-renderer (or yarn add react-jsx-renderer)
  2. Add import { JSXRenderer } from 'react-jsx-renderer';
  3. <JSXRenderer code="Hello, World" /> to render Hello, World

Requirements

  • React: >= 16.0.0

Options

interface ParseOptions {
  /**
   * Options of parser
   */
  meriyah?: meriyah.Options;

  /**
   * When this option is enabled, always parse as an expression.
   */
  forceExpression?: boolean;
}

interface EvaluateOptions {
  /**
   * binding
   */
  binding?: Binding;

  /**
   * components
   */
  components?: ComponentsBinding;

  /**
   * Prefix of generated keys.
   */
  keyPrefix?: string;

  /**
   * When this option is enabled, no key will be generated
   */
  disableKeyGeneration?: boolean;

  /**
   * When this option is enabled, bindings will be excluded from the component search.
   */
  disableSearchCompontsByBinding?: boolean;

  /**
   * When this option is enabled, Call Expression and New Expression will always return undefined.
   */
  disableCall?: boolean;

  /**
   * When this option is enabled, New Expression will always return undefined.
   */
  disableNew?: boolean;

  /**
   * When this option is enabled, access to undefined variables will raise an exception.
   */
  raiseReferenceError?: boolean;

  /**
   * List of functions allowed to be executed.
   *
   * If empty, all functions will be allowed to execute.
   */
  allowedFunctions?: AnyFunction[];

  /**
   * Add user-defined functions to the allowed list.
   */
  allowUserDefinedFunction?: boolean;

  /**
   * List of functions denied to be executed.
   *
   * If empty, all functions will be allowed to execute.
   */
  deniedFunctions?: AnyFunction[];
}

interface RenderingOptions {
  /**
   * List of filters to be applied to elements.
   */
  elementFilters?: JSXElementFilter[];

  /**
   * List of filters to be applied to fragments.
   */
  fragmentFilters?: JSXFragmentFilter[];

  /**
   * List of filters to be applied to text nodes.
   */
  textFilters?: JSXTextFilter[];

  /**
   * When this option is enabled, non-existent HTML elements will not be rendered.
   */
  disableUnknownHTMLElement?: boolean;

  /**
   * Function to determine Unknown HTML Element
   */
  isUnknownHTMLElementTagName?: UnknownHTMLElementTagNameFunction;
}

interface RendererOptions extends {
  /**
   * JSX code
   */
  code?: string;

  /**
   * The component that will be displayed instead when an error occurs.
   */
  fallbackComponent?: JSXFallbackComponent;

  /**
   * If you want to receive the parsed result, set a Ref object to this option.
   */
  refNodes?: Ref<JSXNode[]>;
}

Usage

Using like a simple HTML template engine

input:

import { render } from 'react-dom';
import { JSXRenderer } from 'react-jsx-renderer';

const root = document.getElementById('root');

render(
  <JSXRenderer
    binding={{ name: 'Sho Kusano' }}
    code={'<p>Hello, {name}</p>'}
  />,
  root
);

to:

<p>Hello, Sho Kusano</p>

Using JSX with JavaScript expressions

input:

render(
  <JSXRenderer
    binding={{
      three: 3,
      seven: 7,
    }}
    code={
      '<p>+ {three + seven}</p>' +
      '<p>- {three - seven}</p>' +
      '<p>bitwise shift {three << seven}</p>'
    }
  />,
  root
);

to:

<p>+ 10</p>
<p>- -4</p>
<p>bitwise shift 384</p>

Using JSX with your favorite custom components

const Red = ({ children }) => <b style={{ color: 'red' }}>{children}</b>

render(
  <JSXRenderer
    components={{ RedColor: Red }}
    code={'<p><RedColor>red</RedColor></p>'}
  />,
  root
);

to:

<p><b style="color: red">red</b></p>

Convert JSX with filters

const hrefFilter = (element: JSXElement) => {
  const { props, component, children } = element;
  if (component !== 'a') return element;

  let href = props.href || '';
  if (href.includes('//')) {
    href = secureURLConvert(href); // Add prefix https://secure.url/redirect?url=
  }
  const filteredProps = { ...props, href };
  return { component, children, props: filteredProps };
}

render(
  <JSXRenderer
    elementFilters={[hrefFilter]}
    code={
      '<p><a href="/">root</a></p>' +
      '<p><a href="../">upper directory</a></p>' +
      '<p><a href="subdir">sub directory</a></p>' +
      '<p><a href="https://github.com/">github</a></p>' +
    }
  />,
  root
);

to:

<p><a href="/">root</a></p>
<p><a href="../">upper directory</a></p>
<p><a href="subdir">sub directory</a></p>
<p><a href="https://secure.url/redirect?url=https://github.com">github</a></p>

Provide options by context

ex: Server side rendering.

import { JSDOM } from 'jsdom';

render(
  <JSXRendererOptionsProvider isUnknownHTMLElement={(tagName) => {
    const { window } = new JSDOM();
    return window.document.createElement(tagName) instanceof window.HTMLUnknownElement;
  }}>
    <JSXRenderer
      code={
        '<p><unknown>Avoid</unknown></p>'
      }
    />
  </JSXRendererOptionsProvider>,
  root
);

to:

<p></p>

License

MIT License

Related projects