diff --git a/packages/next/pages/_document.tsx b/packages/next/pages/_document.tsx
index 11dcf48406db..badc0f7a6b85 100644
--- a/packages/next/pages/_document.tsx
+++ b/packages/next/pages/_document.tsx
@@ -1,9 +1,13 @@
import React, { Component, ReactElement, ReactNode, useContext } from 'react'
-import { OPTIMIZED_FONT_PROVIDERS } from '../shared/lib/constants'
+import {
+ OPTIMIZED_FONT_PROVIDERS,
+ NEXT_BUILTIN_DOCUMENT,
+} from '../shared/lib/constants'
import type {
DocumentContext,
DocumentInitialProps,
DocumentProps,
+ DocumentType,
} from '../shared/lib/utils'
import { BuildManifest, getPageFiles } from '../server/get-page-files'
import { cleanAmpPath } from '../server/utils'
@@ -309,7 +313,7 @@ export default class Document
extends Component {
// Add a special property to the built-in `Document` component so later we can
// identify if a user customized `Document` is used or not.
-;(Document as any).__next_internal_document =
+const InternalFunctionDocument: DocumentType =
function InternalFunctionDocument() {
return (
@@ -321,6 +325,7 @@ export default class Document extends Component {
)
}
+;(Document as any)[NEXT_BUILTIN_DOCUMENT] = InternalFunctionDocument
export function Html(
props: React.DetailedHTMLProps<
diff --git a/packages/next/server/base-server.ts b/packages/next/server/base-server.ts
index 5f45738fc956..f84d608e4c57 100644
--- a/packages/next/server/base-server.ts
+++ b/packages/next/server/base-server.ts
@@ -27,6 +27,7 @@ import { parse as parseQs } from 'querystring'
import { format as formatUrl, parse as parseUrl } from 'url'
import { getRedirectStatus } from '../lib/load-custom-routes'
import {
+ NEXT_BUILTIN_DOCUMENT,
SERVERLESS_DIRECTORY,
SERVER_DIRECTORY,
STATIC_STATUS_PAGES,
@@ -1221,7 +1222,7 @@ export default abstract class Server {
// When concurrent features is enabled, the built-in `Document`
// component also supports dynamic HTML.
(this.renderOpts.reactRoot &&
- !!(components.Document as any)?.__next_internal_document)
+ NEXT_BUILTIN_DOCUMENT in components.Document)
// Disable dynamic HTML in cases that we know it won't be generated,
// so that we can continue generating a cache key when possible.
diff --git a/packages/next/server/render.tsx b/packages/next/server/render.tsx
index cd3af5d6d4fd..f38083b00d1b 100644
--- a/packages/next/server/render.tsx
+++ b/packages/next/server/render.tsx
@@ -6,6 +6,7 @@ import type { DomainLocale } from './config'
import type {
AppType,
DocumentInitialProps,
+ DocumentType,
DocumentProps,
DocumentContext,
NextComponentType,
@@ -35,6 +36,7 @@ import {
UNSTABLE_REVALIDATE_RENAME_ERROR,
} from '../lib/constants'
import {
+ NEXT_BUILTIN_DOCUMENT,
SERVER_PROPS_ID,
STATIC_PROPS_ID,
STATIC_STATUS_PAGES,
@@ -769,6 +771,7 @@ export async function renderToHTML(
const { html, head } = await docCtx.renderPage({ enhanceApp })
const styles = jsxStyleRegistry.styles({ nonce: options.nonce })
+ jsxStyleRegistry.flush()
return { html, head, styles }
},
}
@@ -1311,14 +1314,14 @@ export async function renderToHTML(
// 1. Using `Document.getInitialProps` in the Edge runtime.
// 2. Using the class component `Document` with concurrent features.
- const builtinDocument = (Document as any).__next_internal_document as
- | typeof Document
- | undefined
+ const BuiltinFunctionalDocument: DocumentType | undefined = (
+ Document as any
+ )[NEXT_BUILTIN_DOCUMENT]
if (process.env.NEXT_RUNTIME === 'edge' && Document.getInitialProps) {
// In the Edge runtime, `Document.getInitialProps` isn't supported.
// We throw an error here if it's customized.
- if (!builtinDocument) {
+ if (!BuiltinFunctionalDocument) {
throw new Error(
'`getInitialProps` in Document component is not supported with the Edge Runtime.'
)
@@ -1329,8 +1332,8 @@ export async function renderToHTML(
(isServerComponent || process.env.NEXT_RUNTIME === 'edge') &&
Document.getInitialProps
) {
- if (builtinDocument) {
- Document = builtinDocument
+ if (BuiltinFunctionalDocument) {
+ Document = BuiltinFunctionalDocument
} else {
throw new Error(
'`getInitialProps` in Document component is not supported with React Server Components.'
@@ -1433,6 +1436,7 @@ export async function renderToHTML(
}
if (!process.env.__NEXT_REACT_ROOT) {
+ // Enabling react legacy rendering mode: __NEXT_REACT_ROOT = false
if (Document.getInitialProps) {
const documentInitialPropsRes = await documentInitialProps()
if (documentInitialPropsRes === null) return null
@@ -1468,6 +1472,7 @@ export async function renderToHTML(
}
}
} else {
+ // Enabling react concurrent rendering mode: __NEXT_REACT_ROOT = true
let renderStream: ReadableStream & {
allReady?: Promise | undefined
}
@@ -1534,13 +1539,11 @@ export async function renderToHTML(
// be streamed.
// Do not use `await` here.
generateStaticFlightDataIfNeeded()
-
return await continueFromInitialStream({
renderStream,
suffix,
dataStream: serverComponentsInlinedTransformStream?.readable,
- generateStaticHTML:
- generateStaticHTML || !process.env.__NEXT_REACT_ROOT,
+ generateStaticHTML,
flushEffectHandler,
})
}
@@ -1572,8 +1575,8 @@ export async function renderToHTML(
return
}
- let styles
+ let styles
if (hasDocumentGetInitialProps) {
styles = docProps.styles
} else {
diff --git a/packages/next/shared/lib/constants.ts b/packages/next/shared/lib/constants.ts
index bedca6446bb5..7f3dc9acd928 100644
--- a/packages/next/shared/lib/constants.ts
+++ b/packages/next/shared/lib/constants.ts
@@ -25,6 +25,7 @@ export const CLIENT_PUBLIC_FILES_PATH = 'public'
export const CLIENT_STATIC_FILES_PATH = 'static'
export const CLIENT_STATIC_FILES_RUNTIME = 'runtime'
export const STRING_LITERAL_DROP_BUNDLE = '__NEXT_DROP_CLIENT_FILE__'
+export const NEXT_BUILTIN_DOCUMENT = '__NEXT_BUILTIN_DOCUMENT__'
// server/middleware-flight-manifest.js
export const MIDDLEWARE_FLIGHT_MANIFEST = 'middleware-flight-manifest'
diff --git a/test/e2e/styled-jsx/app/pages/index.js b/test/e2e/styled-jsx/app/pages/index.js
new file mode 100644
index 000000000000..31c938b25e4f
--- /dev/null
+++ b/test/e2e/styled-jsx/app/pages/index.js
@@ -0,0 +1,8 @@
+export default function Page() {
+ return (
+
+ )
+}
diff --git a/test/e2e/styled-jsx/index.test.ts b/test/e2e/styled-jsx/index.test.ts
index 414570939fe6..c0f52f658ff9 100644
--- a/test/e2e/styled-jsx/index.test.ts
+++ b/test/e2e/styled-jsx/index.test.ts
@@ -1,8 +1,11 @@
-import { createNext } from 'e2e-utils'
+import path from 'path'
+import { createNext, FileRef } from 'e2e-utils'
import { NextInstance } from 'test/lib/next-modes/base'
import { renderViaHTTP } from 'next-test-utils'
import cheerio from 'cheerio'
+const appDir = path.join(__dirname, 'app')
+
function runTest(packageManager?: string) {
describe(`styled-jsx${packageManager ? ' ' + packageManager : ''}`, () => {
let next: NextInstance
@@ -10,16 +13,7 @@ function runTest(packageManager?: string) {
beforeAll(async () => {
next = await createNext({
files: {
- 'pages/index.js': `
- export default function Page() {
- return (
-
- )
- }
- `,
+ pages: new FileRef(path.join(appDir, 'pages')),
},
...(packageManager
? {
@@ -33,7 +27,7 @@ function runTest(packageManager?: string) {
it('should contain styled-jsx styles in html', async () => {
const html = await renderViaHTTP(next.url, '/')
const $ = cheerio.load(html)
- expect($('head').text()).toContain('color:red')
+ expect($('html').text()).toMatch(/color:(\s)*red/)
})
})
}
diff --git a/test/integration/react-18/test/concurrent.js b/test/integration/react-18/test/concurrent.js
index 92871dbab3b8..f849a0a80c94 100644
--- a/test/integration/react-18/test/concurrent.js
+++ b/test/integration/react-18/test/concurrent.js
@@ -32,7 +32,14 @@ export default (context, _render) => {
})
})
- it('flushes styles as the page renders', async () => {
+ it('flushes styled-jsx styles as the page renders', async () => {
+ const html = await renderViaHTTP(
+ context.appPort,
+ '/use-flush-effect/styled-jsx'
+ )
+ const stylesOccurrence = html.match(/color:(\s)*blue/g) || []
+ expect(stylesOccurrence.length).toBe(1)
+
await withBrowser('/use-flush-effect/styled-jsx', async (browser) => {
await check(
() => browser.waitForElementByCss('#__jsx-900f996af369fc74').text(),