Make script preload optional
On my particular use case, using script preloading slows down Time To First Paint in the order of 2 seconds, which is quite a lot. I believe the culprit is the main CSS stylesheet download being delayed because of too many concurrent downloads.
Disable script preload, of course, makes Time to Interactive slower, but I would rather favor TTFP than the latter.
I think that most sites using SSR would benefit from improved TTFP.
Provide a flag on next.config.js which disables script preload on the default Head component.
This sounds like we need to adjust our implementation -- not disable it entirely. It should be boosting performance, never hurting it!
Is there any update to this? I am facing the same issue.
@likesjx Probably a good idea to read https://twitter.com/timneutkens/status/1154351050700333056
We hit this same issue. Our FP happens way too late because of preloading all the JS bundles.
We should be able to prioritize the painting of the server rendered content and only after that start booting up the SPA etc.
Seems like upgrading to 9.1.5 helps at least a bit: https://github.com/zeit/next.js/pull/9486
Yes, https://github.com/zeit/next.js/pull/9486 works around a browser bug where CSS would be deferred until all JavaScript has been downloaded. I'll mark this issue as closed unless anyone can produce new evidence that CSS is being deferred!
@lglossman
I also want to achieve the same thing, first I have done change in the lambda function to read generated HTML and remove rel="preload" from there.
later I have override Head tag from next/document in _document.js
_document.js
{isDev ? <Head /> : <HeadProduction />}
and here is HeadProduction.js
import { Head } from "next/document";
import React from "react";
import { getPageFiles } from "next/dist/next-server/server/get-page-files";
function getOptionalModernScriptVariant(path) {
if (process.env.__NEXT_MODERN_BUILD) {
return path.replace(/\.js$/, ".module.js");
}
return path;
}
function getDocumentFiles(buildManifest, pathname) {
const sharedFiles = getPageFiles(buildManifest, "/_app");
const pageFiles = pathname !== "/_error" ? getPageFiles(buildManifest, pathname) : [];
return {
sharedFiles,
pageFiles,
allFiles: [...new Set([...sharedFiles, ...pageFiles])],
};
}
class HeadProduction extends Head {
getPreloadMainLinks(files) {
const { assetPrefix, devOnlyCacheBusterQueryString } = this.context;
const preloadFiles = files.allFiles.filter((file) => file.endsWith(getOptionalModernScriptVariant(".js")));
return !preloadFiles.length
? null
: preloadFiles.map((file) => (
<link
key={file}
nonce={this.props.nonce}
// rel="preload" <------------------- comment preload
href={`${assetPrefix}/_next/${encodeURI(
file,
)}${devOnlyCacheBusterQueryString}`}
as="script"
crossOrigin={
this.props.crossOrigin || process.env.__NEXT_CROSS_ORIGIN
}
/>
));
}
render() {
const { head, headTags, styles } = this.context;
const { children } = this.props;
const files = getDocumentFiles(
this.context.buildManifest,
this.context.__NEXT_DATA__.page,
);
return (
<head {...this.props}>
{children}
{head}
{this.getCssLinks(files)}
{this.getPreloadDynamicChunks()}
{this.getPreloadMainLinks(files)}
{styles || null}
{React.createElement(React.Fragment, {}, ...(headTags || []))}
</head>
);
}
}
export default HeadProduction;
This works with the next version 9.5.2
Here is a screenshot of the before and after
Before

After

@Timer I think this would be an useful feature. Let me explain why ...
First of all, let's consider that assets download is triggered during the preparsing phase. Preparsing is blazingly fast and happens before building the DOM. It is a sort of "preload for free". More info here (https://andydavies.me/blog/2013/10/22/how-the-browser-pre-loader-makes-pages-load-faster/)
Then let's consider that in the worst case scenario (new tcp connection) the pages will be received in increasingly big chunks.
The first chunk being around 14KB (extra info here https://www.tunetheweb.com/blog/critical-resources-and-the-first-14kb/).
These chunks are a specific number of TCP packets, that increase on every roundtrip, and are part of the TCP congestion avoidance algorithm. Between them, the browser has to wait a time proportional to the latency between the browser and the server. If we use a CDN this might be very small.
Now, considering what I said so far, it is very likely that the preload tags will make a very small improvement in the vast majority of the cases. Saving only a few milliseconds.
On the other hand, preload tags screw up assets prioritisation. So for example if I have a big image to load above the fold, this will be delayed. Penalising the performance score.
Also I would like to use preload headers (rather than tags, https://www.w3.org/TR/preload/) to have the CDN to use HTTP2 push on the CSS only. This will be more effective in rendering the page faster.
Most helpful comment
@Timer I think this would be an useful feature. Let me explain why ...
First of all, let's consider that assets download is triggered during the preparsing phase. Preparsing is blazingly fast and happens before building the DOM. It is a sort of "preload for free". More info here (https://andydavies.me/blog/2013/10/22/how-the-browser-pre-loader-makes-pages-load-faster/)
Then let's consider that in the worst case scenario (new tcp connection) the pages will be received in increasingly big chunks.
The first chunk being around 14KB (extra info here https://www.tunetheweb.com/blog/critical-resources-and-the-first-14kb/).
These chunks are a specific number of TCP packets, that increase on every roundtrip, and are part of the TCP congestion avoidance algorithm. Between them, the browser has to wait a time proportional to the latency between the browser and the server. If we use a CDN this might be very small.
Now, considering what I said so far, it is very likely that the preload tags will make a very small improvement in the vast majority of the cases. Saving only a few milliseconds.
On the other hand, preload tags screw up assets prioritisation. So for example if I have a big image to load above the fold, this will be delayed. Penalising the performance score.
Also I would like to use preload headers (rather than tags, https://www.w3.org/TR/preload/) to have the CDN to use HTTP2 push on the CSS only. This will be more effective in rendering the page faster.