diff --git a/packages/next/src/server/app-render/create-component-tree.tsx b/packages/next/src/server/app-render/create-component-tree.tsx
index acee5b460119a..8c5fb49f539ce 100644
--- a/packages/next/src/server/app-render/create-component-tree.tsx
+++ b/packages/next/src/server/app-render/create-component-tree.tsx
@@ -15,6 +15,7 @@ import { getTracer } from '../lib/trace/tracer'
import { NextNodeServerSpan } from '../lib/trace/constants'
import { StaticGenBailoutError } from '../../client/components/static-generation-bailout'
import type { LoadingModuleData } from '../../shared/lib/app-router-context.shared-runtime'
+import { DEFAULT_SEGMENT_KEY } from '../../shared/lib/segment'
type ComponentTree = {
seedData: CacheNodeSeedData
@@ -395,6 +396,10 @@ async function createComponentTreeInternal({
// This behavior predates PPR and is only relevant if the
// PPR flag is not enabled.
isPrefetch &&
+ // If we'd be rendering the default parallel route component, we include it in the tree.
+ // Otherwise, the prefetch would cause any parallel routes rendering the default state
+ // to be refetched on navigation, because the server will have cleared them.
+ parallelRoute[0] !== DEFAULT_SEGMENT_KEY &&
(Loading || !hasLoadingComponentInTree(parallelRoute)) &&
// The approach with PPR is different — loading.tsx behaves like a
// regular Suspense boundary and has no special behavior.
diff --git a/test/production/app-dir/parallel-routes-prefetching/app/[letter]/@grid/default.tsx b/test/production/app-dir/parallel-routes-prefetching/app/[letter]/@grid/default.tsx
new file mode 100644
index 0000000000000..86b9e9a388129
--- /dev/null
+++ b/test/production/app-dir/parallel-routes-prefetching/app/[letter]/@grid/default.tsx
@@ -0,0 +1,3 @@
+export default function Default() {
+ return null
+}
diff --git a/test/production/app-dir/parallel-routes-prefetching/app/[letter]/@grid/loading.tsx b/test/production/app-dir/parallel-routes-prefetching/app/[letter]/@grid/loading.tsx
new file mode 100644
index 0000000000000..15a2da4ac28e0
--- /dev/null
+++ b/test/production/app-dir/parallel-routes-prefetching/app/[letter]/@grid/loading.tsx
@@ -0,0 +1,3 @@
+export default function Loading() {
+ return
+ )
+}
diff --git a/test/production/app-dir/parallel-routes-prefetching/app/[letter]/loading.tsx b/test/production/app-dir/parallel-routes-prefetching/app/[letter]/loading.tsx
new file mode 100644
index 0000000000000..127f6a1255ee7
--- /dev/null
+++ b/test/production/app-dir/parallel-routes-prefetching/app/[letter]/loading.tsx
@@ -0,0 +1,3 @@
+export default function Loading() {
+ return
Loading
+}
diff --git a/test/production/app-dir/parallel-routes-prefetching/app/[letter]/page.tsx b/test/production/app-dir/parallel-routes-prefetching/app/[letter]/page.tsx
new file mode 100644
index 0000000000000..eee824bf3f46d
--- /dev/null
+++ b/test/production/app-dir/parallel-routes-prefetching/app/[letter]/page.tsx
@@ -0,0 +1,8 @@
+export default async function LetterPage({
+ params: { letter },
+}: {
+ params: { letter: string }
+}) {
+ await new Promise((r) => setTimeout(r, 250))
+ return
{letter}
+}
diff --git a/test/production/app-dir/parallel-routes-prefetching/app/layout.tsx b/test/production/app-dir/parallel-routes-prefetching/app/layout.tsx
new file mode 100644
index 0000000000000..e7077399c03ce
--- /dev/null
+++ b/test/production/app-dir/parallel-routes-prefetching/app/layout.tsx
@@ -0,0 +1,7 @@
+export default function Root({ children }: { children: React.ReactNode }) {
+ return (
+
+ {children}
+
+ )
+}
diff --git a/test/production/app-dir/parallel-routes-prefetching/app/page.tsx b/test/production/app-dir/parallel-routes-prefetching/app/page.tsx
new file mode 100644
index 0000000000000..aebfb306b399e
--- /dev/null
+++ b/test/production/app-dir/parallel-routes-prefetching/app/page.tsx
@@ -0,0 +1,5 @@
+import Link from 'next/link'
+
+export default function Page() {
+ return Go to a letter
+}
diff --git a/test/production/app-dir/parallel-routes-prefetching/next.config.js b/test/production/app-dir/parallel-routes-prefetching/next.config.js
new file mode 100644
index 0000000000000..807126e4cf0bf
--- /dev/null
+++ b/test/production/app-dir/parallel-routes-prefetching/next.config.js
@@ -0,0 +1,6 @@
+/**
+ * @type {import('next').NextConfig}
+ */
+const nextConfig = {}
+
+module.exports = nextConfig
diff --git a/test/production/app-dir/parallel-routes-prefetching/parallel-routes-prefetching.test.ts b/test/production/app-dir/parallel-routes-prefetching/parallel-routes-prefetching.test.ts
new file mode 100644
index 0000000000000..803034396da9e
--- /dev/null
+++ b/test/production/app-dir/parallel-routes-prefetching/parallel-routes-prefetching.test.ts
@@ -0,0 +1,58 @@
+import { nextTestSetup } from 'e2e-utils'
+import { retry } from 'next-test-utils'
+
+describe('parallel-routes-prefetching', () => {
+ const { next } = nextTestSetup({
+ files: __dirname,
+ })
+
+ it('should not re-fetch data for the __DEFAULT__ segments when navigating within a dynamic segment', async () => {
+ let prefetchRequests = []
+ let rscRequests = []
+ const browser = await next.browser('/a', {
+ beforePageLoad(page) {
+ page.on('request', (request) => {
+ return request.allHeaders().then((headers) => {
+ if (headers['Next-Router-Prefetch'.toLowerCase()] === '1') {
+ prefetchRequests.push(request.url())
+ } else if (headers['RSC'.toLowerCase()] === '1') {
+ rscRequests.push(request.url())
+ }
+ })
+ })
+ },
+ })
+
+ expect(await browser.elementById('letter-page').text()).toBe('a')
+ expect(await browser.elementById('letter-parallel').text()).toBe('a')
+
+ await retry(() => {
+ // we should see 25 prefetch requests for each letter of the alphabet (excluding the current)
+ expect(prefetchRequests).toHaveLength(25)
+ // and 0 RSC requests since we're not navigating to a different dynamic segment
+ expect(rscRequests).toHaveLength(0)
+ })
+
+ prefetchRequests = []
+
+ await browser.elementByCss('[href="/b"]').click()
+
+ await retry(() => {
+ // Navigating to a new dynamic segment should trigger any additional prefetch requests
+ expect(prefetchRequests).toHaveLength(0)
+
+ // but we except 2 RSC requests for the new page:
+ // - one for the `children` slot
+ // - one for the active parallel slot
+ // TODO: It would be nice to achieve this in a single request, but it currently isn't possible,
+ // because the router will see missing data for both slots simultaneously and kick off two separate requests.
+ // PPR doesn't have this issue because it doesn't perform the "lazy fetch" in layout-router.
+ expect(rscRequests).toHaveLength(
+ process.env.__NEXT_EXPERIMENTAL_PPR ? 1 : 2
+ )
+ })
+
+ expect(await browser.elementById('letter-page').text()).toBe('b')
+ expect(await browser.elementById('letter-parallel').text()).toBe('b')
+ })
+})