October DealsAmazon USOctober deal check: compare before you payAmazon US: current deals, useful picks and tech finds.Check DealsWindows FixRecommendedWindows errors stealing your time? Find the fix fastScan stability, cleanup and performance issues.Fix NowOctober DealsAmazon USDeal season is back - check today's better picksAmazon US: current deals, useful picks and tech finds.See Picks×
Skip to content
HTML

How to Convert a React String to HTML (Every Common Case)

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

“Convert a React string to HTML” can mean three different jobs. If you have a React element or component tree and need an HTML string, use renderToStaticMarkup for non-interactive output or renderToString (and hydration) for an interactive app. If you already have an HTML string, you are inserting markup, not converting React; use dangerouslySetInnerHTML only with trusted, sanitized content. If you have plain text, render it as a normal React child so React escapes it.

The right API depends on the input, whether the result must hydrate, and whether rendering happens on a server or in a browser.

Choose the operation before writing code

What you have What you need Use Main limitation
A React node, element, or component tree Static HTML text for an email, file, or API response renderToStaticMarkup The result cannot be hydrated.
A React tree for an interactive page Initial server HTML that the browser will activate renderToString plus hydrateRoot It returns immediately; it does not stream or wait for data.
A React tree on a server that should arrive progressively Streamed HTML renderToPipeableStream in Node.js or renderToReadableStream in Web Streams runtimes The API is runtime-specific.
A React tree in browser code The generated DOM HTML Render into a temporary element, flush, then read innerHTML Do not ship server-rendering code to the client just to serialize markup.
An existing HTML string Markup displayed by React dangerouslySetInnerHTML Untrusted input can create an XSS vulnerability.
Plain text Safe text inside an element Use it as a normal React child It remains text; tags in the string are not interpreted.

These distinctions match React’s server-rendering and common-component guidance: renderToStaticMarkup produces non-interactive markup, while renderToString is for the initial HTML of an app that will hydrate.

Convert a React component to a static HTML string

For a non-interactive result, import renderToStaticMarkup from react-dom/server. It returns a string containing the rendered tree and is suitable for static page generation or content such as emails.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
#1 Best Overall
Sale
HTML and CSS: Design and Build Websites
  • HTML CSS Design and Build Web Sites
  • Comes with secure packaging
  • It can be a gift option
import React from 'react';
import { renderToStaticMarkup } from 'react-dom/server';

function Invoice({ number, total }) {
  return (
    <main>
      <h1>Invoice {number}</h1>
      <p>Total: ${total}</p>
    </main>
  );
}

const html = renderToStaticMarkup(
  <Invoice number="A-1042" total="125.00" />
);

console.log(html);

The returned value is ordinary JavaScript text. You can write it to a file, place it in an email body, or send it in an HTTP response. Because static markup has no React hydration data, do not call hydrateRoot on this output; if you need interactivity, use the next approach instead.

Render HTML that will become an interactive React app

Use renderToString when the server must send initial markup and the browser will attach React event handlers afterward. The server output is only the first render; the client must hydrate the same tree.

// server.mjs
import React from 'react';
import { renderToString } from 'react-dom/server';
import { App } from './App.js';

const html = renderToString(<App />);
// Insert html into your document response.
// browser entry
import { hydrateRoot } from 'react-dom/client';
import { App } from './App.js';

hydrateRoot(document.getElementById('root'), <App />);

renderToString returns immediately. It does not stream output or wait for suspended data; when a child suspends, React renders the nearest Suspense fallback in this mode. If progressive delivery or data-aware rendering is required, use the streaming or prerender APIs described below.

Keep server and client markup identical

  • Render the same component tree and initial props on both sides.
  • Avoid reading browser-only values during the server render; defer them until the client is hydrated.
  • Make sure locale, timezone, and random values do not produce different text between renders.
  • Place the same root element in the document that the client passes to hydrateRoot.

Stream or wait for data on the server

For progressive server output, React provides renderToPipeableStream for Node.js and renderToReadableStream for Web Streams runtimes. These APIs let the response begin while parts of the tree are still resolving, subject to the Suspense boundaries in your app.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

If the requirement is static output that waits for data rather than streaming chunks as they become available, use React’s prerender API in Web Streams environments and the documented Node.js counterpart. Choose the API that matches your server runtime; the current server API index is maintained at React’s Server React DOM APIs reference, and the static API is documented at prerender.

Convert a React tree to HTML in the browser

React does not recommend importing the server-rendering package into client code merely to call renderToString. Instead, create a temporary DOM element, render into it with createRoot, synchronously flush the update, and read innerHTML.

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

export function reactToHtml(node) {
  const container = document.createElement('div');
  const root = createRoot(container);

  flushSync(() => {
    root.render(node);
  });

  const html = container.innerHTML;
  root.unmount();
  return html;
}

const html = reactToHtml(
  <article>
    <h2>Rendered in the browser</h2>
    <p>This is now a string of DOM markup.</p>
  </article>
);

console.log(html);

flushSync is important here: without a synchronous flush, reading innerHTML immediately after root.render can occur before the DOM update is committed. This method serializes the DOM produced by the browser, so browser-only behavior and effects should be treated separately from the initial render.

When the “React string” is actually plain text

A JavaScript string used as a React child is text, not HTML. React escapes characters such as < and & when it renders the child.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
function Message({ text }) {
  return <p>{text}</p>;
}

// The following is displayed literally, not interpreted as a tag:
<Message text="<strong>Not bold</strong>" />

This is the safe default for user-entered content. Do not parse a text string as markup unless you have a specific, trusted-content requirement and a sanitizer appropriate to your input.

Insert an existing HTML string into a React component

If your input is already HTML, the operation is insertion rather than conversion. React’s escape hatch is dangerouslySetInnerHTML:

function ArticleBody({ sanitizedHtml }) {
  const markup = { __html: sanitizedHtml };
  return <div dangerouslySetInnerHTML={markup} />;
}

Only pass content that is trusted and sanitized. React’s common-components documentation warns that untrusted HTML, including user-provided content, can introduce an XSS vulnerability; see the React common-components reference. Sanitizing after insertion is too late, because the browser may already have parsed dangerous elements or attributes. Prefer ordinary React children when the value should remain text.

Trusted Types and sanitization

React supports a TrustedHTML value when your application enforces Trusted Types, but the policy that creates that value must still ensure the source is trusted and sanitized. Trusted Types do not make arbitrary user HTML safe by themselves.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Common errors and fixes

“Objects are not valid as a React child”

You may be passing a component object or an unrendered data structure where React expects a node. Pass an element such as <Card /> to the rendering API, or map your data to elements first.

The result is empty or incomplete in browser code

Read innerHTML only after the render has committed. The browser technique above uses flushSync; also ensure the component does not depend on asynchronous work that has not completed.

Hydration warnings appear

The server string and the client tree differ. Check serialized props, locale and timezone settings, random IDs, browser-only branches, and the exact root element. Render deterministic initial content, then update client-only values after hydration.

Rank #4
Sale
Web Design with HTML, CSS, JavaScript and jQuery Set
  • Brand: Wiley
  • Set of 2 Volumes
  • A handy two-book set that uniquely combines related technologies Highly visual format and accessible language makes these books highly effective learning tools Perfect for beginning web designers and front-end developers

Suspense content is not present in renderToString

This API does not wait for suspended data and emits the nearest fallback. Use a streaming API for progressive Suspense handling or a prerender API when the output must wait for data.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

HTML tags show as text

That is expected when a string is supplied as a normal child. Use React elements for structure. If the value is an existing HTML document, use sanitized dangerouslySetInnerHTML only when the security requirements are met.

Scripts or event handlers do not work in static output

renderToStaticMarkup creates markup; it does not attach React event handlers and its output cannot be hydrated. Use the server-plus-hydration workflow for an interactive app.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Performance, output, and security considerations

  • Use static rendering for one-way output such as emails and prebuilt documents; it avoids hydration work because it is deliberately non-interactive.
  • Use renderToString only when the browser will hydrate the result. Sending interactive markup without hydrating it adds bytes without enabling behavior.
  • Streaming can improve time-to-first-byte for large trees, while prerendering is appropriate when complete data is required before sending output.
  • In-browser serialization creates and mounts a real DOM tree, so remove the temporary root with unmount after reading the markup.
  • Never treat HTML escaping and HTML sanitization as interchangeable. Escaping keeps a value as text; sanitization removes unsafe markup from content you intentionally render as HTML.
  • Test the serialized output for required attributes and text, and test the hydrated app separately for event handlers and client behavior.

Or skip the browser setup

If your actual goal is to capture the rendered page as an image or PDF after it has become HTML, ScreenshotNeo can do the browser work with one request. It accepts consent banners before capture and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each step can be turned off. Bot checks, CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and each response identifies the result with X-Page-Verdict and X-Billed headers. Its MCP server provides take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients.

See the ScreenshotNeo API documentation for all options. A direct call looks like this:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);

The Free plan includes 1,000 screenshots a month with no card. Paid plans start at $5 for 3,000 shots, and every feature is available on every plan. Create a free ScreenshotNeo account.

FAQ

Can static markup be hydrated later?

No. Use renderToString when the output must become an interactive React app.

Should I use innerHTML on a React element?

No. Render the element into a temporary root, flush it, and read that root’s innerHTML.

Is dangerouslySetInnerHTML a converter?

No. It inserts an existing HTML string, and it requires trusted, sanitized content.

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Frequently Asked Questions

Can static markup be hydrated later?

No. Use renderToString when the output must become an interactive React app.

Should I use innerHTML on a React element?

No. Render the element into a temporary root, flush it, and read that root’s innerHTML.

Is dangerouslySetInnerHTML a converter?

No. It inserts an existing HTML string, and it requires trusted, sanitized content.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Leave a Reply

Your email address will not be published. Required fields are marked *

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Read next

Recommended PC Tool
Recommended PC Tool
Crashes, No Sound, or Screen Glitches?Free driver scan
PC Slower Than It Used to Be?Free scan - under a minute

Two free Windows tools

One Free Minute Could Fix That PC

Before you go - each of these free tools takes about a minute and tackles what quietly slows a Windows PC down.

Special offer. View Outbyte info, uninstall instructions, EULA, and Privacy Policy.