Research
Security News
Malicious npm Packages Inject SSH Backdoors via Typosquatted Libraries
Socket’s threat research team has detected six malicious npm packages typosquatting popular libraries to insert SSH backdoors.
react-lazy-ssr
Advanced tools
react-lazy-ssr
is a drop-in substitute for React.lazy
.
Unlike React's built-in .lazy()
method, it can be used in server-side rendering with react-async-ssr.
Requires React 16.8.x or 16.9.x. React 16.10.0+ is not supported at present.
Use react-lazy-ssr
exactly as you would React.lazy
:
import React, {Suspense} from 'react';
import lazy from 'react-lazy-ssr';
const LazyFoo = lazy(
() => import('./Foo.jsx')
);
const App = () => (
<Suspense fallback="Loading...">
<LazyFoo />
</Suspense>
);
There are 5 steps to making server-side rendering work.
There is an example of a complete repo here. It's less complicated than it sounds!
Use react-async-ssr's .renderToStringAsync()
method to render on server, rather than react-dom's .renderToString()
(see here for instructions).
Each lazy component must have a unique name, which can be provided with the chunkName
option.
const LazyFoo = lazy(
() => import('./Foo.jsx'),
{ chunkName: 'Foo' }
);
NB Name must be unique for the component being loaded, NOT unique for each call to lazy()
. If different files both want to lazy load the same component, they can (and should) use the same chunk name.
This package provides a Babel plugin to add chunk names automatically for you.
Add react-lazy-ssr/babel
plugin to your Babel config:
// .babelrc.js
const LazyPlugin = require('react-lazy-ssr/babel');
module.exports = {
presets: [
'@babel/preset-env',
'@babel/preset-react'
],
plugins: [
[ LazyPlugin, { rootPath: __dirname} ]
]
};
rootPath
option is optional, but it ensures no naming clashes where import()
is given relative paths.
The Babel plugin will transform this:
lazy( () => import( './Foo.jsx' ) );
to this:
lazy(
() => import( /* webpackChunkName: "Foo" */ './Foo.jsx' ),
{ chunkName: 'Foo' }
);
Add this package's Webpack plugin to your Webpack config (for the client-side build only):
// webpack.config.js
const ReactLazySsrPlugin = require('react-lazy-ssr/webpack');
module.exports = {
target: 'web',
entry: `./src/client/main.jsx`,
plugins: [
new ReactLazySsrPlugin()
],
...
}
The purpose of the Webpack plugin is to output a JSON stats file which maps module names to the filenames of files Webpack outputs. This stats file is used by the next step.
After running Webpack, you'll find a file reactLazySsrStats.json
in the build folder.
In order for the page to hydrate correctly on the client side, all the code which is lazy-loaded on the server must also be sent to the client to load prior to hydration.
This package provides ChunkExtractor
to do this.
ChunkExtractor
.chunkExtractor.getScriptTags()
to get all the <script>
tags to add at the bottom of the HTML body.// Import packages
const {renderToStringAsync} = require('react-async-ssr'),
{ChunkExtractor} = require('react-lazy-ssr/server');
// Import App
const App = require('./build/server/main.js');
// Import stats file created by Webpack plugin
const stats = require('./build/client/reactLazySsrStats.json');
// Define route
expressApp.get( '/', async (req, res) => {
// Wrap app in a ChunkExtractor
const chunkExtractor = new ChunkExtractor( { stats } );
const app = chunkExtractor.collectChunks( <App /> );
// Async render
const html = await renderToStringAsync( app );
// Get scripts
const scriptsHtml = chunkExtractor.getScriptTags();
// Return response
res.send(`
<html>
<head><title>Example</title></head>
<body>
<div id="app">${ html }</div>
${ scriptsHtml }
</body>
</html>
`);
} );
On the client side, all lazy components must be preloaded before hydrating the app. This ensures the app is "primed" and renders exactly the same on client as it did on the server.
Instead of:
ReactDOM.hydrate(
<App />,
document.getElementById('app')
);
use:
import lazy from 'react-lazy-ssr';
lazy.preloadAll().then( () => {
ReactDOM.hydrate(
<App />,
document.getElementById('app')
);
} );
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 and increase the delay before hydration.
To prevent a component rendering on server-side, use the noSsr
option.
const LazyFoo = lazy(
() => import('./Foo.jsx'),
{ noSsr: true }
);
The no-SSR component will trigger the nearest Suspense
fallback on the server side, and it's that fallback HTML which will be sent to the client. Then the component will be loaded on the client-side after hydration.
To preload a lazy component before it's rendered, call .preload()
.
.preload()
returns a promise which resolves when the component is loaded, or rejects if loading fails.
const LazyFoo = lazy( () => import('./Foo.jsx') );
LazyFoo.preload().then(
() => console.log('Loaded'),
(err) => console.log('Error:', err)
);
By default, ChunkExtractor
expects the app entry point chunk to be called main
. You can change this with the entryPoint
option.
const chunkExtractor = new ChunkExtractor( {
stats,
entryPoint: 'entry'
} );
Or, if the app has multiple entry points, provide an array:
const chunkExtractor = new ChunkExtractor( {
stats,
entryPoint: [ 'entry1', 'entry2' ]
} );
async
and defer
By default, chunkExtractor.getScriptTags()
will produce <script>
tags suitable for putting at the bottom of the HTML body.
It produces something like:
<script>
window.__REACT_LAZY_SSR_CHUNKS_REQUIRED__ = ["LazyLoaded"];
</script>
<script src="/static/LazyLoaded.js"></script>
<script src="/static/vendors~main.js"></script>
<script src="/static/main.js"></script>
You may be able to improve loading performance by putting the scripts in the HTML head and using async
or defer
attributes.
Pass either {async: true}
or {defer: true}
to .getScriptTags()
:
chunkExtractor.getScriptTags( { async: true } )
This adds an async
attribute to the <script>
elements and some Javascript to ensure the page is not hydrated until all have loaded.
<script>
window.__REACT_LAZY_SSR_CHUNKS_REQUIRED__ = ["LazyLoaded"];
window.__REACT_LAZY_SSR_FILES_REQUIRED__ =
["LazyLoaded.js","vendors~main.js","main.js"];
</script>
<script async src="/static/LazyLoaded.js" onload="..."></script>
<script async src="/static/vendors~main.js" onload="..."></script>
<script async src="/static/main.js" onload="..."></script>
You can also use <link rel="preload">
tags at the top of the page to preload Javascript while the browser is still parsing the HTML body.
Use .getPreloadTags()
and put the returned HTML in the document head, in addition to script tags at bottom of the HTML body.
.getPreloadTags()
returns:
<link rel="preload" href="/static/LazyLoaded.js" as="script">
<link rel="preload" href="/static/vendors~main.js" as="script">
<link rel="preload" href="/static/main.js" as="script">
Certain file extensions will be stripped off when creating chunk names e.g. Foo.jsx
=> Foo
.
By default, the file extensions which are stripped are .js
, .jsx
, .mjs
and .cjs
.
You can customize this with the exts
option.
// .babelrc.js
module.exports = {
...
plugins: [
[
'react-lazy-ssr/babel',
{
rootPath: __dirname,
exts: ['js', 'jsx', 'react']
}
]
]
};
If you want the stats file to be called something other than reactLazySsrStats.json
, you can use the filename
option.
// webpack.config.js
module.exports = {
plugins: [
new ReactLazySsrPlugin( {
filename: 'my-alternative-filename.json'
} )
],
...
}
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-lazy-ssr/issues
Pull requests are very welcome. Please:
FAQs
React.lazy substitute which works with server-side rendering
We found that react-lazy-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.
Research
Security News
Socket’s threat research team has detected six malicious npm packages typosquatting popular libraries to insert SSH backdoors.
Security News
MITRE's 2024 CWE Top 25 highlights critical software vulnerabilities like XSS, SQL Injection, and CSRF, reflecting shifts due to a refined ranking methodology.
Security News
In this segment of the Risky Business podcast, Feross Aboukhadijeh and Patrick Gray discuss the challenges of tracking malware discovered in open source softare.