Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: expose basePath in useRouter #74715

Open
wants to merge 1 commit into
base: canary
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/01-app/04-api-reference/04-functions/use-router.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -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**:
>
Expand Down
2 changes: 1 addition & 1 deletion docs/02-pages/03-api-reference/03-functions/use-router.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -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).
Expand Down
1 change: 1 addition & 0 deletions packages/next/src/client/app-index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,7 @@ function ServerRoot(): React.ReactNode {
actionQueue={actionQueue}
globalErrorComponentAndStyles={initialRSCPayload.G}
assetPrefix={initialRSCPayload.p}
basePath={initialRSCPayload.a}
/>
)

Expand Down
13 changes: 11 additions & 2 deletions packages/next/src/client/components/app-router.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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()

Expand All @@ -663,7 +668,11 @@ export default function AppRouter({
errorComponent={globalErrorComponent}
errorStyles={globalErrorStyles}
>
<Router actionQueue={actionQueue} assetPrefix={assetPrefix} />
<Router
actionQueue={actionQueue}
assetPrefix={assetPrefix}
basePath={basePath}
/>
</ErrorBoundary>
)
}
Expand Down
7 changes: 7 additions & 0 deletions packages/next/src/server/app-render/app-render.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,7 @@ export type AppRenderContext = {
pagePath: string
clientReferenceManifest: DeepReadonly<ClientReferenceManifest>
assetPrefix: string
basePath: string
isNotFoundPath: boolean
nonce: string | undefined
res: BaseNextResponse
Expand Down Expand Up @@ -845,6 +846,7 @@ async function getRSCPayload(
P: <Preloads preloadCallbacks={preloadCallbacks} />,
b: ctx.sharedContext.buildId,
p: ctx.assetPrefix,
a: ctx.basePath,
c: prepareInitialCanonicalUrl(url),
i: !!couldBeIntercepted,
f: [
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -1033,6 +1036,7 @@ function App<T>({
actionQueue={actionQueue}
globalErrorComponentAndStyles={response.G}
assetPrefix={response.p}
basePath={response.a}
/>
</ServerInsertedHTMLProvider>
</HeadManagerContext.Provider>
Expand Down Expand Up @@ -1081,6 +1085,7 @@ function AppWithoutContext<T>({
actionQueue={actionQueue}
globalErrorComponentAndStyles={response.G}
assetPrefix={response.p}
basePath={response.a}
/>
)
}
Expand Down Expand Up @@ -1124,6 +1129,7 @@ async function renderToHTMLOrFlightImpl(
nextFontManifest,
serverActions,
assetPrefix = '',
basePath = '',
enableTainting,
} = renderOpts

Expand Down Expand Up @@ -1273,6 +1279,7 @@ async function renderToHTMLOrFlightImpl(
nonce,
res,
sharedContext,
basePath,
}

getTracer().setRootSpanAttribute('next.route', pagePath)
Expand Down
2 changes: 2 additions & 0 deletions packages/next/src/server/app-render/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,8 @@ export type InitialRSCPayload = {
b: string
/** assetPrefix */
p: string
/** basePath */
a: string
/** initialCanonicalUrlParts */
c: string[]
/** couldBeIntercepted */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<AppRouterInstance | null>(
Expand Down
6 changes: 6 additions & 0 deletions packages/next/src/shared/lib/router/adapters.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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')
})
})
1 change: 1 addition & 0 deletions packages/next/src/shared/lib/router/adapters.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ export function adaptForAppRouterInstance(
prefetch(href) {
void pagesRouter.prefetch(href)
},
basePath: pagesRouter.basePath,
}
}

Expand Down
6 changes: 6 additions & 0 deletions test/e2e/app-dir/app-basepath/app/page.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
'use client'

import Link from 'next/link'
import { useRouter } from 'next/navigation'

export default function Page() {
const router = useRouter()

return (
<div>
<h1>Test Page</h1>
<p id="base-path">{router.basePath}</p>
<Link href="/another">Go to page 2</Link>
</div>
)
Expand Down
5 changes: 5 additions & 0 deletions test/e2e/app-dir/app-basepath/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ describe('app dir - basepath', () => {
expect(html).toContain('<h1>Test Page</h1>')
})

it('useRouter should correctly return `basePath`', async () => {
const html = await next.render('/base')
expect(html).toContain('<p id="base-path">/base</p>')
})

it('should support Link with basePath prefixed', async () => {
const browser = await next.browser('/base')
expect(
Expand Down
Loading