Search code examples
reactjsnext.jsrender-to-string

React: Client & Server way to render children to string


How can I render the children as a string in React 18/Next 13?

React says renderToString is not suggested on the client, and it's not clear to me how to render it on the server.

The documentation here gives an example but it's not clear how it works in an actual react component as I get errors that I cannot create another node if the previous one wasn't removed from the head.

import { createRoot } from 'react-dom/client';
import { flushSync } from 'react-dom';

const div = document.createElement('div');
const root = createRoot(div);
flushSync(() => {
  root.render(<MyIcon />);
});
console.log(div.innerHTML); // For example, "<svg>...</svg>"

Source

Whether I get the data on the server or client side, just looking for a working example either or.

function ExampleChildComponent() {
  return (
    <div className="bg-green-500 w-20 h-20">
      Hello I am a green box
      <button className="bg-blue-100 px-6 py-3">I am a button</button>
    </div>
  )
}

function LogChild({ children }: any) {
  // How do you get the children to a string?
  console.log(someFn(children));
  // Interested in both an output showing <GreenBox />
  // and/or the parsed version which shows the stuff inside GreenBox


  return (
    <p>Logged!</p>
  )
}

function App(){
  return (
    <LogChild>
      <ExampleChildComponent />
    </LogChild>
  )
}

Alternatively, if there's an open source project that I can just study works too. Google is very sparse in examples for this question. Either that or answers are pre React 18.


Solution

  • Something like this works.

    Create a div, call the createRoot() function on that div. Then render the component into that root and the code will be the innerHTML of the div.

    import { createRoot } from "react-dom/client";
    import { flushSync } from "react-dom";
    
    function Example() {
      const [code, setCode] = useState("");
    
      const handleClick = () => {
        const div = document.createElement("div");
        const root = createRoot(div);
        flushSync(() => root.render(<MyComponent />)); 
        console.log(div.innerHTML);
        setCode(div.innerHTML);
      };
    
      return (
        <div>
          <code>
            <pre>{JSON.stringify(code, null, 2)}</pre>
          </code>
          <div className="flex space-x-4">
            <button onClick={handleClick}>Get code</button>
            <button onClick={() => setCode("")}>Clear code</button>
          </div>
        </div>
      );
    }