Server-Side Rendering
Server-Side Rendering (often abbreviated as "SSR") allows you to render your application to an HTML string that can be sent to the client to improve load time. Outside of that there are other scenarios, like testing, where SSR proves really useful.
Installation
The server-side renderer for Preact lives in its own repository and can be installed via your packager of choice:
npm install -S preact-render-to-string
After the command above finished, we can start using it right away.
HTML Strings
Both of the following options return a single HTML string that represents the full rendered output of your Preact application.
renderToString
The most basic and straightforward rendering method, renderToString
transforms a Preact tree into a string of HTML synchronously.
import { renderToString } from 'preact-render-to-string';
const name = 'Preact User!'
const App = <div class="foo">Hello {name}</div>;
const html = renderToString(App);
console.log(html);
// <div class="foo">Hello Preact User!</div>
renderToStringAsync
Awaits the resolution of promises before returning the complete HTML string. This is particularly useful when utilizing suspense for lazy-loaded components or data fetching.
// app.js
import { Suspense, lazy } from 'preact/compat';
const HomePage = lazy(() => import('./pages/home.js'));
function App() {
return (
<Suspense fallback={<p>Loading</p>}>
<HomePage />
</Suspense>
);
};
import { renderToStringAsync } from 'preact-render-to-string';
import { App } from './app.js';
const html = await renderToStringAsync(<App />);
console.log(html);
// <h1>Home page</h1>
Note: Unfortunately there's a handful of known limitations in Preact v10's implementation of "resumed hydration" — that is, hydration that can pause and wait for JS chunks or data to be downloaded & available before continuing. This has been solved in the upcoming Preact v11 release.
For now, you'll want to avoid async boundaries that return 0 or more than 1 DOM node as children, such as in the following examples:
function X() { // Some lazy operation, such as initializing analytics return null; }; const LazyOperation = lazy(() => /* import X */);
function Y() { // `<Fragment>` disappears upon rendering, leaving two `<p>` DOM elements return ( <Fragment> <p>Foo</p> <p>Bar</p> </Fragment> ); }; const SuspendingMultipleChildren = lazy(() => /* import Y */);
For a more comprehensive write up of the known problems and how we have addressed them, please see Hydration 2.0 (preactjs/preact#4442)
HTML Streams
Streaming is a method of rendering that allows you to send parts of your Preact application to the client as they are ready rather than waiting for the entire render to complete.
renderToPipeableStream
renderToPipeableStream
is a streaming method that utilizes Node.js Streams to render your application. If you are not using Node, you should look to renderToReadableStream instead.
import { renderToPipeableStream } from 'preact-render-to-string/stream-node';
// Request handler syntax and form will vary across frameworks
function handler(req, res) {
const { pipe, abort } = renderToPipeableStream(<App />, {
onShellReady() {
res.statusCode = 200;
res.setHeader('Content-Type', 'text/html');
pipe(res);
},
onError(error) {
res.statusCode = 500;
res.send(
`<!doctype html><p>An error ocurred:</p><pre>${error.message}</pre>`
);
},
});
// Abandon and switch to client rendering if enough time passes.
setTimeout(abort, 2000);
}
renderToReadableStream
renderToReadableStream
is another streaming method and similar to renderToPipeableStream
, but designed for use in environments that support standardized Web Streams instead.
import { renderToReadableStream } from 'preact-render-to-string/stream';
// Request handler syntax and form will vary across frameworks
function handler(req, res) {
const stream = renderToReadableStream(<App />);
return new Response(stream, {
headers: {
'Content-Type': 'text/html',
},
});
}
Customize Renderer Output
We offer a number of options through the /jsx
module to customize the output of the renderer for a handful of popular use cases.
JSX Mode
The JSX rendering mode is especially useful if you're doing any kind of snapshot testing. It renders the output as if it was written in JSX.
import renderToString from 'preact-render-to-string/jsx';
const App = <div data-foo={true} />;
const html = renderToString(App, {}, { jsx: true });
console.log(html);
// <div data-foo={true} />
Pretty Mode
If you need to get the rendered output in a more human friendly way, we've got you covered! By passing the pretty
option, we'll preserve whitespace and indent the output as expected.
import renderToString from 'preact-render-to-string/jsx';
const Foo = () => <div>foo</div>;
const App = <div class="foo"><Foo /></div>;
const html = renderToString(App, {}, { pretty: true });
console.log(html);
// <div class="foo">
// <div>foo</div>
// </div>
Shallow Mode
For some purposes it's often preferable to not render the whole tree, but only one level. For that we have a shallow renderer which will print child components by name, instead of their return value.
import renderToString from 'preact-render-to-string/jsx';
const Foo = () => <div>foo</div>;
const App = <div class="foo"><Foo /></div>;
const html = renderToString(App, {}, { shallow: true });
console.log(html);
// <div class="foo"><Foo /></div>
XML Mode
For elements without children, XML mode will instead render them as self-closing tags.
import renderToString from 'preact-render-to-string/jsx';
const Foo = () => <div></div>;
const App = <div class="foo"><Foo /></div>;
let html = renderToString(App, {}, { xml: true });
console.log(html);
// <div class="foo"><div /></div>
html = renderToString(App, {}, { xml: false });
console.log(html);
// <div class="foo"><div></div></div>