34

I'm trying to render LaTeX strings in a React project. Although I use the react-mathjax React components, I want to get an HTML string made from the LaTeX strings in order to concatenate it and the other strings and set it by dangerouslySetInnerHTML.

My current code I tried

Sample cod here

  1. LaTeX strings are given as strings
  2. Make an empty DOM aDom by document.createElement('span') (in background. not in the document DOM tree.)
  3. Render a LaTeX string by ReactDOM.render into aDom
  4. After rendering, get a string by aDom.innerHTML or .outerHTML

Problem

The value of aDom.innerHTML (or .outerHTML) is "<span><span data-reactroot=\"\"></span></span>" (almost empty) although aDom has a perfect tree that MathJax generated.

Briefly,

  1. aDom:
  2. aDom.outerHTML:

enter image description here

Question

How can I get the 'correct' HTML string from aDom above?

DᴀʀᴛʜVᴀᴅᴇʀ
  • 7,681
  • 17
  • 73
  • 127
Nyoho
  • 467
  • 1
  • 4
  • 8

3 Answers3

97

This seems to work just fine if you want to render any component to a HTML string:

import { renderToStaticMarkup } from 'react-dom/server'

function componentToString() {
  return renderToStaticMarkup(<MyAwesomeComponent some="props" or="whatever" />)
}
wrdevos
  • 2,029
  • 16
  • 19
  • (Sorry to be late.) I rewrote it with `renderToString` in renderToString branch: https://github.com/NyohoSampleCodes/test-react-project/blob/renderToString/src/components/tex.js#L20 But it seems to be still empty. – Nyoho Jun 05 '19 at 11:22
  • 2
    Is a `function` missing? And maybe with another name, it's a but confusing to reuse the imported one. – tokland Jan 08 '21 at 12:54
  • Loved it!! Too useful for properties that needs an html string. Now create a component, then stringify to html. Beautiful :) – Emisael Carrera Mar 07 '21 at 20:49
9

From what I see, you are getting what you'd expect to get.

Given a root element (aDom in your case), ReactDOM will render it's root component inside this element, and this component's element will have the attribute data-reactroot.

So what you are seeing is exactly how it should be. From what I've tested, the inner dom tree should be there as well.

var Another = React.createClass({
  render: function() {
    return (
      <div>Just to see if other components are rendered as well</div>
    );
  }
});

var Hello = React.createClass({
  render: function() {
    return (
      <div id="first"> 
        <div id="sec-1">Hello</div>
        <div id="sec-2">{ this.props.name }</div>
        <Another />
      </div>
    );
  }
});

var a = document.createElement('div');

ReactDOM.render(
  <Hello name = "World" /> ,
  a
);

console.log(a.outerHTML);
<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>

The result in the console is:

<div><div data-reactroot="" id="first"><div id="sec-1">Hello</div><div id="sec-2">World</div><div>Just to see if other components are rendered as well</div></div></div>
squgeim
  • 2,321
  • 1
  • 14
  • 21
  • Opps, I was getting what I'd expect to get! So, is the problem probably `react-mathjax`? – Nyoho Apr 23 '17 at 11:09
  • What do you get when you `console.log(c)` at the beginning of `componentToString` function? – squgeim Apr 23 '17 at 11:49
  • Inserting `console.log(c)` into line 1 of `componentToString` function, I got http://imgur.com/6Q9sjNm (It seems to be a React element.) – Nyoho Apr 23 '17 at 12:59
  • I see from the screenshot you have posted in the question that MathJax inserts a script tag in the DOM which presumably renders the output in the corresponding span. I believe this is the reason you find the span empty when rendering to memory. If you do need to do it this way, you could make a invisible div in the real DOM (`display: none`) and render your component here. You should get the full content there. – squgeim Apr 24 '17 at 04:15
  • I guess I just don't understand it... What is the difference between your sample code above and my MathJax case? Why does not your case need to make an invisible div in a real DOM of the page? – Nyoho Apr 25 '17 at 22:59
  • The execution of the `script` tag is done by the browser when this script is present in the DOM. When you render the `MathJax` in memory in `componentToString`, the script tag is inserted but it is not executed as it has not reached the browser's DOM, so the text is not present. In general it is considered a bad practice to have `script` tags inside react components. – squgeim Apr 26 '17 at 04:28
  • this code does not work anymore, because react behavior is different now and render is not guaranteed to be synchronous. You need to use callback. See https://github.com/facebook/react/issues/12227 – T.P. May 27 '19 at 19:29
1

I recommend using renderToStaticMarkup over renderToString as it does not add any extra DOM attributes that React uses internally, like `data-reactroot:

import { renderToStaticMarkup } from 'react-dom/server'

getString() {
  return renderToStaticMarkup(<AnyComponent />)
}

Documentation:

gazdagergo
  • 6,187
  • 1
  • 31
  • 45
Mark H
  • 741
  • 8
  • 12