diff --git a/packages/next/src/server/app-render/app-render.tsx b/packages/next/src/server/app-render/app-render.tsx index 75b0131d6af94..6f847b9f6d7d4 100644 --- a/packages/next/src/server/app-render/app-render.tsx +++ b/packages/next/src/server/app-render/app-render.tsx @@ -893,6 +893,7 @@ async function renderToHTMLOrFlightImpl( polyfills, renderServerInsertedHTML, hasPostponed, + basePath: renderOpts.basePath, }) const renderer = createStaticRenderer({ diff --git a/packages/next/src/server/app-render/make-get-server-inserted-html.tsx b/packages/next/src/server/app-render/make-get-server-inserted-html.tsx index 64b8a55cd4720..c57a3f8dd481e 100644 --- a/packages/next/src/server/app-render/make-get-server-inserted-html.tsx +++ b/packages/next/src/server/app-render/make-get-server-inserted-html.tsx @@ -8,15 +8,18 @@ import { import { renderToReadableStream } from 'react-dom/server.edge' import { streamToString } from '../stream-utils/node-web-streams-helper' import { RedirectStatusCode } from '../../client/components/redirect-status-code' +import { addPathPrefix } from '../../shared/lib/router/utils/add-path-prefix' export function makeGetServerInsertedHTML({ polyfills, renderServerInsertedHTML, + basePath, hasPostponed, }: { polyfills: JSX.IntrinsicElements['script'][] renderServerInsertedHTML: () => React.ReactNode hasPostponed: boolean + basePath: string }) { let flushedErrorMetaTagsUntilIndex = 0 // If the render had postponed, then we have already flushed the polyfills. @@ -38,7 +41,10 @@ export function makeGetServerInsertedHTML({ ) : null ) } else if (isRedirectError(error)) { - const redirectUrl = getURLFromRedirectError(error) + const redirectUrl = addPathPrefix( + getURLFromRedirectError(error), + basePath + ) const statusCode = getRedirectStatusCodeFromError(error) const isPermanent = statusCode === RedirectStatusCode.PermanentRedirect ? true : false diff --git a/test/e2e/app-dir/app-basepath/app/dynamic/[id]/loading.js b/test/e2e/app-dir/app-basepath/app/dynamic/[id]/loading.js new file mode 100644 index 0000000000000..d6e9dcd771ffb --- /dev/null +++ b/test/e2e/app-dir/app-basepath/app/dynamic/[id]/loading.js @@ -0,0 +1,4 @@ +// Have loading.js to trigger suspense boundary for /dynamic/[id] page +export default function Loading() { + return
loading
+} diff --git a/test/e2e/app-dir/app-basepath/app/dynamic/[id]/page.js b/test/e2e/app-dir/app-basepath/app/dynamic/[id]/page.js new file mode 100644 index 0000000000000..8d9491c2518e7 --- /dev/null +++ b/test/e2e/app-dir/app-basepath/app/dynamic/[id]/page.js @@ -0,0 +1,11 @@ +import { redirect } from 'next/navigation' + +export default async function Page({ params: { id } }) { + if (id === 'source') redirect('/dynamic/dest') + + return ( +
+

{`id:${id}`}

+
+ ) +} diff --git a/test/e2e/app-dir/app-basepath/index.test.ts b/test/e2e/app-dir/app-basepath/index.test.ts index 9e68462b777df..6f007b2a25a11 100644 --- a/test/e2e/app-dir/app-basepath/index.test.ts +++ b/test/e2e/app-dir/app-basepath/index.test.ts @@ -1,5 +1,5 @@ import { createNextDescribe } from 'e2e-utils' -import { check } from 'next-test-utils' +import { retry } from 'next-test-utils' createNextDescribe( 'app dir - basepath', @@ -42,10 +42,9 @@ createNextDescribe( it('should prefix redirect() with basePath', async () => { const browser = await next.browser('/base/redirect') - await check(async () => { + await retry(async () => { expect(await browser.url()).toBe(`${next.url}/base/another`) - return 'success' - }, 'success') + }) }) it('should render usePathname without the basePath', async () => { @@ -56,5 +55,13 @@ createNextDescribe( }) await Promise.all(validatorPromises) }) + + it('should handle redirect in dynamic in suspense boundary routes with basePath', async () => { + const browser = await next.browser('/base/dynamic/source') + await retry(async () => { + expect(await browser.url()).toBe(`${next.url}/base/dynamic/dest`) + expect(await browser.elementByCss('p').text()).toBe(`id:dest`) + }) + }) } )