Security News
The Unpaid Backbone of Open Source: Solo Maintainers Face Increasing Security Demands
Solo open source maintainers face burnout and security challenges, with 60% unpaid and 60% considering quitting.
react-async-ssr
Advanced tools
React v16.6.0 introduced Suspense for lazy-loading components, but it doesn't work yet on the server-side.
This package enables server-side rendering.
It provides async versions of .renderToString()
and .renderToStaticMarkup()
methods. The async methods support Suspense
and allow async loading of components or data.
npm install react-async-ssr
Also requires React >= 16.6.0.
Before:
const ReactDOMServer = require('react-dom/server');
function render() {
const html = ReactDOMServer.renderToString(<App />);
return html;
}
After:
const ReactDOMServer = require('react-async-ssr');
async function render() {
const html = await ReactDOMServer.renderToStringAsync(<App />);
return html;
}
function App() {
return (
<div>
<Suspense fallback={<Spinner />}>
<LazyComponent />
<LazyComponent />
<LazyData />
</Suspense>
</div>
);
}
const html = await ReactDOM.renderToStringAsync(<App />);
<Suspense>
behaves exactly the same on the server as it does on the client.
.renderToStringAsync()
will render the app in the usual way, except any lazy elements will be awaited and rendered before the returned promise resolves.
So I can just use React.lazy()
, right?
No! React.lazy()
doesn't make sense to use on the server side. It doesn't have any ability to track the modules that have been lazy-loaded, so there's no way to then reload them on the client side, so that .hydrate()
has all the code it needs.
@loadable/component provides a .lazy()
method which is equivalent to React.lazy()
but suitable for server-side rendering. This guide explains how to use it for server-side rendering.
.renderToStringAsync()
supports any component which fits within React's convention for suspendable components.
In it's render()
method, the component should throw a Promise which will resolve when the data is loaded. When the promise resolves, the renderer will re-render the component and add it into the markup.
let data = null, promise;
function LazyData() {
if (data) return <div>{data.foo}</div>;
if (!promise) {
promise = new Promise(resolve => {
setTimeout(() => {
data = {foo: 'bar'};
resolve();
}, 1000);
});
}
throw promise;
}
function App() {
return (
<div>
<Suspense fallback={<div>Loading...</div>}>
<LazyData />
</Suspense>
</div>
);
}
An example using the experimental package react-cache:
const {createResource} = require('react-cache');
const PokemonResource = createResource(
id =>
fetch(`https://pokeapi.co/api/v2/pokemon/${id}/`)
.then(res => res.json())
);
function Pokemon(props) {
const data = PokemonResource.read(props.id);
return <div>My name is {data.name}</div>;
}
function App() {
return (
<div>
<Suspense fallback={<div>Loading...</div>}>
<Pokemon id={1} />
<Pokemon id={2} />
<Pokemon id={3} />
</Suspense>
</div>
);
}
const html = await ReactDOM.renderToStringAsync(<App />);
The above example makes 3 async fetch requests, which are made in parallel. They are awaited, and the HTML markup rendered only once all the data is ready.
.renderToStringAsync()
supports:
The classic model for SSR is:
ReactDOM.hydrate()
With async SSR, the server-side process is different:
The advantages of this change are:
In the example above, the <Pokemon>
component is completely independent. You can drop it in to any app, anywhere in the component tree, and it'll be able to load the data it needs, without any complex "wiring up".
However, some mechanism is required to gather the data loaded on the server in order to send it to the client for hydration.
There are many solutions, for example using a Redux store, or a Context Provider at the root of the app. This package does not make any assumptions about how the user wants to handle this, and no doubt solutions will emerge from the community. All that this package requires is that components follow React's convention that components wishing to do async loading throw promises.
Sometimes you might want to prevent a component rendering on server side. For example, it might be a low-priority part of the page, "below the fold", or a heavy component which will take a long time to load on client side and increase the delay before hydration.
This module provides a mechanism for that.
The component should throw a promise which has [NO_SSR]
property set to true
.
[NO_SSR]
is a symbol which can be imported from react-async-ssr/symbols
.
If the promise has this property, the component will not be rendered and the next Suspense boundary above's fallback will be triggered.
const {NO_SSR} = require('react-async-ssr/symbols');
function LazyNoSSR() {
const promise = new Promise(() => {});
promise[NO_SSR] = true;
throw promise;
}
function App() {
return (
<React.Suspense fallback={<div>Loading...</div>}>
<LazyNoSSR/>
</React.Suspense>
);
}
When rendered on server, this will output <div>Loading...</div>
.
On client side, to ensure no hydration mismatch errors, the component must throw a promise which then resolves to the required component/data, and not render the output synchronously.
It's possible for a lazy component to begin loading, but then its result not to be required, because an enclosing Suspense boundary's fallback gets triggered. If so the result will not be displayed.
In these cases, if the promise has an [ABORT]
method, it will be called.
[ABORT]
is a symbol which can be imported from react-async-ssr/symbols
.
const {ABORT} = require('react-async-ssr/symbols');
function AbortableLazy() {
const promise = new Promise(
resolve => /* do some stuff */
);
promise[ABORT] = () => {
/* this will be called if result of promise will not be rendered */
};
throw promise;
}
Stream rendering (.renderToNodeStream()
) is not yet supported by this package.
Many other solutions achieve something like this by "double-rendering" the app.
In the first render pass, all the promises for async-loaded data are collected. Once all the promises resolve, a 2nd render pass produces the actual HTML markup which is sent to the client. Obviously, this is resource-intensive. And if async components themselves make further async requests, 3rd or 4th or more render passes can be required.
The .renderToStringAsync()
method provided by this package renders in a single pass. The render is interrupted when awaiting an async resource and resumed once it has loaded.
Use npm test
to run the tests. Use npm run cover
to check coverage.
See changelog.md
If you discover a bug, please raise an issue on Github. https://github.com/overlookmotel/react-async-ssr/issues
Pull requests are very welcome. Please:
0.4.0
Breaking changes:
.abort
to symbol [ABORT]
Features:
NO_SSR
promise signal to prevent server renderingOther:
FAQs
Render React Suspense on server
The npm package react-async-ssr receives a total of 208 weekly downloads. As such, react-async-ssr popularity was classified as not popular.
We found that react-async-ssr demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
Solo open source maintainers face burnout and security challenges, with 60% unpaid and 60% considering quitting.
Security News
License exceptions modify the terms of open source licenses, impacting how software can be used, modified, and distributed. Developers should be aware of the legal implications of these exceptions.
Security News
A developer is accusing Tencent of violating the GPL by modifying a Python utility and changing its license to BSD, highlighting the importance of copyleft compliance.