Inject JSX-formatted string into React Component

18,318

I originally provided an answer as to how you can insert a string as HTML directly into React. However, since you also want variables inside your string to be parsed (and other potential logic) before it being inserted into React, I have left both options here as they might be useful for future readers.


Case 1 - Your string is ready to be inserted

If the string containing your HTML is ready to be directly inserted into React, and does not contain code that needs to be parsed first, you should use dangerouslySetInnerHTML. You should set it on the wrapping div that will contain the HTML fetched from your API.

See the example below.

var htmlFromApi = '<div className="button-basics-example"><Button color={Colors.SUCCESS}>Save</Button><Button color={Colors.ALERT}>Delete</Button></div>';

var App = ({html}) => { return <div dangerouslySetInnerHTML={{ __html: html}}></div> }

ReactDOM.render(<App html={htmlFromApi}/>, document.getElementById('app'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.js"></script>
<div id="app"></div>

Case 2 - Your string is not ready and needs additional logic before being inserted

If your string isn't "ready" to be injected directly into the DOM, but needs some kind of processing first (e.g your string contains variables that need to be interpreted first), things get more complicated. Unfortunately, there is no good nor "recommended" way to do this.

  • If the manipulation required is simple and fairly static, you could perhaps use regex. Though this approach comes with limitations. Use cases for this approach might be manipulating a DOM class or adding an element to the string.
  • Another approach is to use a library, such as html-to-react, that might help you with what you are looking for.
  • Finally, if you are using Babel (which you almost certainly are), you could use the Babel transformer. All you need is to import babel-core into your code and transform the string into JSX. This approach might be more limited than using a library, but it should suffice.

Here's how to implement point #3 from above:

Babel.transform(code, options);

Where code is your html-string. In options we need to pass an object with {presets: ['react']} so that Babel understands that want JSX as our output. You could of course add other options here also.

This will return an object that contains stuff like the source-map, but we are only interested in the generated code here. So we need to add:

Babel.transform(code, options).code;

Note that code is javascript code in a string-format and it expresses a function call to create a React Element with React.createElement. To execute a string as code in javascript, we can use eval().

We can then add this React Element into our React Component and render it normally, as shown in the example below.


var htmlFromApi = '<div className="button-basics-example"><Button color={Colors.SUCCESS}>Save</Button><Button color={Colors.ALERT}>Delete</Button></div>';
var Colors = {SUCCESS: "green", ALERT: "red"};

var App = ({html}) => {
  var Component = Babel.transform(htmlFromApi, {presets: ["react"]}).code;
  return <div>{eval(Component)}</div>;
};

var Button = ({color, children}) => (
  <button style={{backgroundColor: color}}>{children}</button>
);

ReactDOM.render(<App />, document.getElementById("app"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="app"></div>

For more details about Babel.transform, see the official documentation.

Share:
18,318

Related videos on Youtube

Rana
Author by

Rana

Fresh C# Developer

Updated on June 04, 2022

Comments

  • Rana
    Rana almost 2 years

    I have a small react page that should compile and display html string. the html in the string written in react-foundation

    The page looks like this :

    import * as React from 'react';
    import * as ReactDOM from 'react-dom';
    import { Link, Button, Colors } from 'react-foundation';
    require('./foundation.css')
    
    var htmlFromApi = '<div className="button-basics-example"><Button color={Colors.SUCCESS}>Save</Button><Button color={Colors.ALERT}>Delete</Button></div>';
    
    var App = ({ html }) => { return <div>{html}</div> }
    
    ReactDOM.render(
    <App html={htmlFromApi}/>,
    document.getElementById('react')
    );
    

    The Result of the above code is just a string, I am wondering if there is a way to convert the html string to react element

    something like this :

    var reactElement= toReactElement(htmlFromApi);
    //the result is <div className="button-basics-example"><Button color={Colors.SUCCESS}>Save</Button><Button color={Colors.ALERT}>Delete</Button></div>
    

    PS I tried <div className="content" dangerouslySetInnerHTML={{ __html: htmlFromApi }}></div> and didn't work

    Thanks in advance

    Edit: the code is here

    • Dan
      Dan over 6 years
      Try using dangerouslySetInnerHTML - facebook.github.io/react/docs/…
    • Isa Ishangulyyev
      Isa Ishangulyyev over 6 years
      How about React.createElement(elementString, propsObject) ?
    • Rana
      Rana over 6 years
      @Dan didn't work, bcz dangerouslySetInnerHTML expect compiled html string, but the string I have is not compiled
    • Rana
      Rana over 6 years
      @isa424 I cannot use React.createElement bcz I don't know how the string will look like, and it is very long string with so many nested components
  • Rana
    Rana over 6 years
    thanks for your answer, but this way the values inside the string will not be evaluated or compiled , am I right , sorry I am very poor with react
  • Rana
    Rana over 6 years
    this is how the result looks like with css in place codepen.io/anon/pen/oeddYN?editors=1111
  • Dan Zuzevich
    Dan Zuzevich over 6 years
    Just a quick note, but I don't think you want to include className in the outer div, as it doesn't output properly. It ends up with an attribute of classname="button-basics-example" inside the browser. Using just the word class, and then setting some styling for button-basic-example in the css inside of your pen, I was able to produce some styling, but not when it uses className inside of the html string. Any thoughts on that?
  • Rana
    Rana over 6 years
    @DanielZuzevich dangerouslySetInnerHTML expect compiled html, so there is no way to make it compile string that has components inside like<Button />, and the string that I have is non compiled html , so I need a way to compile the html then pass it to dangerouslySetInnerHTML
  • Chris
    Chris over 6 years
    @Rana, I have updated my answer. Please have a look.
  • Rana
    Rana over 6 years
    @Chris : I just reached the point u mentioned in the comment, but could not make Babel work in my page ? it this the way we import it ( import * as babel from 'babel-core' ) , note that I have this version ("babel-core": "^6.26.0",) and i am using "webpack": "^3.5.0",
  • Rana
    Rana over 6 years
    @Chris: i faced this problem with babel github.com/babel/babel/issues/2961
  • Rana
    Rana over 6 years
    @Chris: why u defined a Button and Colors comonents? if u remove them and add import { Link, Button, Colors } from 'react-foundation'; then the above dove will not work, the string will contain much more than Buttons , it will use almost all components in react-foundation, do u have any idea how can we make it work with react-foundation?
  • Rana
    Rana over 6 years
    @Chris, the error is : after using description file: C:\Test\X\Scripts\Z\node_modules\babel-core\package.json (relative path: ./lib/helpers) resolve as module C:\Test\X\Scripts\Z\node_modules\babel-core\lib\helpers\node‌​_modules doesn't exist or is not a directory C:\Test\X\Scripts\Z\node_modules\babel-core\lib\node_modules doesn't exist or is not a directory C:\Test\X\Scripts\Z\node_modules\babel-core\node_modules doesn't exist or is not a directory C:\Test\X\Scripts\Z\node_modules\node_modules doesn't exist or is not a directory
  • Rana
    Rana over 6 years
    @Chris : babel-core didn't work for me, but babel-standalone worked fine but i had to define the components i use in the htmlString in the same file, did't accept 'import {Button} from 'react-foundation' '
  • Rodrigo Pires
    Rodrigo Pires almost 6 years
    Hey @Chris, I'm trying to implement your solution but I'm having problem importing babel-core into my React component. Wepack says it can't resolve babel-core module. Any tips on that? Thank you!
  • Chris
    Chris almost 6 years
    @RodrigoPires, well, first off I highly suggest going with the 1st option I provided, if possible. If you still want to go with option (2): Did you do npm install babel-core? (or yarn if you use that)
  • Rodrigo Pires
    Rodrigo Pires almost 6 years
    @Chris In my case, I need a full jsx expression to be stored in a string and then evaluated to jsx, including objects/variables and conditionals. ie: <span className={item.active ? "active" : ""}>{item.name}<span>. So yes, I've installed and imported babel-core, that's when the error occurs. If you also know any other solution for my scenario, please let me know! Tks!