15

I need to render an HTML (JSX) string in a React class. I don't know if this is possible or not. dangerouslySetInnerHTML is not valid for me because I have different react components inside this file. It's not plain HTML.

I have an example with the expected result: https://jsfiddle.net/86rg50re/1/

var MyComponent = React.createClass({
    propTypes: {
        owner: React.PropTypes.string
    },

    render: function() {
        return <div>Congrats {this.props.owner}! you have rendered MyComponent ({this.props.children})</div>;
    }
});

var Hello = React.createClass({
    render: function() {
        return <div>Header <MyComponent owner={"Daniel"}>Yayyyyyy!</MyComponent></div>;
    }
});

But what I have is this:

var Hello = React.createClass({
    render: function() {
        var content = '<div>Header <MyComponent owner={"Daniel"}>Yayyyyyy!</MyComponent></div>';
        return transformStringToJSX(content);
    }

Obviously transformStringToJSX doesn't exists.

Is there a way to render jsx strings?

Mogsdad
  • 44,709
  • 21
  • 151
  • 275
gyss
  • 1,753
  • 4
  • 23
  • 31
  • Couldn't you just import the file as a JavaScript file and reference variables and components from it like any other import? – Bojangles Oct 21 '15 at 15:00
  • I suppose that is my last option. I'd prefer another option because I want to store only JSX content in that file – gyss Oct 21 '15 at 15:05
  • JSX ends up just being JavaScript. I think your best bet is to export a thin wrapping component and import that as JS. – Bojangles Oct 21 '15 at 15:06
  • Do you mean to use this for example: https://www.npmjs.com/package/htmltojsx. An then execute the js generated? – gyss Oct 21 '15 at 15:15
  • I assume you mean a jsx file not known at compile time. If so, could you pre-compile the external files rather than have them as jsx? – J. Mark Stevens Oct 21 '15 at 18:06
  • Hi Janaka, it could be a solution but my idea is to make it simpler: just render a string that contains JSX – gyss Oct 22 '15 at 09:44

2 Answers2

11

You can use babel to transform it

npm install --save babel-core

Then in your code

var babel = require('babel-core');
var Component = eval(babel.transform('<div><MyComponent /></div>').code);

Please note that it is generally a bad idea to convert strings to executable code.

Wai Ha Lee
  • 8,598
  • 83
  • 57
  • 92
hampusohlsson
  • 10,109
  • 5
  • 33
  • 50
  • Hi ahmohl! It's not working with the babel package, you have to use 'babel-core'. Anyways, if I use "Component" inside my render function it throws an error. – gyss Oct 26 '15 at 12:14
  • This is something similar of what I'm trying to do. But instead of a single component, a whole string containing JSX: https://github.com/facebook/react/issues/3365 – gyss Oct 26 '15 at 12:41
  • 1
    If you use `eval(babel.transform(yourComponentString).code);` it works. However, I would recommend against doing what you are trying to do as it is bad JS practise to convert strings to code. – hampusohlsson Oct 26 '15 at 12:55
  • Im using babel.transform('
    hello
    ').code but get an error 'Unexpected token'
    – chulian Oct 24 '16 at 23:33
  • 1
    @chulian make sure you have specified the react preset. https://babeljs.io/docs/plugins/preset-react/ – hampusohlsson Oct 25 '16 at 03:43
  • @hampusohlsson is there a way to use this inside of your React code? When being used with Webpack, you get a wall of errors. e.g. `ERROR in ./~/debug/src/node.js Module not found: Error: Can't resolve 'net' in '/Users/ben/Desktop/Work/code/ru-coding-challege/node_modules/debug/src' @ ./~/babel-core/lib/transformation/file/logger.js @ ./~/babel-core/lib/transformation/file/index.js @ ./~/babel-core/lib/api/node.js @ ./~/babel-core/index.js @ ./src/js/containers/app-container.js @ multi (webpack)-dev-server/client?http://localhost:3000 ./src/js/index.js ` – Ben Apr 27 '17 at 19:09
  • 1
    And yet evaluating strings is what most template engines do. – max pleaner Aug 01 '17 at 16:00
  • @hampusohlsson could you please elaborate more on how to set the react preset? – Shubhang Arora Apr 25 '19 at 04:49
  • The link for jslinterrors is dead. I don't think it is correct to say that it is "generally" a bad idea, I think it is more accurate to say that there are specific cases when it is fine and there are cases when you should be mindful of security issues that it might cause. – ICW Jan 09 '21 at 00:07
  • Also, this answer is incomplete because it does not run without an error. This is just not a good answer – ICW Jan 09 '21 at 00:59
2

The most upvoted answer simply does not work, skips various crucial steps, and doesn't explain where the code should go. I'm not sure if the poster just didn't actually run the code or what. Anyways, to actually get it working you need to do something like this:

npm install @babel/core

npm install @babel/preset-react

You need to use babel to transform the JSX into valid javascript that can run clientside on the server side. The serverside code in next.js

export async function getServerSideProps() {
    const stringToRender = require('@babel/core').transform('<h1><Component/></h1>', {presets: ['@babel/preset-react']});
    return {
        props: {
            stringToRender
        }
    }
}

Now we need to evaluate the string on the client:

import React from 'react';
import Component from '../component'; // Import your component

export default function Page({stringToRender}) {
    const [content, setContent] = useState(null);
    
    useEffect(()=>{
        // It is necessary to evaluate within useEffect and window so that React are available.
        window.Component = Component; // This makes Component defined in the `eval` call.
        setContent(eval(stringToRender))
    }, [])
    
    return (
        <div>
            {content}
        </div>
    )
}

export async function getServerSideProps() {
    const stringToRender = require('@babel/core').transform('<h1><Component/></h1>', {presets: ['@babel/preset-react']});
    return {
        props: {
            stringToRender
        }
    }
}

This code will actually run to the desired effect. If you have any additional components to render you will need to set them individually on the window object so that the components will be defined when the eval() call is made.

ICW
  • 4,875
  • 5
  • 27
  • 33