preact-render-to-string

repository·main·Indexed 20 days ago

https://github.com/preactjs/preact-render-to-string

A utility for rendering Preact components and JSX/VDOM into HTML strings. It supports universal/isomorphic rendering in Node.js and browser environments, featuring synchronous rendering via renderToString, asynchronous rendering for Suspense and lazy components via renderToStringAsync, and streaming capabilities through renderToReadableStream (Web Streams) and renderToPipeableStream (Node.js Streams). It also provides renderToStaticMarkup for static HTML and renderToStringPretty for human-readable output.

Tokens
6.8K
Snippets
27
Records
28
Agent score
71%

What's inside preact-render-to-string

  1. How Suspense markers are handled during rendering

    main

    When using renderToStringAsync, components that trigger suspense are wrapped with specific HTML comment markers to identify the boundaries. This allows client-side hydration or streaming tools to identify where suspended content resides.

    Markers:

    • Start: <!--$s-->
    • End: <!--/$s-->
  2. Render Preact via Express

    main

    You can integrate preact-render-to-string into an Express server to perform Server-Side Rendering (SSR). On each request, render the component to a string and wrap it in a standard HTML5 document template.

    import express from 'express';
    import { h } from 'preact';
    import { render } from 'preact-render-to-string';
    /** @jsx h */
    
    const Fox = ({ name }) => (
    	<div class="fox">
    		<h5>{name}</h5>
    		<p>This page is all about {name}.</p>
    	</div>
    );
    
    const app = express();
    app.listen(8080);
    
    app.get('/:fox', (req, res) => {
    	let html = render(<Fox name={req.params.fox} />);
    	res.send(`<!DOCTYPE html><html><body>${html}</body></html>`);
    });
  3. Render Preact Components to HTML

    main

    The render function supports both classical Component classes and pure functional components. It will recursively render the component tree into a single HTML string.

    import { render } from 'preact-render-to-string';
    import { h, Component } from 'preact';
    /** @jsx h */
    
    class Fox extends Component {
    	render({ name }) {
    		return <span class="fox">{name}</span>;
    	}
    }
    
    const Box = ({ type, children }) => (
    	<div class={`box box-${type}`}>{children}</div>
    );
    
    let html = render(
    	<Box type="open">
    		<Fox name="Finn" />
    	</Box>
    );
    
    console.log(html);
    // <div class="box box-open"><span class="fox">Finn</span></div>
  4. Render `Suspense` and `lazy` components with `renderToStringAsync`

    main

    To support asynchronous components like those created with lazy and wrapped in Suspense from preact/compat, use renderToStringAsync instead of the synchronous render. This function returns a Promise that resolves once all suspended content has been loaded and rendered.

    import { Suspense, lazy } from 'preact/compat';
    import { renderToStringAsync } from 'preact-render-to-string';
    
    // Creation of the lazy component
    const HomePage = lazy(() => import('./pages/home'));
    
    const Main = () => {
    	return (
    		<Suspense fallback={<p>Loading</p>}>
    			<HomePage />
    		</Suspense>
    	);
    };
    
    const main = async () => {
    	const html = await renderToStringAsync(<Main />);
    	console.log(html);
    };
    
    main().catch((error) => {
    	console.error(error);
    });
  5. Stream HTML using `renderToPipeableStream` (Node.js Streams)

    main

    renderToPipeableStream Options

    OptionDescription
    onShellReady()Called synchronously once the initial shell has been rendered and streaming is about to start. Pipe here for fastest TTFB.
    onAllReady()Called after all <Suspense> boundaries have resolved and the stream is complete.
    onError(error)Called for render errors inside suspended subtrees.

    Note: Calling abort() stops the render and destroys the stream; any pending suspended subtrees are dropped.

    import { createServer } from 'node:http';
    import { renderToPipeableStream } from 'preact-render-to-string/stream-node';
    import { Suspense, lazy } from 'preact/compat';
    
    const Profile = lazy(() => import('./Profile'));
    
    const App = () => (
    	<html>
    		<head><title>My App</title></head>
    		<body>
    			<Suspense fallback={<p>Loading profile…</p>}>
    				<Profile />
    			</Suspense>
    		</body>
    	</html>
    );
    
    createServer((req, res) => {
    	res.setHeader('Content-Type', 'text/html');
    
    	const { pipe, abort } = renderToPipeableStream(<App />, {
    		onShellReady() {
    			// Called once the synchronous shell is ready to stream.
    			pipe(res);
    		},
    		onAllReady() {
    			// Called once every suspended subtree has been flushed.
    		},
    		onError(error) {
    			console.error(error);
    			res.statusCode = 500;
    		}
    	});
    	
    	// Optional: abort the render after a timeout
    	setTimeout(abort, 10_000);
    }).listen(8080);
  6. Render JSX/VDOM to HTML with `render`

    main

    Use the render function to convert Preact Virtual DOM nodes or JSX directly into an HTML string. This works in both Node.js and the browser environments.

    import { render } from 'preact-render-to-string';
    import { h } from 'preact';
    /** @jsx h */
    
    let vdom = <div class="foo">content</div>;
    
    let html = render(vdom);
    console.log(html);
    // <div class="foo">content</div>
  7. Stream HTML using `renderToReadableStream` (Web Streams)

    main

    For environments supporting Web Streams (like Deno, Bun, or Cloudflare Workers), use renderToReadableStream from preact-render-to-string/stream. This allows you to flush <Suspense> fallbacks immediately and replace them with content as it resolves, improving Time to First Byte (TTFB).

    The returned ReadableStream includes an allReady property, which is a Promise<void> that resolves once all suspended subtrees have been flushed.

    import { renderToReadableStream } from 'preact-render-to-string/stream';
    import { Suspense, lazy } from 'preact/compat';
    
    const Profile = lazy(() => import('./Profile'));
    
    const App = () => (
    	<html>
    		<head><title>My App</title></head>
    		<body>
    			<Suspense fallback={<p>Loading profile…</p>}>
    				<Profile />
    			</Suspense>
    		</body>
    	</html>
    );
    
    // Example usage in a Web Stream environment
    export default {
    	fetch() {
    		const stream = renderToReadableStream(<App />);
    		// stream.allReady resolves once all suspended content has been flushed
    		return new Response(stream, {
    			headers: { 'Content-Type': 'text/html' }
    		});
    	}
    };
  8. Enable Error Boundaries in Rendering

    main

    By default, rendering errors might not be caught by Preact's error boundary lifecycle methods (getDerivedStateFromErrors or componentDidCatch) during string rendering. To enable this capability, set errorBoundaries to true in the Preact options object.

    import { options } from 'preact';
    
    // Enable error boundaries in `preact-render-to-string`
    options.errorBoundaries = true;
  9. Render Preact components to HTML asynchronously with renderToStringAsync

    main

    Use renderToStringAsync when your component tree uses Suspense or returns Promises (e.g., for data fetching). This function awaits all nested Promises (up to a maximum depth of 25) to ensure the resulting HTML is fully resolved.

    Parameters:

    • vnode: The JSX Element / VNode to render.
    • context (optional): An initial root context object.

    Returns: A Promise that resolves to the serialized HTML string.

    import { renderToStringAsync } from 'preact-render-to-string';
    
    const html = await renderToStringAsync(<Suspense fallback={<Loading />}>
      <AsyncComponent />
    </Suspense>);
  10. Render VNodes to a pretty HTML string with renderToStringPretty

    main

    Use renderToStringPretty to convert a Preact VNode into an HTML string. This function is the default export of the module. It supports several configuration options to control the output format, such as XML compatibility, indentation (pretty printing), and shallow rendering.

    import renderToStringPretty from 'preact-render-to-string';
    import { h } from 'preact';
    
    const vnode = h('div', { id: 'app' }, 'Hello World');
    const html = renderToStringPretty(vnode, undefined, { pretty: 2 });
  11. Use renderToStringAsync for asynchronous rendering

    main

    Use renderToStringAsync when your component tree contains asynchronous elements, such as Suspense boundaries or components that fetch data during the render phase. It returns a Promise<string> if the rendering process is asynchronous, or a string if it can be completed synchronously.

    import { renderToStringAsync } from 'preact-render-to-string';
    
    // Returns a Promise that resolves to the rendered HTML string
    const html = await renderToStringAsync(<Suspense fallback={<div>Loading...</div>}><MyAsyncComponent /></Suspense>);
  12. Perform shallow rendering with `shallowRender`

    main

    The shallowRender function is a convenience alias for render(vnode, context, { shallow: true }). It renders the top-level elements but leaves nested Components inline as strings (e.g., <ComponentName ... />) instead of traversing into them and rendering their children.

    This is ideal for inspecting the structure of a component tree without the noise of deeply nested child components.

    Usage

    import { shallowRender } from 'preact-render-to-string';
    
    const vnode = <App user="Alice" />;
    
    // Instead of rendering the full App tree, it returns something like:
    // <App user="Alice" />
    const output = shallowRender(vnode);
    import { shallowRender } from 'preact-render-to-string';
    
    const vnode = <App user="Alice" />;
    
    // Instead of rendering the full App tree, it returns something like:
    // <App user="Alice" />
    const output = shallowRender(vnode);