diff --git a/packages/next/next-server/lib/utils.ts b/packages/next/next-server/lib/utils.ts
index d1c5c43a86aa200..8769f20ecc38271 100644
--- a/packages/next/next-server/lib/utils.ts
+++ b/packages/next/next-server/lib/utils.ts
@@ -187,6 +187,7 @@ export type DocumentProps = DocumentInitialProps & {
canonicalBase: string
headTags: any[]
unstable_runtimeJS?: false
+ unstable_JsPreload?: false
devOnlyCacheBusterQueryString: string
scriptLoader: { defer?: string[]; eager?: any[] }
locale?: string
diff --git a/packages/next/next-server/server/render.tsx b/packages/next/next-server/server/render.tsx
index 0b3d1defd97bfa6..5cda0722c6553ce 100644
--- a/packages/next/next-server/server/render.tsx
+++ b/packages/next/next-server/server/render.tsx
@@ -170,6 +170,7 @@ export type RenderOptsPartial = {
previewProps: __ApiPreviewProps
basePath: string
unstable_runtimeJS?: false
+ unstable_JsPreload?: false
optimizeFonts: boolean
fontManifest?: FontManifest
optimizeImages: boolean
@@ -218,6 +219,7 @@ function renderDocument(
gip,
appGip,
unstable_runtimeJS,
+ unstable_JsPreload,
devOnlyCacheBusterQueryString,
scriptLoader,
locale,
@@ -288,6 +290,7 @@ function renderDocument(
assetPrefix,
headTags,
unstable_runtimeJS,
+ unstable_JsPreload,
devOnlyCacheBusterQueryString,
scriptLoader,
locale,
@@ -1019,6 +1022,7 @@ export async function renderToHTML(
process.env.NODE_ENV === 'production'
? pageConfig.unstable_runtimeJS
: undefined,
+ unstable_JsPreload: pageConfig.unstable_JsPreload,
dangerousAsPath: router.asPath,
ampState,
props,
diff --git a/packages/next/pages/_document.tsx b/packages/next/pages/_document.tsx
index 923651e172910a5..5f5bf42ebe720af 100644
--- a/packages/next/pages/_document.tsx
+++ b/packages/next/pages/_document.tsx
@@ -342,8 +342,10 @@ export class Head extends Component<
dangerousAsPath,
headTags,
unstable_runtimeJS,
+ unstable_JsPreload,
} = this.context
const disableRuntimeJS = unstable_runtimeJS === false
+ const disableJsPreload = unstable_JsPreload === false
this.context.docComponentsRendered.Head = true
@@ -566,8 +568,12 @@ export class Head extends Component<
{!process.env.__NEXT_OPTIMIZE_CSS && (
)}
- {!disableRuntimeJS && this.getPreloadDynamicChunks()}
- {!disableRuntimeJS && this.getPreloadMainLinks(files)}
+ {!disableRuntimeJS &&
+ !disableJsPreload &&
+ this.getPreloadDynamicChunks()}
+ {!disableRuntimeJS &&
+ !disableJsPreload &&
+ this.getPreloadMainLinks(files)}
{process.env.__NEXT_OPTIMIZE_CSS && this.getCssLinks(files)}
{process.env.__NEXT_OPTIMIZE_CSS && (
diff --git a/packages/next/types/index.d.ts b/packages/next/types/index.d.ts
index 5e3fddbd1a3c6c1..3a18b705f9a330e 100644
--- a/packages/next/types/index.d.ts
+++ b/packages/next/types/index.d.ts
@@ -74,6 +74,7 @@ export type PageConfig = {
}
env?: Array
unstable_runtimeJS?: false
+ unstable_JsPreload?: false
}
export {
diff --git a/test/integration/disable-js-preload/next.config.js b/test/integration/disable-js-preload/next.config.js
new file mode 100644
index 000000000000000..cc17cf48c578fd5
--- /dev/null
+++ b/test/integration/disable-js-preload/next.config.js
@@ -0,0 +1,6 @@
+module.exports = {
+ onDemandEntries: {
+ // Make sure entries are not getting disposed.
+ maxInactiveAge: 1000 * 60 * 60,
+ },
+}
diff --git a/test/integration/disable-js-preload/pages/index.js b/test/integration/disable-js-preload/pages/index.js
new file mode 100644
index 000000000000000..da71dc064b50057
--- /dev/null
+++ b/test/integration/disable-js-preload/pages/index.js
@@ -0,0 +1,5 @@
+export const config = {
+ unstable_JsPreload: false,
+}
+
+export default () => Hello World!
diff --git a/test/integration/disable-js-preload/test/index.test.js b/test/integration/disable-js-preload/test/index.test.js
new file mode 100644
index 000000000000000..11c8fcacbfddda1
--- /dev/null
+++ b/test/integration/disable-js-preload/test/index.test.js
@@ -0,0 +1,73 @@
+/* eslint-env jest */
+
+import { join } from 'path'
+import cheerio from 'cheerio'
+import {
+ nextServer,
+ nextBuild,
+ startApp,
+ stopApp,
+ renderViaHTTP,
+ findPort,
+ launchApp,
+ killApp,
+} from 'next-test-utils'
+
+const appDir = join(__dirname, '../')
+let appPort
+let server
+let app
+jest.setTimeout(1000 * 60 * 5)
+
+const context = {}
+
+describe('disabled JS preloads', () => {
+ describe('production mode', () => {
+ beforeAll(async () => {
+ await nextBuild(appDir)
+ app = nextServer({
+ dir: join(__dirname, '../'),
+ dev: false,
+ quiet: true,
+ })
+
+ server = await startApp(app)
+ context.appPort = appPort = server.address().port
+ })
+ afterAll(() => stopApp(server))
+
+ it('should render the page', async () => {
+ const html = await renderViaHTTP(appPort, '/')
+ expect(html).toMatch(/Hello World/)
+ })
+
+ it('should not have JS preload links', async () => {
+ const html = await renderViaHTTP(appPort, '/')
+ const $ = cheerio.load(html)
+ expect($('link[rel=preload]').length).toBe(0)
+ })
+ })
+
+ describe('dev mode', () => {
+ let appPort
+ let app
+
+ beforeAll(async () => {
+ appPort = await findPort()
+ app = await launchApp(join(__dirname, '../'), appPort)
+ })
+
+ afterAll(() => killApp(app))
+
+ it('should render the page', async () => {
+ const html = await renderViaHTTP(appPort, '/')
+ expect(html).toMatch(/Hello World/)
+ })
+
+ it('should not have JS preload links', async () => {
+ const html = await renderViaHTTP(appPort, '/')
+ const $ = cheerio.load(html)
+ expect($('link[rel=preload]').length).toBe(0)
+ })
+ })
+})