diff --git a/packages/next/client/components/head.tsx b/packages/next/client/components/head.tsx
new file mode 100644
index 000000000000..0b123102d6c7
--- /dev/null
+++ b/packages/next/client/components/head.tsx
@@ -0,0 +1,10 @@
+import React from 'react'
+
+export function DefaultHead() {
+ return (
+ <>
+
+
+ >
+ )
+}
diff --git a/packages/next/server/app-render.tsx b/packages/next/server/app-render.tsx
index be0bf6eb63d8..ef6ed8766ca7 100644
--- a/packages/next/server/app-render.tsx
+++ b/packages/next/server/app-render.tsx
@@ -46,6 +46,7 @@ import {
FLIGHT_PARAMETERS,
} from '../client/components/app-router-headers'
import type { StaticGenerationStore } from '../client/components/static-generation-async-storage'
+import { DefaultHead } from '../client/components/head'
const isEdgeRuntime = process.env.NEXT_RUNTIME === 'edge'
@@ -1011,7 +1012,8 @@ export async function renderToHTMLOrFlight(
async function resolveHead(
[segment, parallelRoutes, { head }]: LoaderTree,
- parentParams: { [key: string]: any }
+ parentParams: { [key: string]: any },
+ isRootHead: boolean
): Promise {
// Handle dynamic segment params.
const segmentParam = getDynamicParamFromSegment(segment)
@@ -1029,7 +1031,7 @@ export async function renderToHTMLOrFlight(
parentParams
for (const key in parallelRoutes) {
const childTree = parallelRoutes[key]
- const returnedHead = await resolveHead(childTree, currentParams)
+ const returnedHead = await resolveHead(childTree, currentParams, false)
if (returnedHead) {
return returnedHead
}
@@ -1038,6 +1040,8 @@ export async function renderToHTMLOrFlight(
if (head) {
const Head = await interopDefault(await head[0]())
return
+ } else if (isRootHead) {
+ return
}
return null
@@ -1451,7 +1455,6 @@ export async function renderToHTMLOrFlight(
))
: null}
- {/* {HeadTags ? : null} */}
>
)
},
@@ -1585,7 +1588,7 @@ export async function renderToHTMLOrFlight(
return [actualSegment]
}
- const rscPayloadHead = await resolveHead(loaderTree, {})
+ const rscPayloadHead = await resolveHead(loaderTree, {}, true)
// Flight data that is going to be passed to the browser.
// Currently a single item array but in the future multiple patches might be combined in a single request.
const flightData: FlightData = [
@@ -1656,7 +1659,7 @@ export async function renderToHTMLOrFlight(
}
: {}
- const initialHead = await resolveHead(loaderTree, {})
+ const initialHead = await resolveHead(loaderTree, {}, true)
/**
* A new React Component that renders the provided React Component
diff --git a/test/e2e/app-dir/app/pages/index.js b/test/e2e/app-dir/app/pages/index.js
index 9f0223e8b3d7..ac03305fde3c 100644
--- a/test/e2e/app-dir/app/pages/index.js
+++ b/test/e2e/app-dir/app/pages/index.js
@@ -3,11 +3,10 @@ import Link from 'next/link'
import useSWR from 'swr'
import styles from '../styles/shared.module.css'
-export default function Page(props) {
+export default function Page() {
const { data } = useSWR('swr-index', (v) => v, { fallbackData: 'swr-index' })
return (
<>
- rc:{React.Component ? 'c' : 'no'}
hello from pages/index
Dashboard
{data}
diff --git a/test/e2e/app-dir/head.test.ts b/test/e2e/app-dir/head.test.ts
index b9739f5938f8..47fb252a4ea6 100644
--- a/test/e2e/app-dir/head.test.ts
+++ b/test/e2e/app-dir/head.test.ts
@@ -39,6 +39,10 @@ describe('app dir head', () => {
const $ = cheerio.load(html)
const headTags = $('head').children().toArray()
+ // should not include default tags in page with head.js provided
+ expect(html).not.toContain(
+ ''
+ )
expect(headTags.find((el) => el.attribs.src === '/hello.js')).toBeTruthy()
expect(
headTags.find((el) => el.attribs.src === '/another.js')
diff --git a/test/e2e/app-dir/rsc-basic.test.ts b/test/e2e/app-dir/rsc-basic.test.ts
index 713806074c16..d7fb47439f6c 100644
--- a/test/e2e/app-dir/rsc-basic.test.ts
+++ b/test/e2e/app-dir/rsc-basic.test.ts
@@ -112,6 +112,10 @@ describe('app dir - rsc basics', () => {
// should have only 1 DOCTYPE
expect(homeHTML).toMatch(/^