diff --git a/packages/wouter/src/use-hash-location.js b/packages/wouter/src/use-hash-location.js
index 130b886..79dee1b 100644
--- a/packages/wouter/src/use-hash-location.js
+++ b/packages/wouter/src/use-hash-location.js
@@ -51,4 +51,11 @@ export const useHashLocation = ({ ssrPath = "/" } = {}) => [
navigate,
];
-useHashLocation.hrefs = (href) => "#" + href;
+// Search stays before the hash, matching `navigate`. A query inside the hash
+// (`#/settings?tab=1`) is not a separate search and fails route matching.
+useHashLocation.hrefs = (href) => {
+ const queryAt = href.indexOf("?");
+ return queryAt < 0
+ ? "#" + href
+ : href.slice(queryAt) + "#" + href.slice(0, queryAt);
+};
diff --git a/packages/wouter/test/use-hash-location.test.tsx b/packages/wouter/test/use-hash-location.test.tsx
index 59856f5..3b743eb 100644
--- a/packages/wouter/test/use-hash-location.test.tsx
+++ b/packages/wouter/test/use-hash-location.test.tsx
@@ -2,7 +2,7 @@ import { test, expect, mock } from "bun:test";
import { renderHook, render, act, fireEvent } from "@testing-library/react";
import { renderToStaticMarkup } from "react-dom/server";
-import { Router, Route, useLocation, Link } from "../src/index.js";
+import { Router, Route, useLocation, useSearch, Link } from "../src/index.js";
import { useHashLocation } from "../src/use-hash-location.js";
import { waitForHashChangeEvent } from "./test-utils";
@@ -377,3 +377,37 @@ test("uses string URLs as hashchange event payload", () => {
removeEventListener("hashchange", hashChangeFn);
});
+
+test("Link href keeps a query before the hash so opening it matches the route", () => {
+ history.replaceState(null, "", "/app#/home");
+
+ const { getByRole, unmount } = render(
+
+ Settings
+
+ );
+
+ const href = getByRole("link", { name: "Settings" }).getAttribute("href");
+ expect(href).toBe("?tab=profile#/settings");
+ unmount();
+
+ const opened = new URL(href!, "https://example.test/app");
+ history.replaceState(null, "", opened.pathname + opened.search + opened.hash);
+
+ const Settings = () => {
+ const search = useSearch();
+ return <>Settings page {search}>;
+ };
+
+ const { getByText, queryByText } = render(
+
+
+
+
+ Home
+
+ );
+
+ expect(queryByText("Home")).not.toBeInTheDocument();
+ expect(getByText("Settings page tab=profile")).toBeInTheDocument();
+});