diff --git a/docs/01-app/04-api-reference/04-functions/use-router.mdx b/docs/01-app/04-api-reference/04-functions/use-router.mdx
index f4a167eca8339..0e8b36960e88c 100644
--- a/docs/01-app/04-api-reference/04-functions/use-router.mdx
+++ b/docs/01-app/04-api-reference/04-functions/use-router.mdx
@@ -47,6 +47,7 @@ export default function Page() {
- `router.prefetch(href: string)`: [Prefetch](/docs/app/building-your-application/routing/linking-and-navigating#2-prefetching) the provided route for faster client-side transitions.
- `router.back()`: Navigate back to the previous route in the browser’s history stack.
- `router.forward()`: Navigate forwards to the next page in the browser’s history stack.
+- `router.basePath`: The active [basePath](/docs/app/api-reference/config/next-config-js/basePath) (if enabled).
> **Good to know**:
>
diff --git a/docs/02-pages/03-api-reference/03-functions/use-router.mdx b/docs/02-pages/03-api-reference/03-functions/use-router.mdx
index ed2c9d2664a8b..f4a4e9ff51772 100644
--- a/docs/02-pages/03-api-reference/03-functions/use-router.mdx
+++ b/docs/02-pages/03-api-reference/03-functions/use-router.mdx
@@ -40,7 +40,7 @@ The following is the definition of the `router` object returned by both [`useRou
- `query`: `Object` - The query string parsed to an object, including [dynamic route](/docs/pages/building-your-application/routing/dynamic-routes) parameters. It will be an empty object during prerendering if the page doesn't use [Server-side Rendering](/docs/pages/building-your-application/data-fetching/get-server-side-props). Defaults to `{}`
- `asPath`: `String` - The path as shown in the browser including the search params and respecting the `trailingSlash` configuration. `basePath` and `locale` are not included.
- `isFallback`: `boolean` - Whether the current page is in [fallback mode](/docs/pages/api-reference/functions/get-static-paths#fallback-true).
-- `basePath`: `String` - The active [basePath](/docs/app/api-reference/config/next-config-js/basePath) (if enabled).
+- `basePath`: `String` - The active [basePath](/docs/pages/api-reference/config/next-config-js/basePath) (if enabled).
- `locale`: `String` - The active locale (if enabled).
- `locales`: `String[]` - All supported locales (if enabled).
- `defaultLocale`: `String` - The current default locale (if enabled).
diff --git a/packages/next/src/client/app-index.tsx b/packages/next/src/client/app-index.tsx
index 1bf4acf3a4d78..5e3a1d9dca212 100644
--- a/packages/next/src/client/app-index.tsx
+++ b/packages/next/src/client/app-index.tsx
@@ -190,6 +190,7 @@ function ServerRoot(): React.ReactNode {
actionQueue={actionQueue}
globalErrorComponentAndStyles={initialRSCPayload.G}
assetPrefix={initialRSCPayload.p}
+ basePath={initialRSCPayload.a}
/>
)
diff --git a/packages/next/src/client/components/app-router.tsx b/packages/next/src/client/components/app-router.tsx
index 32e407f65842b..0107885ccb4fc 100644
--- a/packages/next/src/client/components/app-router.tsx
+++ b/packages/next/src/client/components/app-router.tsx
@@ -242,9 +242,11 @@ function Head({
function Router({
actionQueue,
assetPrefix,
+ basePath,
}: {
actionQueue: AppRouterActionQueue
assetPrefix: string
+ basePath: string
}) {
const [state, dispatch] = useReducer(actionQueue)
const { canonicalUrl } = useUnwrapState(state)
@@ -330,10 +332,11 @@ function Router({
})
}
},
+ basePath,
}
return routerInstance
- }, [actionQueue, dispatch, navigate])
+ }, [actionQueue, dispatch, navigate, basePath])
useEffect(() => {
// Exists for debugging purposes. Don't use in application code.
@@ -651,10 +654,12 @@ export default function AppRouter({
actionQueue,
globalErrorComponentAndStyles: [globalErrorComponent, globalErrorStyles],
assetPrefix,
+ basePath,
}: {
actionQueue: AppRouterActionQueue
globalErrorComponentAndStyles: [ErrorComponent, React.ReactNode | undefined]
assetPrefix: string
+ basePath: string
}) {
useNavFailureHandler()
@@ -663,7 +668,11 @@ export default function AppRouter({
errorComponent={globalErrorComponent}
errorStyles={globalErrorStyles}
>
-
+
)
}
diff --git a/packages/next/src/server/app-render/app-render.tsx b/packages/next/src/server/app-render/app-render.tsx
index eb16ab27416a9..83f6474744857 100644
--- a/packages/next/src/server/app-render/app-render.tsx
+++ b/packages/next/src/server/app-render/app-render.tsx
@@ -218,6 +218,7 @@ export type AppRenderContext = {
pagePath: string
clientReferenceManifest: DeepReadonly
assetPrefix: string
+ basePath: string
isNotFoundPath: boolean
nonce: string | undefined
res: BaseNextResponse
@@ -845,6 +846,7 @@ async function getRSCPayload(
P: ,
b: ctx.sharedContext.buildId,
p: ctx.assetPrefix,
+ a: ctx.basePath,
c: prepareInitialCanonicalUrl(url),
i: !!couldBeIntercepted,
f: [
@@ -964,6 +966,7 @@ async function getErrorRSCPayload(
return {
b: ctx.sharedContext.buildId,
p: ctx.assetPrefix,
+ a: ctx.basePath,
c: prepareInitialCanonicalUrl(url),
m: undefined,
i: false,
@@ -1033,6 +1036,7 @@ function App({
actionQueue={actionQueue}
globalErrorComponentAndStyles={response.G}
assetPrefix={response.p}
+ basePath={response.a}
/>
@@ -1081,6 +1085,7 @@ function AppWithoutContext({
actionQueue={actionQueue}
globalErrorComponentAndStyles={response.G}
assetPrefix={response.p}
+ basePath={response.a}
/>
)
}
@@ -1124,6 +1129,7 @@ async function renderToHTMLOrFlightImpl(
nextFontManifest,
serverActions,
assetPrefix = '',
+ basePath = '',
enableTainting,
} = renderOpts
@@ -1273,6 +1279,7 @@ async function renderToHTMLOrFlightImpl(
nonce,
res,
sharedContext,
+ basePath,
}
getTracer().setRootSpanAttribute('next.route', pagePath)
diff --git a/packages/next/src/server/app-render/types.ts b/packages/next/src/server/app-render/types.ts
index 8046de212cd7b..c95b894f7c5ab 100644
--- a/packages/next/src/server/app-render/types.ts
+++ b/packages/next/src/server/app-render/types.ts
@@ -258,6 +258,8 @@ export type InitialRSCPayload = {
b: string
/** assetPrefix */
p: string
+ /** basePath */
+ a: string
/** initialCanonicalUrlParts */
c: string[]
/** couldBeIntercepted */
diff --git a/packages/next/src/shared/lib/app-router-context.shared-runtime.ts b/packages/next/src/shared/lib/app-router-context.shared-runtime.ts
index ff273f669bbc4..9f738df383dc1 100644
--- a/packages/next/src/shared/lib/app-router-context.shared-runtime.ts
+++ b/packages/next/src/shared/lib/app-router-context.shared-runtime.ts
@@ -148,6 +148,10 @@ export interface AppRouterInstance {
* Prefetch the provided href.
*/
prefetch(href: string, options?: PrefetchOptions): void
+ /**
+ * The configured app's basePath.
+ */
+ basePath: string
}
export const AppRouterContext = React.createContext(
diff --git a/packages/next/src/shared/lib/router/adapters.test.tsx b/packages/next/src/shared/lib/router/adapters.test.tsx
index 1120d008dcc1d..5d2314cf9c083 100644
--- a/packages/next/src/shared/lib/router/adapters.test.tsx
+++ b/packages/next/src/shared/lib/router/adapters.test.tsx
@@ -11,6 +11,7 @@ describe('adaptForAppRouterInstance', () => {
push: jest.fn(),
replace: jest.fn(),
prefetch: jest.fn(),
+ basePath: '/base-path',
} as unknown as NextRouter
const adapter = adaptForAppRouterInstance(router)
@@ -62,4 +63,9 @@ describe('adaptForAppRouterInstance', () => {
adapter.prefetch('/foo')
expect(router.prefetch).toHaveBeenCalledWith('/foo')
})
+
+ it('should forward the basePath to `basePath`', () => {
+ const basePath = adapter.basePath
+ expect(basePath).toEqual('/base-path')
+ })
})
diff --git a/packages/next/src/shared/lib/router/adapters.tsx b/packages/next/src/shared/lib/router/adapters.tsx
index cc3e34b3dfd04..4fc1dbb056a17 100644
--- a/packages/next/src/shared/lib/router/adapters.tsx
+++ b/packages/next/src/shared/lib/router/adapters.tsx
@@ -32,6 +32,7 @@ export function adaptForAppRouterInstance(
prefetch(href) {
void pagesRouter.prefetch(href)
},
+ basePath: pagesRouter.basePath,
}
}
diff --git a/test/e2e/app-dir/app-basepath/app/page.js b/test/e2e/app-dir/app-basepath/app/page.js
index 852f704e6c0e4..e031f077aaf99 100644
--- a/test/e2e/app-dir/app-basepath/app/page.js
+++ b/test/e2e/app-dir/app-basepath/app/page.js
@@ -1,9 +1,15 @@
+'use client'
+
import Link from 'next/link'
+import { useRouter } from 'next/navigation'
export default function Page() {
+ const router = useRouter()
+
return (
Test Page
+
{router.basePath}
Go to page 2
)
diff --git a/test/e2e/app-dir/app-basepath/index.test.ts b/test/e2e/app-dir/app-basepath/index.test.ts
index f7161723f8254..3fbbede56db96 100644
--- a/test/e2e/app-dir/app-basepath/index.test.ts
+++ b/test/e2e/app-dir/app-basepath/index.test.ts
@@ -21,6 +21,11 @@ describe('app dir - basepath', () => {
expect(html).toContain('Test Page
')
})
+ it('useRouter should correctly return `basePath`', async () => {
+ const html = await next.render('/base')
+ expect(html).toContain('/base
')
+ })
+
it('should support Link with basePath prefixed', async () => {
const browser = await next.browser('/base')
expect(