- Returns the current location object, or null when the URL
- is not available (e.g. during SSR). This is the SSR-safe alternative
- to useLocation().
-
- Location | null — The current location object
- with pathname, search, and{" "}
- hash properties, or null during SSR when
- no URL is available.
-
-
-
-
useSearchParams()
diff --git a/packages/docs/src/pages/ApiReferenceIndexPage.tsx b/packages/docs/src/pages/ApiReferenceIndexPage.tsx
index 030d4cf..16d9296 100644
--- a/packages/docs/src/pages/ApiReferenceIndexPage.tsx
+++ b/packages/docs/src/pages/ApiReferenceIndexPage.tsx
@@ -37,10 +37,6 @@ export function ApiReferenceIndexPage() {
useLocation() — Current location object
-
- useLocationSSR() — SSR-safe current location (returns{" "}
- null during SSR)
-
useSearchParams() — Search query management
diff --git a/packages/docs/src/pages/LearnSsrPage.tsx b/packages/docs/src/pages/LearnSsrPage.tsx
index a2ee3d8..c9125bf 100644
--- a/packages/docs/src/pages/LearnSsrPage.tsx
+++ b/packages/docs/src/pages/LearnSsrPage.tsx
@@ -91,9 +91,10 @@ useSearchParams();
// Error: "useSearchParams: URL is not available during SSR."`}
To avoid these errors, either use URL-dependent hooks only in
- components rendered by path-based routes, or use the SSR-safe{" "}
- useLocationSSR() hook which returns null{" "}
- instead of throwing when the URL is unavailable:
+ components rendered by path-based routes, or read the current path
+ inside a client-side effect (e.g., useLayoutEffect +{" "}
+ navigation.currentEntry) so the value is only accessed
+ after hydration:
{`// ✗ Bad: AppShell renders during SSR, useLocation will throw
function AppShell() {
@@ -101,13 +102,20 @@ function AppShell() {
return
{/* ... */}
;
}
-// ✓ Good: Use useLocationSSR in components that render during SSR
+// ✓ Good: Read the path in a client-side effect
+function useCurrentPath() {
+ const [path, setPath] = useState(undefined);
+ useLayoutEffect(() => {
+ setPath(navigation.currentEntry?.url
+ ? new URL(navigation.currentEntry.url).pathname
+ : undefined);
+ }, []);
+ return path;
+}
+
function AppShell() {
- const location = useLocationSSR(); // Returns null during SSR
- const isActive = (path: string) => {
- if (location === null) return false;
- return location.pathname === path;
- };
+ const path = useCurrentPath(); // undefined during SSR, string after hydration
+ const isActive = (p: string) => path === p;
return ;
}
@@ -159,8 +167,9 @@ function HomePage() {
Avoid useLocation and useSearchParams in
- components that render during SSR; use useLocationSSR{" "}
- instead when you need location information in the app shell
+ components that render during SSR; use a client-side effect (e.g.,{" "}
+ useLayoutEffect) to read location information in the
+ app shell
This two-stage model keeps SSR output lightweight while enabling
diff --git a/packages/router/src/__tests__/hooks.test.tsx b/packages/router/src/__tests__/hooks.test.tsx
index 2431bc4..1388a5f 100644
--- a/packages/router/src/__tests__/hooks.test.tsx
+++ b/packages/router/src/__tests__/hooks.test.tsx
@@ -4,11 +4,9 @@ import { Suspense, type ReactNode } from "react";
import { Router } from "../Router.js";
import { useNavigate } from "../hooks/useNavigate.js";
import { useLocation } from "../hooks/useLocation.js";
-import { useLocationSSR } from "../hooks/useLocationSSR.js";
import { useSearchParams } from "../hooks/useSearchParams.js";
import { useIsPending } from "../hooks/useIsPending.js";
import { setupNavigationMock, cleanupNavigationMock } from "./setup.js";
-import { RouterContext } from "../context/RouterContext.js";
import type { RouteDefinition } from "../route.js";
describe("hooks", () => {
@@ -133,72 +131,6 @@ describe("hooks", () => {
});
});
- describe("useLocationSSR", () => {
- it("returns current location when URL is available", () => {
- mockNavigation = setupNavigationMock(
- "http://localhost/page?foo=bar#section",
- );
-
- function TestComponent() {
- const location = useLocationSSR();
- return (
-