2

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

Chris
  • 57,622
  • 19
  • 111
  • 137
Rana
  • 1,170
  • 3
  • 14
  • 28
  • Try using dangerouslySetInnerHTML - https://facebook.github.io/react/docs/dom-elements.html#dangerouslysetinnerhtml – Dan Aug 22 '17 at 11:57
  • How about React.createElement(elementString, propsObject) ? – Isa Ishangulyyev Aug 22 '17 at 11:59
  • @Dan didn't work, bcz dangerouslySetInnerHTML expect compiled html string, but the string I have is not compiled – Rana Aug 22 '17 at 12:01
  • @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 Aug 22 '17 at 12:03

2 Answers2

8

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.

Community
  • 1
  • 1
Chris
  • 57,622
  • 19
  • 111
  • 137
  • 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 Aug 22 '17 at 12:13
  • this is how the result looks like with css in place https://codepen.io/anon/pen/oeddYN?editors=1111 – Rana Aug 22 '17 at 12:15
  • 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? – Dan Zuzevich Aug 22 '17 at 12:50
  • @DanielZuzevich dangerouslySetInnerHTML expect compiled html, so there is no way to make it compile string that has components inside like, and the string that I have is non compiled html , so I need a way to compile the html then pass it to dangerouslySetInnerHTML – Rana Aug 22 '17 at 13:09
  • @Rana, I have updated my answer. Please have a look. – Chris Aug 22 '17 at 14:02
  • @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 Aug 22 '17 at 14:17
  • @Chris: i faced this problem with babel https://github.com/babel/babel/issues/2961 – Rana Aug 22 '17 at 14:20
  • @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 Aug 22 '17 at 15:45
  • @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 Aug 23 '17 at 07:46
  • @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' ' – Rana Aug 25 '17 at 14:46
  • 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! – Rodrigo Pires Jul 23 '18 at 14:10
  • @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) – Chris Jul 23 '18 at 14:12
  • @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: `{item.name}`. 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! – Rodrigo Pires Jul 24 '18 at 17:05
0

Try to use react-string-format npm package. Example:

import { format } from 'react-string-format';
...
format('hiperlink: {0}, span tag: {1}', <a href="#">Click Me</a>, <span>Bla-bla</span>); 
Berezh
  • 942
  • 9
  • 12