diff --git a/packages/react-router/src/link.tsx b/packages/react-router/src/link.tsx index f5d2b987652..2d4c2a60daf 100644 --- a/packages/react-router/src/link.tsx +++ b/packages/react-router/src/link.tsx @@ -120,13 +120,13 @@ export function useLinkProps< let external = false if (router.origin) { if (href.startsWith(router.origin)) { - href = href.replace(router.origin, '') || '/' + href = router.history.createHref(href.replace(router.origin, '')) || '/' } else { external = true } } return { href, external } - }, [disabled, next.maskedLocation, next.url, router.origin]) + }, [disabled, next.maskedLocation, next.url, router.origin, router.history]) const externalLink = React.useMemo(() => { if (hrefOption?.external) { diff --git a/packages/react-router/tests/link.test.tsx b/packages/react-router/tests/link.test.tsx index d0c1d79b119..e9ca9564a62 100644 --- a/packages/react-router/tests/link.test.tsx +++ b/packages/react-router/tests/link.test.tsx @@ -17,6 +17,7 @@ import { Outlet, RouterProvider, createBrowserHistory, + createHashHistory, createLink, createMemoryHistory, createRootRoute, @@ -6335,3 +6336,55 @@ describe('rewrite', () => { expect(appLink).toHaveAttribute('href', 'http://app.example.com/') }) }) + +describe('hash history with target="_blank" links', () => { + test('should generate correct href for target="_blank" links in hash history mode', async () => { + const hashHistory = createHashHistory() + + const rootRoute = createRootRoute() + const indexRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/', + component: () => { + return ( + <> +

Index

+ + Posts (same tab) + + + About (new tab) + + + ) + }, + }) + + const postsRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/posts', + component: () =>

Posts

, + }) + + const aboutRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/about', + component: () =>

About

, + }) + + const router = createRouter({ + routeTree: rootRoute.addChildren([indexRoute, postsRoute, aboutRoute]), + history: hashHistory, + }) + + render() + + const postsLink = await screen.findByTestId('posts-link') + expect(postsLink).toHaveAttribute('href', '/#/posts') + expect(postsLink).not.toHaveAttribute('target', '_blank') + + const postsBlankLink = await screen.findByTestId('about-blank-link') + expect(postsBlankLink).toHaveAttribute('href', '/#/about') + expect(postsBlankLink).toHaveAttribute('target', '_blank') + }) +}) diff --git a/packages/solid-router/src/link.tsx b/packages/solid-router/src/link.tsx index ca0b70b9381..407f106d36b 100644 --- a/packages/solid-router/src/link.tsx +++ b/packages/solid-router/src/link.tsx @@ -146,7 +146,7 @@ export function useLinkProps< let external = false if (router.origin) { if (href.startsWith(router.origin)) { - href = href.replace(router.origin, '') + href = router.history.createHref(href.replace(router.origin, '')) } else { external = true } diff --git a/packages/solid-router/tests/link.test.tsx b/packages/solid-router/tests/link.test.tsx index e06100edf3b..b80bcb9442d 100644 --- a/packages/solid-router/tests/link.test.tsx +++ b/packages/solid-router/tests/link.test.tsx @@ -15,6 +15,7 @@ import { Outlet, RouterProvider, createBrowserHistory, + createHashHistory, createLink, createMemoryHistory, createRootRoute, @@ -5674,3 +5675,55 @@ describe('relative links to from route', () => { }, ) }) + +describe('hash history with target="_blank" links', () => { + test('should generate correct href for target="_blank" links in hash history mode', async () => { + const hashHistory = createHashHistory() + + const rootRoute = createRootRoute() + const indexRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/', + component: () => { + return ( + <> +

Index

+ + Posts (same tab) + + + About (new tab) + + + ) + }, + }) + + const postsRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/posts', + component: () =>

Posts

, + }) + + const aboutRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/about', + component: () =>

About

, + }) + + const router = createRouter({ + routeTree: rootRoute.addChildren([indexRoute, postsRoute, aboutRoute]), + history: hashHistory, + }) + + render(() => ) + + const postsLink = await screen.findByTestId('posts-link') + expect(postsLink).toHaveAttribute('href', '/#/posts') + expect(postsLink).not.toHaveAttribute('target', '_blank') + + const postsBlankLink = await screen.findByTestId('about-blank-link') + expect(postsBlankLink).toHaveAttribute('href', '/#/about') + expect(postsBlankLink).toHaveAttribute('target', '_blank') + }) +})