Skip to content

Repository files navigation

preact-render-to-string

NPMBuild status

Render JSX and Preact components to an HTML string.

Works in Node & the browser, making it useful for universal/isomorphic rendering.

>> Cute Fox-Related Demo(@ CodePen) <<


Render JSX/VDOM to HTML

import{render}from'preact-render-to-string';import{h}from'preact';/** @jsx h */letvdom=<divclass="foo">content</div>;lethtml=render(vdom);console.log(html);// <div class="foo">content</div>

Render Preact Components to HTML

import{render}from'preact-render-to-string';import{h,Component}from'preact';/** @jsx h */// Classical components workclassFoxextendsComponent{render({ name }){return<spanclass="fox">{name}</span>;}}// ... and so do pure functional components:constBox=({ type, children })=>(<divclass={`box box-${type}`}>{children}</div>);lethtml=render(<Boxtype="open"><Foxname="Finn"/></Box>);console.log(html);// <div class="box box-open"><span class="fox">Finn</span></div>

Render JSX / Preact / Whatever via Express!

importexpressfrom'express';import{h}from'preact';import{render}from'preact-render-to-string';/** @jsx h */// silly example component:constFox=({ name })=>(<divclass="fox"><h5>{name}</h5><p>This page is all about {name}.</p></div>);// basic HTTP server via express:constapp=express();app.listen(8080);// on each request, render and return a component:app.get('/:fox',(req,res)=>{lethtml=render(<Foxname={req.params.fox}/>);// send it back wrapped up as an HTML5 document:res.send(`<!DOCTYPE html><html><body>${html}</body></html>`);});

Error Boundaries

Rendering errors can be caught by Preact via getDerivedStateFromErrors or componentDidCatch. To enable that feature in preact-render-to-string set errorBoundaries = true

import{options}from'preact';// Enable error boundaries in `preact-render-to-string`options.errorBoundaries=true;

Suspense & lazy components with preact/compat

npm install preact preact-render-to-string
exportdefault()=>{return<h1>Home page</h1>;};
import{Suspense,lazy}from'preact/compat';// Creation of the lazy componentconstHomePage=lazy(()=>import('./pages/home'));constMain=()=>{return(<Suspensefallback={<p>Loading</p>}><HomePage/></Suspense>);};
import{renderToStringAsync}from'preact-render-to-string';import{Main}from'./main';constmain=async()=>{// Rendering of lazy componentsconsthtml=awaitrenderToStringAsync(<Main/>);console.log(html);// <h1>Home page</h1>};// Execution & error handlingmain().catch((error)=>{console.error(error);});

Streaming

Note

This is an early version of our streaming implementation.

Preact supports streaming HTML to the client incrementally, flushing <Suspense> fallbacks immediately and replacing them with the resolved content as data arrives. This reduces Time to First Byte and allows the browser to start parsing earlier.

renderToReadableStream — Web Streams

import{renderToReadableStream}from'preact-render-to-string/stream';import{Suspense,lazy}from'preact/compat';constProfile=lazy(()=>import('./Profile'));constApp=()=>(<html><head><title>My App</title></head><body><Suspensefallback={<p>Loading profile…</p>}><Profile/></Suspense></body></html>);// Works in any Web Streams environment (Deno, Bun, Cloudflare Workers, …)exportdefault{fetch(){conststream=renderToReadableStream(<App/>);// stream.allReady resolves once all suspended content has been flushedreturnnewResponse(stream,{headers: {'Content-Type': 'text/html'}});}};

The returned ReadableStream has an extra allReady: Promise<void> property that resolves once every suspended subtree has been written. Await it before sending the response if you need the complete document before anything is flushed (e.g. for static export). At which point you might be better off using renderToStringAsync though.

conststream=renderToReadableStream(<App/>);awaitstream.allReady;// wait for full render

renderToPipeableStream — Node.js Streams

import{createServer}from'node:http';import{renderToPipeableStream}from'preact-render-to-string/stream-node';import{Suspense,lazy}from'preact/compat';constProfile=lazy(()=>import('./Profile'));constApp=()=>(<html><head><title>My App</title></head><body><Suspensefallback={<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 timeoutsetTimeout(abort,10_000);}).listen(8080);
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.

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


License

MIT

About

📄 Universal rendering for Preact: render JSX and Preact components to HTML.

Topics

Resources

Code of conduct

Stars

724 stars

Watchers

7 watching

Forks

Releases

Sponsor this project

Packages

Used by

Contributors

Languages