diff --git a/.changeset/invalid-link-to.md b/.changeset/invalid-link-to.md
new file mode 100644
index 0000000000..309699aaf8
--- /dev/null
+++ b/.changeset/invalid-link-to.md
@@ -0,0 +1,5 @@
+---
+"react-router-dom": patch
+---
+
+Fail gracefully on `` and other invalid URL values
diff --git a/packages/react-router-dom/__tests__/link-href-test.tsx b/packages/react-router-dom/__tests__/link-href-test.tsx
index 386fb7d9b2..762f972ddc 100644
--- a/packages/react-router-dom/__tests__/link-href-test.tsx
+++ b/packages/react-router-dom/__tests__/link-href-test.tsx
@@ -907,4 +907,24 @@ describe(" href", () => {
);
});
});
+
+ test("fails gracefully on invalid `to` values", () => {
+ let warnSpy = jest.spyOn(console, "warn").mockImplementation(() => {});
+ let renderer: TestRenderer.ReactTestRenderer;
+ TestRenderer.act(() => {
+ renderer = TestRenderer.create(
+
+
+ } />
+
+
+ );
+ });
+
+ expect(renderer.root.findByType("a").props.href).toEqual("//");
+ expect(warnSpy).toHaveBeenCalledWith(
+ ' contains an invalid URL which will probably break when clicked - please update to a valid URL path.'
+ );
+ warnSpy.mockRestore();
+ });
});
diff --git a/packages/react-router-dom/index.tsx b/packages/react-router-dom/index.tsx
index 32e17b2a6c..a8351110e4 100644
--- a/packages/react-router-dom/index.tsx
+++ b/packages/react-router-dom/index.tsx
@@ -451,17 +451,26 @@ export const Link = React.forwardRef(
// Only check for external origins client-side
if (isBrowser) {
- let currentUrl = new URL(window.location.href);
- let targetUrl = to.startsWith("//")
- ? new URL(currentUrl.protocol + to)
- : new URL(to);
- 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;
+ try {
+ let currentUrl = new URL(window.location.href);
+ let targetUrl = to.startsWith("//")
+ ? new URL(currentUrl.protocol + to)
+ : new URL(to);
+ 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;
+ }
+ } catch (e) {
+ // We can't do external URL detection without a valid URL
+ warning(
+ false,
+ ` contains an invalid URL which will probably break ` +
+ `when clicked - please update to a valid URL path.`
+ );
}
}
}