React ReactDOMServer: Two Explanations Quiz
Two explanations of `react-dom/server` rendering APIs (string vs streaming) plus companions on streaming SSR with `renderToPipeableStream` and on hydration mismatches.
483 views
15
Explain what react-dom/server's renderToString and renderToStaticMarkup do, and when you would reach for each. Include the trade-off around React's internal DOM attributes such as data-reactroot.
Examples
Example 1:
Input:
import { renderToString, renderToStaticMarkup } from 'react-dom/server';
renderToString(<App />);
renderToStaticMarkup(<App />);
Output:
renderToString -> HTML with React internal markers (used when the client will hydrate).
renderToStaticMarkup -> identical HTML without React internal markers (used for static pages).
Explanation: only renderToString produces output that React can later hydrate; renderToStaticMarkup is for pages that will never be rehydrated.import { renderToString, renderToStaticMarkup } from 'react-dom/server';
function App() {
return <h1>Hello SSR</h1>;
}
// pick the right API for the consumer:
const forHydration = renderToString(<App />);
const forStaticPage = renderToStaticMarkup(<App />);
Describe the full server-side rendering pipeline that ships HTML from a Node server and then hands control to React on the client. Cover where renderToString slots in and what hydrateRoot does.
Examples
Example 1:
Input: a request hits a Node SSR server
Output sequence:
1. Server runs renderToString(<App />), interpolates the HTML into a page shell, ships it.
2. Browser parses the HTML; the page is visible before React loads (fast First Contentful Paint).
3. Bundle loads, hydrateRoot(container, <App />) attaches React to the existing DOM (no re-render).
4. From that point on, React owns the tree and re-renders normally on state changes.
Explanation: SSR exists to deliver visible HTML quickly; hydration exists to make that HTML interactive.// server.js (Node)
import { renderToString } from 'react-dom/server';
import App from './App';
export function ssrHandler(req, res) {
const appHtml = renderToString(<App />);
res.send(`<!doctype html>
<html><body>
<div id="root">${appHtml}</div>
<script src="/client.js"></script>
</body></html>`);
}
// client.js
import { hydrateRoot } from 'react-dom/client';
import App from './App';
hydrateRoot(document.getElementById('root'), <App />);
Explain how renderToPipeableStream (Node) and renderToReadableStream (Web) differ from renderToString. What problem does streaming solve, and how does <Suspense> interact with it?
Examples
Example 1:
Input:
import { renderToPipeableStream } from 'react-dom/server';
const { pipe } = renderToPipeableStream(<App />, { onShellReady() { pipe(res); } });
Output: the shell HTML flushes to the wire as soon as it is ready; suspended boundaries stream in later as their data resolves.
Explanation: streaming starts sending bytes before the full tree is rendered, so the browser can start parsing and rendering earlier.// Node streaming SSR
import { renderToPipeableStream } from 'react-dom/server';
import App from './App';
export function streamingHandler(req, res) {
const { pipe } = renderToPipeableStream(<App />, {
onShellReady() {
res.setHeader('Content-Type', 'text/html');
pipe(res); // start flushing the shell immediately
},
onShellError(err) {
res.statusCode = 500;
res.end('error');
},
});
}
Identify the cause of a hydration mismatch warning, and list three common triggers you should design SSR code around to prevent them.
Examples
Example 1:
Input:
function Now() { return <time>{new Date().toISOString()}</time>; }
Server renders at 12:00:00.001Z, client hydrates at 12:00:00.310Z.
Output: React logs "Hydration failed because the server rendered HTML did not match the client."
Explanation: the server and client produced different text for the same node because the value depends on Date.now().// hydration mismatch example - DO NOT do this
function Now() {
return <time>{new Date().toISOString()}</time>;
}
// Server renders one timestamp, client renders a different one on hydration.
