83

How would I take a string, and convert it to jsx? For example, if I bring in a string from a textarea, how could I convert it to a React element;

var jsxString = document.getElementById('textarea').value;

What is the process I would use to convert this on the client? Is it possible?

Felix Kling
  • 795,719
  • 175
  • 1,089
  • 1,143
jhamm
  • 24,124
  • 39
  • 105
  • 179

13 Answers13

56

You can consider using the React attribute dangerouslySetInnerHTML:

class YourComponent{
  render() {
    someHtml = '<div><strong>blablabla<strong><p>another blbla</p/></div>'
     
    return (
      <div className="Container" dangerouslySetInnerHTML={{__html: someHtml}}></div>
    )
  }
}
Remolten
  • 2,614
  • 2
  • 25
  • 29
user8296051
  • 601
  • 5
  • 2
  • 16
    This is not working in case of rendering **React Components** `Don't see your post Add Your Post` In this case `` is not getting rendered as it should be – Ali Sajid Apr 11 '19 at 10:35
  • 3
    `dangerouslySetInnerHTML` doesn't allow for camel-case name inside of tags and it will convert them to lower case. This is because it is meant for html tags. – Jacksonkr Sep 06 '19 at 19:26
  • 2
    **Warning** : `dangerouslySetInnerHTML` is vulnurable to XSS attacks. – Trect Mar 01 '20 at 13:08
33

2023 answer update

Seem react-html-parser has no longer been maintained for a few years already. As pointed on the comment by Bill Keller, there is a library still actively maintained named html-react-parser. However, please read carefully on the Readme and make some considerations before using it in production. Like in this section Is this XSS safe?, if it is acceptable, then you are good to go.

Another alternative sounds more promising to me because it emphasizes "Safely" on their repo and home page. The library is named "Interweave". Please check the following repo for more detail about installation and usage: https://github.com/milesj/interweave/

2018 answer

Personally, I love to do it just like in the previous answer which recommends the usage of dangerouslySetInnerHTML property in JSX.

Just for an alternative, nowadays there is a library called react-html-parser. You can check it and install from NPM registry at this URL: https://www.npmjs.com/package/react-html-parser. Today's weekly download statistic for that package is 23,696. Looks a quite popular library to use. Even it looks more convenient to use, my self, still need more read and further consideration before really using it.

Code snippet copied from the NPM page:

import React from 'react';
import ReactHtmlParser, { processNodes, convertNodeToElement, htmlparser2 } from 'react-html-parser';

class HtmlComponent extends React.Component {
  render() {
    const html = '<div>Example HTML string</div>';
    return <div>{ ReactHtmlParser(html) }</div>;
  }
}
Bayu
  • 2,340
  • 1
  • 26
  • 31
  • 1
    Much better than the `dangerouslySetInnerHTML` option in my opinion – Sam Walpole Feb 07 '19 at 16:11
  • 2
    It does the same thing as `dangerouslySetInnerHTML`. No use – Trect Mar 01 '20 at 13:10
  • 10
    The year is 2020. The active Github repo to check out is: [https://github.com/remarkablemark/html-react-parser](https://github.com/remarkablemark/html-react-parser) – Bill Keller Jul 25 '20 at 10:32
  • @Trect, you better check the documentation. Though actually now it is outdated. Please check updated answer for more updated lib and better alternative. – Bayu Feb 28 '23 at 12:09
  • @BillKeller, thanks for sharing. I have updated my answer based on your suggestion. You might be interested in checking it out. It has an additional library mentioned there. Thanks. – Bayu Feb 28 '23 at 12:11
16

Here's how you can do it, without using dangerouslySetInnerHTML.

import React from "react";

let getNodes = str =>
  new DOMParser().parseFromString(str, "text/html").body.childNodes;
let createJSX = nodeArray => {
  return nodeArray.map(node => {
    let attributeObj = {};
    const {
      attributes,
      localName,
      childNodes,
      nodeValue
    } = node;
    if (attributes) {
      Array.from(attributes).forEach(attribute => {
        if (attribute.name === "style") {
          let styleAttributes = attribute.nodeValue.split(";");
          let styleObj = {};
          styleAttributes.forEach(attribute => {
            let [key, value] = attribute.split(":");
            styleObj[key] = value;
          });
          attributeObj[attribute.name] = styleObj;
        } else {
          attributeObj[attribute.name] = attribute.nodeValue;
        }
      });
    }
    return localName ?
      React.createElement(
        localName,
        attributeObj,
        childNodes && Array.isArray(Array.from(childNodes)) ?
        createJSX(Array.from(childNodes)) :
        []
      ) :
      nodeValue;
  });
};

export const StringToJSX = props => {
  return createJSX(Array.from(getNodes(props.domString)));
};

Import StringToJSX and pass the string in as props in the following format.

<StringToJSX domString={domString}/>

PS: I might have missed out on a few edge cases like attributes.

Alex
  • 2,154
  • 3
  • 26
  • 49
Prakash N
  • 161
  • 1
  • 5
  • 3
    Not sure why this doesn't have the most upvotes.. Fantastic! I used this for svg's although I had to change a bunch, but still helped a whole bunch! dangerouslySetInnerHtml doesn't work that well with SVG's, this works great. – Brad Vanderbush Sep 05 '20 at 05:19
  • Unfortunately this doesn't work with server-side rendering because `DOMParser` is browser specific – Etienne Martin Oct 01 '21 at 17:50
  • @EtienneMartin Should be able to use jsdom the same way from Node – BuckFilledPlatypus Jan 06 '22 at 23:11
  • here is this function (with typescript) in a blitz if anyone wants to play around with it: https://stackblitz.com/edit/react-ts-kbt7rs?file=StringToJSX.tsx,App.tsx – benmneb Dec 06 '22 at 06:43
  • Not good enough, it converts React components to lowercase – Cedric Ipkiss Aug 14 '23 at 14:41
14

I came across this answer recently and, it was a good deal for me. You don't need to provide a string. Returning an array of JSX elements will do the trick.

We can store JSX elements in JavaScript array.

let arrUsers = [<li>Steve</li>,<li>Bob</li>,<li>Michael</li>];

and in your HTML (JSX) bind it like,

<ul>{arrUsers}</ul>

As simple as it is.

Balasubramani M
  • 7,742
  • 2
  • 45
  • 47
10

If you consider string

<div>Hello World</div>

If we are very strict, this actually is the valid JSX. The question is how to compile this JSX string into React code.

Easiest and the recommended way is to download some library like Babel and use it to transform the code. Babel can run in the Browser like the repl does.

It is also possible to transform JSX to other formats, but in this case you have to find a compiler or create one yourself.

The steps to create the JSX => React transformation yourself is:

  1. transform the code string into AST representation
  2. parse the AST and output code back to string

So you need somekind of AST parser like espree supporting JSX and then you can create a code which walks the AST tree and outputs something, like React -code out of it.

The AST tree of JSX data consists of normal JavaScript AST together with JSX nodes. The parser should walk through the tree and transform the JSX nodes into normal JavaScript code.

If you compile to React and encounter a JSX node with tag "div" you should compile that into React.createElement("div",... call with attributes and subnodes found under that AST node inserted as parameters of that call.

I have created a small AST Walker, which can process AST tree, ASTWalker, which can be used to transform the AST tree into some output format, like React or DOM.

On-line example of how to use it is here:

http://codepen.io/teroktolonen/pen/KzWVqx?editors=1010

The main code looks like this:

    // here is the JSX string to parse
    var codeStr = "<div>Hello world</div>";
    var walker = ASTWalker({
        defaultNamespace: "react",
    });
    // compile AST representation out of it.
    var rawAST = espree.parse(codeStr, {
          ecmaVersion: 6,
          sourceType: "script",
          // specify additional language features
          ecmaFeatures: {
            // enable JSX parsing
            jsx: true
          } 
        });

   // then you can walk the walk to create the code
   walker.startWalk( rawAST, {} );
   var code = walker.getCode();  
   console.log(code); 
   document.getElementById("sourceCode").innerHTML = code;

DISCLAIMER: The library is not intented for compiling into React. It is mostly used with defaultNamespace: "DOM", using it to compile into plain JavaScript + DOM representation. Trying anything more complicated than simple tags may result as an error.

The important thing is to notice that React is not only possible output format for JSX.

Tero Tolonen
  • 4,144
  • 4
  • 27
  • 32
  • What you have here: `
    Hello World
    ` isn't a string. But you are right to say that it is JSX. This: `"
    Hello World
    "` is a string and is not JSX. there is no such thing as a "JSX string". Be careful how you word your answers as it might be confusing.
    – Sean Clancy Aug 26 '19 at 17:28
6

I've been using html-to-react with some success (self closing tags cause a problem though, but a fix is in the pull requests...) to parse markup strings as DOM like objects, and in turn React elements. It's not pretty, and if you can avoid it, do so. But it gets the job done.

html-to-react at github: https://github.com/mikenikles/html-to-react

dannyjolie
  • 10,959
  • 3
  • 33
  • 28
5

Use React-JSX-Parser

You can use the React-JSX-Parser library dedicated for this.

npm install react-jsx-parser

here is the repo

Abraham
  • 12,140
  • 4
  • 56
  • 92
3

html-react-parser is what you need.

import parse from 'html-react-parser';
import React from 'react';

export default function YourComponent() {
    someHtml = '<div><strong>blablabla<strong><p>another blbla</p/></div>'
     
    return (
        <div className="Container">{parse(someHtml)}</div>
    )
}
TheBotlyNoob
  • 369
  • 5
  • 16
0

Here's a little utility component for this:

const RawHtml = ({ children="", tag: Tag = 'div', ...props }) =>
  <Tag { ...props } dangerouslySetInnerHTML={{ __html: children }}/>;

Sample usage:

<RawHtml tag={'span'} style={{'font-weight':'bold'}}>
  {"Lorem<br/>ipsum"}
</RawHtml>
TheZver
  • 1,502
  • 2
  • 14
  • 19
0

First you can change it to a non-string array and then use it as JSX

class ObjReplicate extends React.Component {
  constructor(props) {
    super(props);
    this.state = { myText: '' };
  }
  
  textChange=(e) =>{this.setState({ myText: e.target.value })};

  render() {
    const toShow = this.state.myText;
    var i=1;
    var allObjs=new Array;
    while (i<100){
      i++;
      allObjs[i] = <p>{toShow}</p>; //non-sting array to use  in JSX
   }
   
    return (
      <div>
        <input onChange={this.textChange}></input>
        {allObjs}
      </div>
   );
  }
}

ReactDOM.render(<ObjReplicate/>,document.getElementById('root'));
Dharmik Patel
  • 1,041
  • 6
  • 12
  • As it’s currently written, your answer is unclear. Please [edit] to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Mar 25 '22 at 10:20
0
var jsxString = document.getElementById('textarea').value;
return <>{jsxString}</>;
setec
  • 15,506
  • 3
  • 36
  • 51
  • Please use the edit link on your question to add additional information. The Post Answer button should be used only for complete answers to the question. – Nerdroid Mar 03 '23 at 05:11
0

None of the answers allow you to maintain the capitalization of React components, in other words, just like with dangerouslySetInnerHTML, whenever the string is rendered, ExampleComponent becomes examplecomponent (in lower case).

Improving on TheBotlyNoob's answer, you can circumvent that by using html-react-parser and then replacing all lowercased React components. It's even more fluid as it allows you to define additional custom attributes on the replacement component, as well as preserve the component's children. For example:

import parse, { domToReact } from 'html-react-parser';
import SomeComponent from 'some-component';

...
const options = {
    replace: ({ attribs, children }) => {  
      if (!attribs) {
        return;
      }
      if (attribs.class === 'replace') {
        return <SomeComponent someAttribute="" className="anyClass">{domToReact(children, options)}</SomeComponent>
      }
    }
};

...
<div>
  {parse(dynamicHTML, options)}
</div>
Cedric Ipkiss
  • 5,662
  • 2
  • 43
  • 72
-8

You need to use babel with preset react

npm install --save-dev babel-cli babel-preset-react

Add the following line to your .babelrc file:

{
  "presets": ["react"]
}

Then run

./node_modules/.bin/babel script.js --out-file script-compiled.js

You can find more info here

Dmitriy Nevzorov
  • 6,042
  • 1
  • 20
  • 28
  • 1
    So there is no way to do it dynamically. I have to run something from the command line to do it? I want to be able to enter a string into a `textarea` and render it on the page. – jhamm Mar 19 '16 at 16:53
  • No, it's just for the demonstration, you can go to http://babeljs.io/ and see implementation for lots of tools like gulp or grunt – Dmitriy Nevzorov Mar 19 '16 at 16:54
  • you can do `require("babel-core").transform("your code");` – Dmitriy Nevzorov Mar 19 '16 at 16:57