diff --git a/.changeset/big-olives-doubt.md b/.changeset/big-olives-doubt.md
new file mode 100644
index 0000000000..006cb6ce52
--- /dev/null
+++ b/.changeset/big-olives-doubt.md
@@ -0,0 +1,5 @@
+---
+"react-router-dom": patch
+---
+
+Treat absolute/same-origin/different-basename values as external
diff --git a/package.json b/package.json
index 446186dc2d..7ededba1ec 100644
--- a/package.json
+++ b/package.json
@@ -114,7 +114,7 @@
"none": "15 kB"
},
"packages/react-router-dom/dist/react-router-dom.production.min.js": {
- "none": "11.5 kB"
+ "none": "11.6 kB"
},
"packages/react-router-dom/dist/umd/react-router-dom.production.min.js": {
"none": "17.5 kB"
diff --git a/packages/react-router-dom/__tests__/link-click-test.tsx b/packages/react-router-dom/__tests__/link-click-test.tsx
index d3a8facd4c..29e878ce76 100644
--- a/packages/react-router-dom/__tests__/link-click-test.tsx
+++ b/packages/react-router-dom/__tests__/link-click-test.tsx
@@ -138,7 +138,6 @@ describe("A click", () => {
Home
{
handlerCalled = true;
@@ -171,6 +170,78 @@ describe("A
click", () => {
});
});
+ describe("when a same-origin/different-basename absolute URL is specified", () => {
+ it("does not prevent default", () => {
+ function Home() {
+ return (
+
+
Home
+ About
+
+ );
+ }
+
+ act(() => {
+ ReactDOM.createRoot(node).render(
+
+
+ } />
+
+
+ );
+ });
+
+ let anchor = node.querySelector("a");
+ expect(anchor).not.toBeNull();
+
+ let event: MouseEvent;
+ act(() => {
+ event = click(anchor);
+ });
+
+ expect(event.defaultPrevented).toBe(false);
+ });
+
+ it("calls provided listener", () => {
+ let handlerCalled;
+ let defaultPrevented;
+
+ function Home() {
+ return (
+
+
Home
+ {
+ handlerCalled = true;
+ defaultPrevented = e.defaultPrevented;
+ }}
+ >
+ About
+
+
+ );
+ }
+
+ act(() => {
+ ReactDOM.createRoot(node).render(
+
+
+ } />
+
+
+ );
+ });
+
+ act(() => {
+ click(node.querySelector("a"));
+ });
+
+ expect(handlerCalled).toBe(true);
+ expect(defaultPrevented).toBe(false);
+ });
+ });
+
describe("when reloadDocument is specified", () => {
it("does not prevent default", () => {
function Home() {
diff --git a/packages/react-router-dom/__tests__/link-push-test.tsx b/packages/react-router-dom/__tests__/link-push-test.tsx
index 305f1f1009..07b3263429 100644
--- a/packages/react-router-dom/__tests__/link-push-test.tsx
+++ b/packages/react-router-dom/__tests__/link-push-test.tsx
@@ -283,4 +283,47 @@ describe("Link push and replace", () => {
`);
});
});
+
+ describe("to an absolute same-origin/same-basename URL, when it is clicked", () => {
+ it("performs a push", () => {
+ function Home() {
+ return (
+
+
Home
+ About
+
+ );
+ }
+
+ let renderer: TestRenderer.ReactTestRenderer;
+ TestRenderer.act(() => {
+ renderer = TestRenderer.create(
+
+
+ } />
+ } />
+
+
+ );
+ });
+
+ let anchor = renderer.root.findByType("a");
+
+ TestRenderer.act(() => {
+ anchor.props.onClick(
+ new MouseEvent("click", {
+ view: window,
+ bubbles: true,
+ cancelable: true,
+ })
+ );
+ });
+
+ expect(renderer.toJSON()).toMatchInlineSnapshot(`
+
+ PUSH
+
+ `);
+ });
+ });
});
diff --git a/packages/react-router-dom/index.tsx b/packages/react-router-dom/index.tsx
index 1c24998adc..e7884bc18b 100644
--- a/packages/react-router-dom/index.tsx
+++ b/packages/react-router-dom/index.tsx
@@ -42,6 +42,7 @@ import {
createHashHistory,
UNSAFE_invariant as invariant,
joinPaths,
+ stripBasename,
ErrorResponse,
} from "@remix-run/router";
@@ -420,6 +421,8 @@ export const Link = React.forwardRef
(
},
ref
) {
+ let { basename } = React.useContext(NavigationContext);
+
// Rendered into for absolute URLs
let absoluteHref;
let isExternal = false;
@@ -434,9 +437,11 @@ export const Link = React.forwardRef(
let targetUrl = to.startsWith("//")
? new URL(currentUrl.protocol + to)
: new URL(to);
- if (targetUrl.origin === currentUrl.origin) {
- // Strip the protocol/origin for same-origin absolute URLs
- to = targetUrl.pathname + targetUrl.search + targetUrl.hash;
+ let path = stripBasename(targetUrl.pathname, basename);
+
+ if (targetUrl.origin === currentUrl.origin && path != null) {
+ // Strip the protocol/origin/basename for same-origin absolute URLs
+ to = path + targetUrl.search + targetUrl.hash;
} else {
isExternal = true;
}