Using Fragment to insert HTML rendered on the back end via dangerouslySetInnerHTML

Short Answer

Not possible:

key is the only attribute that can be passed to Fragment. In the future, we may add support for additional attributes, such as event handlers.

https://reactjs.org/docs/fragments.html

You may want to chime in and suggest this as a future addition.

https://github.com/facebook/react/issues

In the Meantime

You may want to consider using an HTML parsing library like:

https://github.com/remarkablemark/html-react-parser

Check out this example to see how it will accomplish your goal:

http://remarkablemark.org/blog/2016/10/07/dangerously-set-innerhtml-alternative/

In Short

You'll be able to do this:

<>
{require('html-react-parser')(
    '<em>foo</em>'
)}
</>

Update December 2020

This issue (also mentioned by OP) was closed on Oct 2, 2019. - However, stemming from the original issue, it seems a RawHTML component has entered the RFC process but has not reached production, and has no set timeline for when a working solution may be available.

That being said, I would now like to allude to a solution I currently use to get around this issue.

In my case, dangerouslySetInnerHTML was utilized to render plain HTML for a user to download; it was not ideal to have additional wrapper tags included in the output.

After reading around the web and StackOverflow, it seemed most solutions mentioned using an external library like html-react-parser.

For this use-case, html-react-parser would not suffice because it converts HTML strings to React element(s). Meaning, it would strip all HTML that wasn't standard JSX.


Solution:

The code below is the no library solution I opted to use:

//HTML that will be set using dangerouslySetInnerHTML
const html = `<div>This is a div</div>`

The wrapper div within the RawHtml component is purposely named "unwanteddiv".

//Component that will return our dangerouslySetInnerHTML
//Note that we are using "unwanteddiv" as a wrapper
    const RawHtml = () => {
        return (
            <unwanteddiv key={[]}
                dangerouslySetInnerHTML={{
                    __html: html,
                }}
            />
        );
    };

For the purpose of this example, we will use renderToStaticMarkup.

const staticHtml = ReactDomServer.renderToStaticMarkup(
<RawHtml/>
);

The ParseStaticHtml function is where the magic happens, here you will see why we named the wrapper div "unwanteddiv".

  //The ParseStaticHtml function will check the staticHtml 
  //If the staticHtml type is 'string' 
  //We will remove "<unwanteddiv/>" leaving us with only the desired output
    const ParseStaticHtml = (html) => {
        if (typeof html === 'string') {
            return html.replace(/<unwanteddiv>/g, '').replace(/<\/unwanteddiv>/g, '');
        } else {
            return html;
        }
  };

Now, if we pass the staticHtml through the ParseStaticHtml function you will see the desired output without the additional wrapper div:

  console.log(ParseStaticHtml(staticHtml));

Additionally, I have created a codesandbox example that shows this in action.

Notice, the console log will throw a warning: "The tag <unwanteddiv> is unrecognized in this browser..." - However, this is fine because we intentionally gave it a unique name so we can easily differentiate and target the wrapper with our replace method and essentially remove it before output.

Besides, receiving a mild scolding from a code linter is not as bad as adding more dependencies for something that should be more simply implemented.