Repository: TanStack/router (ts)

Issue:
Some route properties are undefined sometimes
### Describe the bug

When creating routes programmatically and accessing properties like `to` or `path` directly it happens that they are `undefined` although according to their type they are guaranteed to exist. This happens usually when using access a route directly without a router context. So when I am access e.g. `myRoute.to` in a react component that is wrapped in `RouterProvider` this is usually not a problem. When when I use `myRoute.to` somewhere else, e.g. not a react route then this error happens.

### Your Example Website or App

https://stackblitz.com/edit/github-yk78pg-fd4auw?file=README.md

### Steps to Reproduce the Bug or Issue

* create a route via `const myRoute = createRoute({ path: '/', /* ... */ })`;
* try to access `myRoute.to` or `myRoute.path`

### Expected behavior

I think it should be either fixed so that `to` is not `undefined` or the type should be adjusted to correctly reflect that this property could be `undefined`.

### Screenshots or Videos

_No response_

### Platform

- Version: `1.51.6`


### Additional context

_No response_

Pull request description:
fix(react-router): throw invariant exception when trying to access route properties prior to initialization
closes #2231

Patch:
diff --git a/examples/react/authenticated-routes/package.json b/examples/react/authenticated-routes/package.json
index d374cac8c..38ba282eb 100644
--- a/examples/react/authenticated-routes/package.json
+++ b/examples/react/authenticated-routes/package.json
@@ -9,8 +9,8 @@
     "start": "vite"
   },
   "dependencies": {
-    "@tanstack/react-router": "^1.51.7",
-    "@tanstack/router-devtools": "^1.51.7",
+    "@tanstack/react-router": "^1.51.8",
+    "@tanstack/router-devtools": "^1.51.8",
     "@tanstack/router-plugin": "^1.51.6",
     "immer": "^10.1.1",
     "react": "^18.2.0",
diff --git a/examples/react/basic-default-search-params/package.json b/examples/react/basic-default-search-params/package.json
index dfd6ee2ea..4cec7b6cc 100644
--- a/examples/react/basic-default-search-params/package.json
+++ b/examples/react/basic-default-search-params/package.json
@@ -10,8 +10,8 @@
   },
   "dependencies": {
     "@tanstack/react-query": "^5.51.21",
-    "@tanstack/react-router": "^1.51.7",
-    "@tanstack/router-devtools": "^1.51.7",
+    "@tanstack/react-router": "^1.51.8",
+    "@tanstack/router-devtools": "^1.51.8",
     "redaxios": "^0.5.1",
     "react": "^18.2.0",
     "react-dom": "^18.2.0",
diff --git a/examples/react/basic-file-based-codesplitting/package.json b/examples/react/basic-file-based-codesplitting/package.json
index 4a474babd..d8af393a8 100644
--- a/examples/react/basic-file-based-codesplitting/package.json
+++ b/examples/react/basic-file-based-codesplitting/package.json
@@ -9,8 +9,8 @@
     "start": "vite"
   },
   "dependencies": {
-    "@tanstack/react-router": "^1.51.7",
-    "@tanstack/router-devtools": "^1.51.7",
+    "@tanstack/react-router": "^1.51.8",
+    "@tanstack/router-devtools": "^1.51.8",
     "@tanstack/router-plugin": "^1.51.6",
     "immer": "^10.1.1",
     "react": "^18.2.0",
diff --git a/examples/react/basic-file-based/package.json b/examples/react/basic-file-based/package.json
index 2db906486..cd9e4855d 100644
--- a/examples/react/basic-file-based/package.json
+++ b/examples/react/basic-file-based/package.json
@@ -10,8 +10,8 @@
     "test:e2e": "playwright test --project=chromium"
   },
   "dependencies": {
-    "@tanstack/react-router": "^1.51.7",
-    "@tanstack/router-devtools": "^1.51.7",
+    "@tanstack/react-router": "^1.51.8",
+    "@tanstack/router-devtools": "^1.51.8",
     "@tanstack/router-plugin": "^1.51.6",
     "immer": "^10.1.1",
     "react": "^18.2.0",
diff --git a/examples/react/basic-react-query-file-based/package.json b/examples/react/basic-react-query-file-based/package.json
index 1bc1b9d9c..c4b758782 100644
--- a/examples/react/basic-react-query-file-based/package.json
+++ b/examples/react/basic-react-query-file-based/package.json
@@ -12,8 +12,8 @@
   "dependencies": {
     "@tanstack/react-query": "^5.51.21",
     "@tanstack/react-query-devtools": "^5.51.21",
-    "@tanstack/react-router": "^1.51.7",
-    "@tanstack/router-devtools": "^1.51.7",
+    "@tanstack/react-router": "^1.51.8",
+    "@tanstack/router-devtools": "^1.51.8",
     "@tanstack/router-plugin": "^1.51.6",
     "immer": "^10.1.1",
     "react": "^18.2.0",
diff --git a/examples/react/basic-react-query/package.json b/examples/react/basic-react-query/package.json
index 6789d6d8f..1e968aa2d 100644
--- a/examples/react/basic-react-query/package.json
+++ b/examples/react/basic-react-query/package.json
@@ -12,8 +12,8 @@
   "dependencies": {
     "@tanstack/react-query": "^5.51.21",
     "@tanstack/react-query-devtools": "^5.51.21",
-    "@tanstack/react-router": "^1.51.7",
-    "@tanstack/router-devtools": "^1.51.7",
+    "@tanstack/react-router": "^1.51.8",
+    "@tanstack/router-devtools": "^1.51.8",
     "react": "^18.2.0",
     "react-dom": "^18.2.0",
     "redaxios": "^0.5.1"
diff --git a/examples/react/basic-ssr-file-based/package.json b/examples/react/basic-ssr-file-based/package.json
index cd1822e68..61b99c3ac 100644
--- a/examples/react/basic-ssr-file-based/package.json
+++ b/examples/react/basic-ssr-file-based/package.json
@@ -11,10 +11,10 @@
     "debug": "node --inspect-brk server"
   },
   "dependencies": {
-    "@tanstack/react-router": "^1.51.7",
-    "@tanstack/router-devtools": "^1.51.7",
+    "@tanstack/react-router": "^1.51.8",
+    "@tanstack/router-devtools": "^1.51.8",
     "@tanstack/router-plugin": "^1.51.6",
-    "@tanstack/start": "^1.51.7",
+    "@tanstack/start": "^1.51.8",
     "get-port": "^7.1.0",
     "react": "^18.2.0",
     "react-dom": "^18.2.0",
diff --git a/examples/react/basic-ssr-streaming-file-based/package.json b/examples/react/basic-ssr-streaming-file-based/package.json
index 725406c7f..8cf33ef2a 100644
--- a/examples/react/basic-ssr-streaming-file-based/package.json
+++ b/examples/react/basic-ssr-streaming-file-based/package.json
@@ -11,10 +11,10 @@
     "debug": "node --inspect-brk server"
   },
   "dependencies": {
-    "@tanstack/react-router": "^1.51.7",
-    "@tanstack/router-devtools": "^1.51.7",
+    "@tanstack/react-router": "^1.51.8",
+    "@tanstack/router-devtools": "^1.51.8",
     "@tanstack/router-plugin": "^1.51.6",
-    "@tanstack/start": "^1.51.7",
+    "@tanstack/start": "^1.51.8",
     "get-port": "^7.1.0",
     "react": "^18.2.0",
     "react-dom": "^18.2.0",
diff --git a/examples/react/basic/package.json b/examples/react/basic/package.json
index f016d7d04..4dac82d5f 100644
--- a/examples/react/basic/package.json
+++ b/examples/react/basic/package.json
@@ -10,8 +10,8 @@
     "test:e2e": "playwright test --project=chromium"
   },
   "dependencies": {
-    "@tanstack/react-router": "^1.51.7",
-    "@tanstack/router-devtools": "^1.51.7",
+    "@tanstack/react-router": "^1.51.8",
+    "@tanstack/router-devtools": "^1.51.8",
     "react": "^18.2.0",
     "react-dom": "^18.2.0",
     "redaxios": "^0.5.1"
diff --git a/examples/react/deferred-data/package.json b/examples/react/deferred-data/package.json
index f36a11d71..f39427340 100644
--- a/examples/react/deferred-data/package.json
+++ b/examples/react/deferred-data/package.json
@@ -9,8 +9,8 @@
     "start": "vite"
   },
   "dependencies": {
-    "@tanstack/react-router": "^1.51.7",
-    "@tanstack/router-devtools": "^1.51.7",
+    "@tanstack/react-router": "^1.51.8",
+    "@tanstack/router-devtools": "^1.51.8",
     "redaxios": "^0.5.1",
     "immer": "^10.1.1",
     "react": "^18.2.0",
diff --git a/examples/react/kitchen-sink-file-based/package.json b/examples/react/kitchen-sink-file-based/package.json
index 0b5e6447c..abec1dfec 100644
--- a/examples/react/kitchen-sink-file-based/package.json
+++ b/examples/react/kitchen-sink-file-based/package.json
@@ -9,8 +9,8 @@
     "start": "vite"
   },
   "dependencies": {
-    "@tanstack/react-router": "^1.51.7",
-    "@tanstack/router-devtools": "^1.51.7",
+    "@tanstack/react-router": "^1.51.8",
+    "@tanstack/router-devtools": "^1.51.8",
     "@tanstack/router-plugin": "^1.51.6",
     "immer": "^10.1.1",
     "react": "^18.2.0",
diff --git a/examples/react/kitchen-sink-react-query-file-based/package.json b/examples/react/kitchen-sink-react-query-file-based/package.json
index ac0af3053..910c4995e 100644
--- a/examples/react/kitchen-sink-react-query-file-based/package.json
+++ b/examples/react/kitchen-sink-react-query-file-based/package.json
@@ -11,8 +11,8 @@
   "dependencies": {
     "@tanstack/react-query": "^5.51.21",
     "@tanstack/react-query-devtools": "^5.51.21",
-    "@tanstack/react-router": "^1.51.7",
-    "@tanstack/router-devtools": "^1.51.7",
+    "@tanstack/react-router": "^1.51.8",
+    "@tanstack/router-devtools": "^1.51.8",
     "@tanstack/router-plugin": "^1.51.6",
     "immer": "^10.1.1",
     "react": "^18.2.0",
diff --git a/examples/react/kitchen-sink-react-query/package.json b/examples/react/kitchen-sink-react-query/package.json
index 4e815e62f..f7bf78271 100644
--- a/examples/react/kitchen-sink-react-query/package.json
+++ b/examples/react/kitchen-sink-react-query/package.json
@@ -11,8 +11,8 @@
   "dependencies": {
     "@tanstack/react-query": "^5.51.21",
     "@tanstack/react-query-devtools": "^5.51.21",
-    "@tanstack/react-router": "^1.51.7",
-    "@tanstack/router-devtools": "^1.51.7",
+    "@tanstack/react-router": "^1.51.8",
+    "@tanstack/router-devtools": "^1.51.8",
     "redaxios": "^0.5.1",
     "immer": "^10.1.1",
     "react": "^18.2.0",
diff --git a/examples/react/kitchen-sink/package.json b/examples/react/kitchen-sink/package.json
index b37761fc3..58dce3ceb 100644
--- a/examples/react/kitchen-sink/package.json
+++ b/examples/react/kitchen-sink/package.json
@@ -9,8 +9,8 @@
     "start": "vite"
   },
   "dependencies": {
-    "@tanstack/react-router": "^1.51.7",
-    "@tanstack/router-devtools": "^1.51.7",
+    "@tanstack/react-router": "^1.51.8",
+    "@tanstack/router-devtools": "^1.51.8",
     "redaxios": "^0.5.1",
     "immer": "^10.1.1",
     "react": "^18.2.0",
diff --git a/examples/react/large-file-based/package.json b/examples/react/large-file-based/package.json
index 9dbe1ebf1..4ac10670c 100644
--- a/examples/react/large-file-based/package.json
+++ b/examples/react/large-file-based/package.json
@@ -12,8 +12,8 @@
   },
   "dependencies": {
     "@tanstack/react-query": "^5.51.21",
-    "@tanstack/react-router": "^1.51.7",
-    "@tanstack/router-devtools": "^1.51.7",
+    "@tanstack/react-router": "^1.51.8",
+    "@tanstack/router-devtools": "^1.51.8",
     "@tanstack/router-plugin": "^1.51.6",
     "immer": "^10.1.1",
     "react": "^18.2.0",
diff --git a/examples/react/location-masking/package.json b/examples/react/location-masking/package.json
index 1805664e1..b50df9642 100644
--- a/examples/react/location-masking/package.json
+++ b/examples/react/location-masking/package.json
@@ -11,8 +11,8 @@
   "dependencies": {
     "@radix-ui/react-dialog": "^1.1.1",
     "@tanstack/react-query": "^5.51.21",
-    "@tanstack/react-router": "^1.51.7",
-    "@tanstack/router-devtools": "^1.51.7",
+    "@tanstack/react-router": "^1.51.8",
+    "@tanstack/router-devtools": "^1.51.8",
     "redaxios": "^0.5.1",
     "react": "^18.2.0",
     "react-dom": "^18.2.0"
diff --git a/examples/react/navigation-blocking/package.json b/examples/react/navigation-blocking/package.json
index af91387bf..86c5c2b34 100644
--- a/examples/react/navigation-blocking/package.json
+++ b/examples/react/navigation-blocking/package.json
@@ -10,8 +10,8 @@
   },
   "dependencies": {
     "@tanstack/react-query": "^5.51.21",
-    "@tanstack/react-router": "^1.51.7",
-    "@tanstack/router-devtools": "^1.51.7",
+    "@tanstack/react-router": "^1.51.8",
+    "@tanstack/router-devtools": "^1.51.8",
     "redaxios": "^0.5.1",
     "react": "^18.2.0",
     "react-dom": "^18.2.0"
diff --git a/examples/react/quickstart-file-based/package.json b/examples/react/quickstart-file-based/package.json
index 6e40b3f3d..8f558aad6 100644
--- a/examples/react/quickstart-file-based/package.json
+++ b/examples/react/quickstart-file-based/package.json
@@ -9,8 +9,8 @@
     "start": "vite"
   },
   "dependencies": {
-    "@tanstack/react-router": "^1.51.7",
-    "@tanstack/router-devtools": "^1.51.7",
+    "@tanstack/react-router": "^1.51.8",
+    "@tanstack/router-devtools": "^1.51.8",
     "@tanstack/router-plugin": "^1.51.6",
     "immer": "^10.1.1",
     "react": "^18.2.0",
diff --git a/examples/react/quickstart-rspack-file-based/package.json b/examples/react/quickstart-rspack-file-based/package.json
index 4e4c7e729..f193bf8ab 100644
--- a/examples/react/quickstart-rspack-file-based/package.json
+++ b/examples/react/quickstart-rspack-file-based/package.json
@@ -8,8 +8,8 @@
     "preview": "rsbuild preview"
   },
   "dependencies": {
-    "@tanstack/react-router": "^1.51.7",
-    "@tanstack/router-devtools": "^1.51.7",
+    "@tanstack/react-router": "^1.51.8",
+    "@tanstack/router-devtools": "^1.51.8",
     "react": "^18.3.1",
     "react-dom": "^18.3.1"
   },
diff --git a/examples/react/quickstart-webpack-file-based/package.json b/examples/react/quickstart-webpack-file-based/package.json
index a2300caab..388c02259 100644
--- a/examples/react/quickstart-webpack-file-based/package.json
+++ b/examples/react/quickstart-webpack-file-based/package.json
@@ -7,8 +7,8 @@
     "build": "webpack build"
   },
   "dependencies": {
-    "@tanstack/react-router": "^1.51.7",
-    "@tanstack/router-devtools": "^1.51.7",
+    "@tanstack/react-router": "^1.51.8",
+    "@tanstack/router-devtools": "^1.51.8",
     "react": "^18.3.1",
     "react-dom": "^18.3.1"
   },
diff --git a/examples/react/quickstart/package.json b/examples/react/quickstart/package.json
index 0b1a3353e..9fbcc3d87 100644
--- a/examples/react/quickstart/package.json
+++ b/examples/react/quickstart/package.json
@@ -9,8 +9,8 @@
     "start": "vite"
   },
   "dependencies": {
-    "@tanstack/react-router": "^1.51.7",
-    "@tanstack/router-devtools": "^1.51.7",
+    "@tanstack/react-router": "^1.51.8",
+    "@tanstack/router-devtools": "^1.51.8",
     "react": "^18.2.0",
     "react-dom": "^18.2.0"
   },
diff --git a/examples/react/scroll-restoration/package.json b/examples/react/scroll-restoration/package.json
index 84c57f05e..5df0f468f 100644
--- a/examples/react/scroll-restoration/package.json
+++ b/examples/react/scroll-restoration/package.json
@@ -9,9 +9,9 @@
     "start": "vite"
   },
   "dependencies": {
-    "@tanstack/react-router": "^1.51.7",
+    "@tanstack/react-router": "^1.51.8",
     "@tanstack/react-virtual": "^3.8.4",
-    "@tanstack/router-devtools": "^1.51.7",
+    "@tanstack/router-devtools": "^1.51.8",
     "react": "^18.2.0",
     "react-dom": "^18.2.0"
   },
diff --git a/examples/react/search-validator-adapters/package.json b/examples/react/search-validator-adapters/package.json
index d530b99d6..57974a1eb 100644
--- a/examples/react/search-validator-adapters/package.json
+++ b/examples/react/search-validator-adapters/package.json
@@ -9,12 +9,12 @@
     "start": "vite"
   },
   "dependencies": {
-    "@tanstack/react-router": "^1.51.7",
-    "@tanstack/router-devtools": "^1.51.7",
+    "@tanstack/react-router": "^1.51.8",
+    "@tanstack/router-devtools": "^1.51.8",
     "@tanstack/router-plugin": "^1.51.6",
-    "@tanstack/router-zod-adapter": "^1.51.7",
-    "@tanstack/router-valibot-adapter": "^1.51.7",
-    "@tanstack/router-arktype-adapter": "^1.51.7",
+    "@tanstack/router-zod-adapter": "^1.51.8",
+    "@tanstack/router-valibot-adapter": "^1.51.8",
+    "@tanstack/router-arktype-adapter": "^1.51.8",
     "@tanstack/react-query": "^5.51.21",
     "react": "^18.2.0",
     "react-dom": "^18.2.0",
diff --git a/examples/react/start-basic-auth/package.json b/examples/react/start-basic-auth/package.json
index f7dee3b72..57ca1af43 100644
--- a/examples/react/start-basic-auth/package.json
+++ b/examples/react/start-basic-auth/package.json
@@ -14,10 +14,10 @@
   },
   "dependencies": {
     "@prisma/client": "5.17.0",
-    "@tanstack/react-router": "^1.51.7",
-    "@tanstack/router-devtools": "^1.51.7",
+    "@tanstack/react-router": "^1.51.8",
+    "@tanstack/router-devtools": "^1.51.8",
     "@tanstack/router-plugin": "^1.51.6",
-    "@tanstack/start": "^1.51.7",
+    "@tanstack/start": "^1.51.8",
     "@vitejs/plugin-react": "^4.3.1",
     "dotenv": "^16.4.5",
     "isbot": "^5.1.12",
diff --git a/examples/react/start-basic-counter/package.json b/examples/react/start-basic-counter/package.json
index 4e9a5ed63..4072819ce 100644
--- a/examples/react/start-basic-counter/package.json
+++ b/examples/react/start-basic-counter/package.json
@@ -11,8 +11,8 @@
     "format": "prettier --write '**/*' --ignore-unknown"
   },
   "dependencies": {
-    "@tanstack/react-router": "^1.51.7",
-    "@tanstack/start": "^1.51.7",
+    "@tanstack/react-router": "^1.51.8",
+    "@tanstack/start": "^1.51.8",
     "react": "^18.3.1",
     "react-dom": "^18.3.1",
     "vinxi": "0.4.2"
diff --git a/examples/react/start-basic-react-query/package.json b/examples/react/start-basic-react-query/package.json
index c6ebed41c..a0f5a1cd2 100644
--- a/examples/react/start-basic-react-query/package.json
+++ b/examples/react/start-basic-react-query/package.json
@@ -14,11 +14,11 @@
   "dependencies": {
     "@tanstack/react-query": "^5.51.21",
     "@tanstack/react-query-devtools": "^5.51.21",
-    "@tanstack/react-router": "^1.51.7",
-    "@tanstack/react-router-with-query": "^1.51.7",
-    "@tanstack/router-devtools": "^1.51.7",
+    "@tanstack/react-router": "^1.51.8",
+    "@tanstack/react-router-with-query": "^1.51.8",
+    "@tanstack/router-devtools": "^1.51.8",
     "@tanstack/router-plugin": "^1.51.6",
-    "@tanstack/start": "^1.51.7",
+    "@tanstack/start": "^1.51.8",
     "@vitejs/plugin-react": "^4.3.1",
     "isbot": "^5.1.14",
     "react": "^18.3.1",
diff --git a/examples/react/start-basic-rsc/package.json b/examples/react/start-basic-rsc/package.json
index 465c4f647..6d2e7dc3d 100644
--- a/examples/react/start-basic-rsc/package.json
+++ b/examples/react/start-basic-rsc/package.json
@@ -12,10 +12,10 @@
   },
   "dependencies": {
     "@babel/plugin-syntax-typescript": "^7.24.7",
-    "@tanstack/react-router": "^1.51.7",
-    "@tanstack/router-devtools": "^1.51.7",
+    "@tanstack/react-router": "^1.51.8",
+    "@tanstack/router-devtools": "^1.51.8",
     "@tanstack/router-plugin": "^1.51.6",
-    "@tanstack/start": "^1.51.7",
+    "@tanstack/start": "^1.51.8",
     "redaxios": "^0.5.1",
     "tailwind-merge": "^2.5.2",
     "vinxi": "0.4.2"
diff --git a/examples/react/start-basic/package.json b/examples/react/start-basic/package.json
index d9ddda570..902d85b54 100644
--- a/examples/react/start-basic/package.json
+++ b/examples/react/start-basic/package.json
@@ -12,10 +12,10 @@
     "test:e2e": "playwright test --project=chromium"
   },
   "dependencies": {
-    "@tanstack/react-router": "^1.51.7",
-    "@tanstack/router-devtools": "^1.51.7",
+    "@tanstack/react-router": "^1.51.8",
+    "@tanstack/router-devtools": "^1.51.8",
     "@tanstack/router-plugin": "^1.51.6",
-    "@tanstack/start": "^1.51.7",
+    "@tanstack/start": "^1.51.8",
     "@vitejs/plugin-react": "^4.3.1",
     "isbot": "^5.1.14",
     "react": "^18.3.1",
diff --git a/examples/react/start-clerk-basic/package.json b/examples/react/start-clerk-basic/package.json
index d8b908b5e..24d818bd9 100644
--- a/examples/react/start-clerk-basic/package.json
+++ b/examples/react/start-clerk-basic/package.json
@@ -13,10 +13,10 @@
   },
   "dependencies": {
     "@clerk/tanstack-start": "0.3.0-snapshot.vdf04997",
-    "@tanstack/react-router": "^1.51.7",
-    "@tanstack/router-devtools": "^1.51.7",
+    "@tanstack/react-router": "^1.51.8",
+    "@tanstack/router-devtools": "^1.51.8",
     "@tanstack/router-plugin": "^1.51.6",
-    "@tanstack/start": "^1.51.7",
+    "@tanstack/start": "^1.51.8",
     "@vitejs/plugin-react": "^4.3.1",
     "dotenv": "^16.4.5",
     "isbot": "^5.1.12",
diff --git a/examples/react/start-convex-trellaux/package.json b/examples/react/start-convex-trellaux/package.json
index 30736eeef..eb65116f1 100644
--- a/examples/react/start-convex-trellaux/package.json
+++ b/examples/react/start-convex-trellaux/package.json
@@ -15,11 +15,11 @@
   "dependencies": {
     "@tanstack/react-query": "^5.51.21",
     "@tanstack/react-query-devtools": "^5.51.21",
-    "@tanstack/react-router": "^1.51.7",
-    "@tanstack/react-router-with-query": "^1.51.7",
-    "@tanstack/router-devtools": "^1.51.7",
+    "@tanstack/react-router": "^1.51.8",
+    "@tanstack/react-router-with-query": "^1.51.8",
+    "@tanstack/router-devtools": "^1.51.8",
     "@tanstack/router-plugin": "^1.51.6",
-    "@tanstack/start": "^1.51.7",
+    "@tanstack/start": "^1.51.8",
     "@vitejs/plugin-react": "^4.3.1",
     "@convex-dev/react-query": "0.0.0-alpha.5",
     "concurrently": "^8.2.2",
diff --git a/examples/react/start-trellaux/package.json b/examples/react/start-trellaux/package.json
index 32695825a..577fd8fd0 100644
--- a/examples/react/start-trellaux/package.json
+++ b/examples/react/start-trellaux/package.json
@@ -13,11 +13,11 @@
   "dependencies": {
     "@tanstack/react-query": "^5.51.21",
     "@tanstack/react-query-devtools": "^5.51.21",
-    "@tanstack/react-router": "^1.51.7",
-    "@tanstack/react-router-with-query": "^1.51.7",
-    "@tanstack/router-devtools": "^1.51.7",
+    "@tanstack/react-router": "^1.51.8",
+    "@tanstack/react-router-with-query": "^1.51.8",
+    "@tanstack/router-devtools": "^1.51.8",
     "@tanstack/router-plugin": "^1.51.6",
-    "@tanstack/start": "^1.51.7",
+    "@tanstack/start": "^1.51.8",
     "@vitejs/plugin-react": "^4.3.1",
     "isbot": "^5.1.14",
     "ky": "^1.5.0",
diff --git a/examples/react/with-framer-motion/package.json b/examples/react/with-framer-motion/package.json
index 096cbef76..0c51f0e3b 100644
--- a/examples/react/with-framer-motion/package.json
+++ b/examples/react/with-framer-motion/package.json
@@ -9,8 +9,8 @@
     "start": "vite"
   },
   "dependencies": {
-    "@tanstack/react-router": "^1.51.7",
-    "@tanstack/router-devtools": "^1.51.7",
+    "@tanstack/react-router": "^1.51.8",
+    "@tanstack/router-devtools": "^1.51.8",
     "redaxios": "^0.5.1",
     "framer-motion": "^11.3.21",
     "immer": "^10.1.1",
diff --git a/examples/react/with-trpc-react-query/package.json b/examples/react/with-trpc-react-query/package.json
index a113af0eb..bef1c9d3e 100644
--- a/examples/react/with-trpc-react-query/package.json
+++ b/examples/react/with-trpc-react-query/package.json
@@ -10,8 +10,8 @@
   "dependencies": {
     "@tanstack/react-query": "^5.51.21",
     "@tanstack/react-query-devtools": "^5.51.21",
-    "@tanstack/react-router": "^1.51.7",
-    "@tanstack/router-devtools": "^1.51.7",
+    "@tanstack/react-router": "^1.51.8",
+    "@tanstack/router-devtools": "^1.51.8",
     "@tanstack/router-plugin": "^1.51.6",
     "@trpc/client": "11.0.0-rc.477",
     "@trpc/react-query": "11.0.0-rc.477",
diff --git a/examples/react/with-trpc/package.json b/examples/react/with-trpc/package.json
index ba3b61d6c..a15e156e8 100644
--- a/examples/react/with-trpc/package.json
+++ b/examples/react/with-trpc/package.json
@@ -8,8 +8,8 @@
     "start": "vinxi start"
   },
   "dependencies": {
-    "@tanstack/react-router": "^1.51.7",
-    "@tanstack/router-devtools": "^1.51.7",
+    "@tanstack/react-router": "^1.51.8",
+    "@tanstack/router-devtools": "^1.51.8",
     "@tanstack/router-plugin": "^1.51.6",
     "@trpc/client": "11.0.0-rc.477",
     "@trpc/server": "11.0.0-rc.477",
diff --git a/packages/react-router-with-query/package.json b/packages/react-router-with-query/package.json
index 20f15fd1d..a9d67ab20 100644
--- a/packages/react-router-with-query/package.json
+++ b/packages/react-router-with-query/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@tanstack/react-router-with-query",
-  "version": "1.51.7",
+  "version": "1.51.8",
   "description": "Modern and scalable routing for React applications",
   "author": "Tanner Linsley",
   "license": "MIT",
diff --git a/packages/react-router/package.json b/packages/react-router/package.json
index d2697d2a2..8355166f9 100644
--- a/packages/react-router/package.json
+++ b/packages/react-router/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@tanstack/react-router",
-  "version": "1.51.7",
+  "version": "1.51.8",
   "description": "Modern and scalable routing for React applications",
   "author": "Tanner Linsley",
   "license": "MIT",
diff --git a/packages/react-router/src/qss.ts b/packages/react-router/src/qss.ts
index b5ec8e9e4..b08d5878f 100644
--- a/packages/react-router/src/qss.ts
+++ b/packages/react-router/src/qss.ts
@@ -68,6 +68,7 @@ export function decode(str: any, pfx?: string) {
     const equalIndex = tmp.indexOf('=')
     if (equalIndex !== -1) {
       k = tmp.slice(0, equalIndex)
+      k = decodeURIComponent(k)
       const value = tmp.slice(equalIndex + 1)
       if (out[k] !== void 0) {
         // @ts-expect-error
@@ -77,6 +78,7 @@ export function decode(str: any, pfx?: string) {
       }
     } else {
       k = tmp
+      k = decodeURIComponent(k)
       out[k] = ''
     }
   }
diff --git a/packages/react-router/src/route.ts b/packages/react-router/src/route.ts
index 000bdef20..7797b496b 100644
--- a/packages/react-router/src/route.ts
+++ b/packages/react-router/src/route.ts
@@ -879,13 +879,45 @@ export class Route<
     TBeforeLoadFn
   >
 
-  // Set up in this.init()
+  // The following properties are set up in this.init()
   parentRoute!: TParentRoute
-  id!: TId
-  // customId!: TCustomId
-  path!: TPath
-  fullPath!: TFullPath
-  to!: TrimPathRight<TFullPath>
+  private _id!: TId
+  private _path!: TPath
+  private _fullPath!: TFullPath
+  private _to!: TrimPathRight<TFullPath>
+
+  public get to() {
+    invariant(
+      this._to,
+      `trying to access property 'to' on a route which is not initialized yet. Route properties are only available after 'createRouter' completed.`,
+    )
+    return this._to
+  }
+
+  public get id() {
+    invariant(
+      this._id,
+      `trying to access property 'id' on a route which is not initialized yet. Route properties are only available after 'createRouter' completed.`,
+    )
+    return this._id
+  }
+
+  public get path() {
+    invariant(
+      // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
+      this.isRoot || this._id || this._path,
+      `trying to access property 'path' on a route which is not initialized yet. Route properties are only available after 'createRouter' completed.`,
+    )
+    return this._path
+  }
+
+  public get fullPath() {
+    invariant(
+      this._fullPath,
+      `trying to access property 'fullPath' on a route which is not initialized yet. Route properties are only available after 'createRouter' completed.`,
+    )
+    return this._fullPath
+  }
 
   // Optional
   children?: TChildren
@@ -981,7 +1013,7 @@ export class Route<
     this.parentRoute = this.options.getParentRoute?.()
 
     if (isRoot) {
-      this.path = rootRouteId as TPath
+      this._path = rootRouteId as TPath
     } else {
       invariant(
         this.parentRoute,
@@ -1017,11 +1049,11 @@ export class Route<
     const fullPath =
       id === rootRouteId ? '/' : joinPaths([this.parentRoute.fullPath, path])
 
-    this.path = path as TPath
-    this.id = id as TId
+    this._path = path as TPath
+    this._id = id as TId
     // this.customId = customId as TCustomId
-    this.fullPath = fullPath as TFullPath
-    this.to = fullPath as TrimPathRight<TFullPath>
+    this._fullPath = fullPath as TFullPath
+    this._to = fullPath as TrimPathRight<TFullPath>
   }
 
   addChildren<
diff --git a/packages/router-arktype-adapter/package.json b/packages/router-arktype-adapter/package.json
index ba88b762c..44bc8b9de 100644
--- a/packages/router-arktype-adapter/package.json
+++ b/packages/router-arktype-adapter/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@tanstack/router-arktype-adapter",
-  "version": "1.51.7",
+  "version": "1.51.8",
   "description": "Modern and scalable routing for React applications",
   "author": "Tanner Linsley",
   "license": "MIT",
diff --git a/packages/router-devtools/package.json b/packages/router-devtools/package.json
index 78f4c78e8..8f6a3e200 100644
--- a/packages/router-devtools/package.json
+++ b/packages/router-devtools/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@tanstack/router-devtools",
-  "version": "1.51.7",
+  "version": "1.51.8",
   "description": "Modern and scalable routing for React applications",
   "author": "Tanner Linsley",
   "license": "MIT",
diff --git a/packages/router-valibot-adapter/package.json b/packages/router-valibot-adapter/package.json
index 73dc2b19b..f5d728120 100644
--- a/packages/router-valibot-adapter/package.json
+++ b/packages/router-valibot-adapter/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@tanstack/router-valibot-adapter",
-  "version": "1.51.7",
+  "version": "1.51.8",
   "description": "Modern and scalable routing for React applications",
   "author": "Tanner Linsley",
   "license": "MIT",
diff --git a/packages/router-zod-adapter/package.json b/packages/router-zod-adapter/package.json
index ca4f8c820..272b921a8 100644
--- a/packages/router-zod-adapter/package.json
+++ b/packages/router-zod-adapter/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@tanstack/router-zod-adapter",
-  "version": "1.51.7",
+  "version": "1.51.8",
   "description": "Modern and scalable routing for React applications",
   "author": "Tanner Linsley",
   "license": "MIT",
diff --git a/packages/start/package.json b/packages/start/package.json
index 3bb14ca92..4a82fb713 100644
--- a/packages/start/package.json
+++ b/packages/start/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@tanstack/start",
-  "version": "1.51.7",
+  "version": "1.51.8",
   "description": "Modern and scalable routing for React applications",
   "author": "Tanner Linsley",
   "license": "MIT",


Test patch:
diff --git a/packages/react-router/tests/createRoute.test.ts b/packages/react-router/tests/createRoute.test.ts
deleted file mode 100644
index efa13c814..000000000
--- a/packages/react-router/tests/createRoute.test.ts
+++ /dev/null
@@ -1,330 +0,0 @@
-import { describe, it } from 'vitest'
-import { z } from 'zod'
-import { createMemoryHistory, RootRoute, Route, createRouter } from '../src'
-
-const lazyTest = (comp: any, key: any) => {
-  comp.preload = () => {}
-  return comp
-}
-
-// Write a test
-describe('everything', () => {
-  it('should work', () => {
-    // // Build our routes. We could do this in our component, too.
-    // const rootRoute = createRootRoute()
-    // const indexRoute = createRoute({
-    //   getParentRoute: () => rootRoute,
-    //   path: '/',
-    //   validateSearch: (search) =>
-    //     z
-    //       .object({
-    //         version: z.number(),
-    //       })
-    //       .parse(search),
-    // })
-    // const testRoute = createRoute({
-    //   getParentRoute: () => rootRoute,
-    //   component: lazyTest(() => import('./TestComponent'), 'NamedComponent'),
-    //   path: 'test',
-    //   validateSearch: (search) =>
-    //     z
-    //       .object({
-    //         version: z.number(),
-    //         isGood: z.boolean(),
-    //       })
-    //       .parse(search),
-    // })
-    // const dashboardRoute = createRoute({
-    //   getParentRoute: () => rootRoute,
-    //   path: 'dashboard',
-    //   loader: async () => {
-    //     console.info('Fetching all invoices...')
-    //     return {
-    //       invoices: 'await fetchInvoices()',
-    //     }
-    //   },
-    // })
-    // const dashboardIndexRoute = createRoute({
-    //   getParentRoute: () => dashboardRoute,
-    //   path: '/',
-    // })
-    // const invoicesRoute = createRoute({
-    //   getParentRoute: () => dashboardRoute,
-    //   path: 'invoices',
-    // })
-    // const invoicesIndexRoute = createRoute({
-    //   getParentRoute: () => invoicesRoute,
-    //   path: '/',
-    // })
-    // const invoiceRoute = createRoute({
-    //   getParentRoute: () => invoicesRoute,
-    //   path: '$invoiceId',
-    //   parseParams: ({ invoiceId }) => ({ invoiceId: Number(invoiceId) }),
-    //   stringifyParams: ({ invoiceId }) => ({
-    //     invoiceId: String(invoiceId),
-    //   }),
-    //   loader: async ({ params: { invoiceId } }) => {
-    //     console.info('Fetching invoice...')
-    //     return {
-    //       invoice: 'await fetchInvoiceById(invoiceId!)',
-    //     }
-    //   },
-    // })
-    // const usersRoute = createRoute({
-    //   getParentRoute: () => dashboardRoute,
-    //   path: 'users',
-    //   loader: async () => {
-    //     return {
-    //       users: 'await fetchUsers()',
-    //     }
-    //   },
-    //   validateSearch: (search) =>
-    //     z
-    //       .object({
-    //         usersView: z
-    //           .object({
-    //             sortBy: z.enum(['name', 'id', 'email']).optional(),
-    //             filterBy: z.string().optional(),
-    //           })
-    //           .optional(),
-    //       })
-    //       .parse(search),
-    //   preSearchFilters: [
-    //     // Keep the usersView search param around
-    //     // while in this route (or it's children!)
-    //     (search) => ({
-    //       ...search,
-    //       usersView: {
-    //         ...search.usersView,
-    //       },
-    //     }),
-    //   ],
-    // })
-    // const userRoute = createRoute({
-    //   getParentRoute: () => usersRoute,
-    //   path: '$userId',
-    //   loader: async ({ params: { userId }, search }) => {
-    //     return {
-    //       user: 'await fetchUserById(userId!)',
-    //     }
-    //   },
-    // })
-    // const authenticatedRoute = createRoute({
-    //   getParentRoute: () => rootRoute,
-    //   path: 'authenticated/', // Trailing slash doesn't mean anything
-    // })
-    // const authenticatedIndexRoute = createRoute({
-    //   getParentRoute: () => authenticatedRoute,
-    //   path: '/',
-    // })
-    // const layoutRoute = createRoute({
-    //   getParentRoute: () => rootRoute,
-    //   id: 'layout',
-    //   component: () => 'layout-wrapper',
-    //   validateSearch: (search) =>
-    //     z
-    //       .object({
-    //         isLayout: z.boolean(),
-    //       })
-    //       .parse(search),
-    // })
-    // const layoutARoute = createRoute({
-    //   getParentRoute: () => layoutRoute,
-    //   path: 'layout-a',
-    //   component: () => 'layout-a',
-    // })
-    // const layoutBRoute = createRoute({
-    //   getParentRoute: () => layoutRoute,
-    //   path: 'layout-b',
-    //   component: () => 'layout-b',
-    // })
-    // const routeTree = rootRoute.addChildren([
-    //   indexRoute,
-    //   testRoute,
-    //   dashboardRoute.addChildren([
-    //     dashboardIndexRoute,
-    //     invoicesRoute.addChildren([invoicesIndexRoute, invoiceRoute]),
-    //     usersRoute.addChildren([userRoute]),
-    //   ]),
-    //   authenticatedRoute.addChildren([authenticatedIndexRoute]),
-    //   layoutRoute.addChildren([layoutARoute, layoutBRoute]),
-    // ])
-    // const router = createRouter({
-    //   routeTree,
-    //   history: createMemoryHistory({
-    //     initialEntries: ['/?version=1'],
-    //   }),
-    // })
-    // const route = router.getRoute('/dashboard/users/$userId')
-    // router.buildLink({
-    //   to: '/dashboard/users/$userId',
-    //   params: {
-    //     userId: '2',
-    //   },
-    //   search: (prev) => ({
-    //     usersView: {
-    //       sortBy: 'email',
-    //     },
-    //   }),
-    // })
-    // // @ts-expect-error
-    // router.buildLink({
-    //   from: '/',
-    //   to: '/test',
-    // })
-    // router.buildLink({
-    //   from: '/',
-    //   to: '/test',
-    //   search: () => {
-    //     return {
-    //       version: 2,
-    //       isGood: true,
-    //     }
-    //   },
-    // })
-    // router.buildLink({
-    //   from: '/test',
-    //   to: '/',
-    // })
-    // router.buildLink({
-    //   from: route.id,
-    //   to: '',
-    // })
-    // router.buildLink({
-    //   from: '/dashboard',
-    //   to: '/dashboard/invoices',
-    //   params: {
-    //     invoiceId: 2,
-    //   },
-    // })
-    // router.buildLink({
-    //   from: '/dashboard',
-    //   to: '/dashboard/invoices/$invoiceId',
-    //   params: {
-    //     // @ts-expect-error
-    //     invoiceId: '2',
-    //   },
-    // })
-    // router.buildLink({
-    //   to: '/dashboard/invoices/$invoiceId',
-    //   params: {
-    //     invoiceId: 2,
-    //   },
-    // })
-    // router.buildLink({
-    //   to: '/',
-    //   search: {
-    //     version: 2,
-    //   },
-    // })
-    // router.buildLink({
-    //   to: '/dashboard/users/$userId',
-    //   // @ts-expect-error
-    //   params: (current) => ({
-    //     userId: current?.invoiceId,
-    //   }),
-    //   search: (old) => ({
-    //     usersView: {
-    //       sortBy: 'email' as const,
-    //       filterBy: String(old.version),
-    //     },
-    //   }),
-    // })
-    // router.buildLink({
-    //   from: '/dashboard/invoices/$invoiceId',
-    //   to: '/dashboard/users/$userId',
-    //   params: (current) => ({
-    //     userId: `${current?.invoiceId}`,
-    //   }),
-    //   search: (prev) => {
-    //     return {
-    //       usersView: {
-    //         sortBy: 'name' as const,
-    //         filterBy: 'tanner',
-    //       },
-    //     }
-    //   },
-    // })
-    // router.buildLink({
-    //   from: '/dashboard/users/$userId',
-    //   to: '/',
-    //   search: (prev) => {
-    //     return {
-    //       version: 2,
-    //     }
-    //   },
-    // })
-    // router.buildLink({
-    //   from: '/',
-    //   to: '/dashboard/users/$userId',
-    //   params: {
-    //     userId: '2',
-    //   },
-    //   search: (prev) => ({
-    //     usersView: {
-    //       sortBy: 'id',
-    //       filterBy: `${prev.version}`,
-    //     },
-    //   }),
-    // })
-    // router.navigate({
-    //   search: (prev: any) => ({
-    //     version: prev.version,
-    //   }),
-    // })
-    // router.buildLink({
-    //   from: '/dashboard/invoices',
-    //   to: '/dashboard',
-    // })
-    // router.buildLink({
-    //   from: '/',
-    //   // @ts-expect-error
-    //   to: '/does-not-exist',
-    // })
-    // router.buildLink({
-    //   to: '/dashboard/invoices/$invoiceId',
-    //   params: {
-    //     invoiceId: 2,
-    //   },
-    // })
-    // router.buildLink({
-    //   from: '/dashboard/invoices/$invoiceId',
-    //   to: '.',
-    //   params: (d) => ({
-    //     invoiceId: d.invoiceId,
-    //   }),
-    // })
-    // router.buildLink({
-    //   from: '/dashboard/invoices/$invoiceId',
-    //   to: testRoute.fullPath,
-    //   search: {
-    //     version: 2,
-    //     isGood: true,
-    //   },
-    // })
-    // router.buildLink({
-    //   to: '/layout-a',
-    //   search: (current) => ({
-    //     isLayout: !!current.version,
-    //   }),
-    // })
-    // router.buildLink({
-    //   to: '/',
-    //   state: true,
-    // })
-    // router.buildLink({
-    //   to: '/',
-    //   state: {},
-    // })
-    // router.buildLink({
-    //   to: '/',
-    //   state: (prev) => prev,
-    // })
-    // router.buildLink({
-    //   to: '/',
-    //   state: (prev) => ({
-    //     test: true,
-    //   }),
-    // })
-  })
-})
diff --git a/packages/react-router/tests/navigate.test.tsx b/packages/react-router/tests/navigate.test.tsx
index 61e34f55c..cf91b6612 100644
--- a/packages/react-router/tests/navigate.test.tsx
+++ b/packages/react-router/tests/navigate.test.tsx
@@ -68,6 +68,15 @@ function createTestRouter(initialHistory?: RouterHistory) {
     getParentRoute: () => gLayoutRoute,
     path: '$username',
   })
+  const searchRoute = createRoute({
+    getParentRoute: () => rootRoute,
+    path: 'search',
+    validateSearch: (search: Record<string, unknown>) => {
+      return {
+        ['foo=bar']: Number(search['foo=bar'] ?? 1),
+      }
+    },
+  })
 
   const projectTree = projectRoute.addChildren([
     projectIdRoute.addChildren([
@@ -460,4 +469,22 @@ describe('router.navigate navigation using layout routes resolves correctly', as
 
     expect(router.state.location.pathname).toBe('/g/tkdodo')
   })
+
+  it('should handle search params with special characters', async () => {
+    const { router } = createTestRouter(
+      createMemoryHistory({ initialEntries: ['/search?foo%3Dbar=2'] }),
+    )
+
+    await router.load()
+
+    expect(router.state.location.pathname).toBe('/search')
+    expect(router.state.location.search).toStrictEqual({ 'foo=bar': 2 })
+
+    await router.navigate({
+      search: { 'foo=bar': 3 },
+    })
+    await router.invalidate()
+
+    expect(router.state.location.search).toStrictEqual({ 'foo=bar': 3 })
+  })
 })
diff --git a/packages/react-router/tests/qss.test.ts b/packages/react-router/tests/qss.test.ts
index b7aebec20..7f1bbca23 100644
--- a/packages/react-router/tests/qss.test.ts
+++ b/packages/react-router/tests/qss.test.ts
@@ -32,6 +32,12 @@ describe('encode function', () => {
     const queryString = encode(obj)
     expect(queryString).toEqual('token=foo%3F&key=value%3D')
   })
+
+  it('should handle encoding a top-level key with a special character', () => {
+    const obj = { 'foo=bar': 1 }
+    const queryString = encode(obj)
+    expect(queryString).toEqual('foo%3Dbar=1')
+  })
 })
 
 describe('decode function', () => {
@@ -64,4 +70,22 @@ describe('decode function', () => {
     const decodedObj = decode(queryString)
     expect(decodedObj).toEqual({ token: 'foo?', key: 'value=' })
   })
+
+  it('should handle decoding a top-level key with a special character', () => {
+    const queryString = 'foo%3Dbar=1'
+    const decodedObj = decode(queryString)
+    expect(decodedObj).toEqual({ 'foo=bar': 1 })
+  })
+
+  it('should handle decoding a top-level key with a special character and without a value', () => {
+    const queryString = 'foo%3Dbar='
+    const decodedObj = decode(queryString)
+    expect(decodedObj).toEqual({ 'foo=bar': '' })
+  })
+
+  it('should handle decoding a value-less top-level key with a special character', () => {
+    const queryString = 'foo%3Dbar'
+    const decodedObj = decode(queryString)
+    expect(decodedObj).toEqual({ 'foo=bar': '' })
+  })
 })
diff --git a/packages/react-router/tests/route.test.ts b/packages/react-router/tests/route.test.ts
deleted file mode 100644
index a001f7bfa..000000000
--- a/packages/react-router/tests/route.test.ts
+++ /dev/null
@@ -1,53 +0,0 @@
-/* eslint-disable */
-import { describe, it, expect } from 'vitest'
-import { getRouteApi, createRoute } from '../src'
-
-describe('getRouteApi', () => {
-  it('should have the useMatch hook', () => {
-    const api = getRouteApi('foo')
-    expect(api.useMatch).toBeDefined()
-  })
-
-  it('should have the useRouteContext hook', () => {
-    const api = getRouteApi('foo')
-    expect(api.useRouteContext).toBeDefined()
-  })
-
-  it('should have the useSearch hook', () => {
-    const api = getRouteApi('foo')
-    expect(api.useSearch).toBeDefined()
-  })
-
-  it('should have the useParams hook', () => {
-    const api = getRouteApi('foo')
-    expect(api.useParams).toBeDefined()
-  })
-
-  it('should have the useLoaderData hook', () => {
-    const api = getRouteApi('foo')
-    expect(api.useLoaderData).toBeDefined()
-  })
-
-  it('should have the useLoaderDeps hook', () => {
-    const api = getRouteApi('foo')
-    expect(api.useLoaderDeps).toBeDefined()
-  })
-
-  it('should have the useNavigate hook', () => {
-    const api = getRouteApi('foo')
-    expect(api.useNavigate).toBeDefined()
-  })
-})
-
-describe('createRoute has the same hooks as getRouteApi', () => {
-  const routeApi = getRouteApi('foo')
-  const hookNames = Object.keys(routeApi).filter((key) => key.startsWith('use'))
-  const route = createRoute({} as any)
-
-  it.each(hookNames.map((name) => [name]))(
-    'should have the "%s" hook defined',
-    (hookName) => {
-      expect(route[hookName as keyof typeof route]).toBeDefined()
-    },
-  )
-})
diff --git a/packages/react-router/tests/route.test.tsx b/packages/react-router/tests/route.test.tsx
new file mode 100644
index 000000000..fd881e328
--- /dev/null
+++ b/packages/react-router/tests/route.test.tsx
@@ -0,0 +1,140 @@
+/* eslint-disable */
+import { describe, it, expect } from 'vitest'
+import {
+  getRouteApi,
+  createRoute,
+  createRootRoute,
+  createRouter,
+  useNavigate,
+} from '../src'
+import React from 'react'
+import exp from 'constants'
+
+describe('getRouteApi', () => {
+  it('should have the useMatch hook', () => {
+    const api = getRouteApi('foo')
+    expect(api.useMatch).toBeDefined()
+  })
+
+  it('should have the useRouteContext hook', () => {
+    const api = getRouteApi('foo')
+    expect(api.useRouteContext).toBeDefined()
+  })
+
+  it('should have the useSearch hook', () => {
+    const api = getRouteApi('foo')
+    expect(api.useSearch).toBeDefined()
+  })
+
+  it('should have the useParams hook', () => {
+    const api = getRouteApi('foo')
+    expect(api.useParams).toBeDefined()
+  })
+
+  it('should have the useLoaderData hook', () => {
+    const api = getRouteApi('foo')
+    expect(api.useLoaderData).toBeDefined()
+  })
+
+  it('should have the useLoaderDeps hook', () => {
+    const api = getRouteApi('foo')
+    expect(api.useLoaderDeps).toBeDefined()
+  })
+
+  it('should have the useNavigate hook', () => {
+    const api = getRouteApi('foo')
+    expect(api.useNavigate).toBeDefined()
+  })
+})
+
+describe('createRoute has the same hooks as getRouteApi', () => {
+  const routeApi = getRouteApi('foo')
+  const hookNames = Object.keys(routeApi).filter((key) => key.startsWith('use'))
+  const route = createRoute({} as any)
+
+  it.each(hookNames.map((name) => [name]))(
+    'should have the "%s" hook defined',
+    (hookName) => {
+      expect(route[hookName as keyof typeof route]).toBeDefined()
+    },
+  )
+})
+
+describe('throws invariant exception when trying to access properties before `createRouter` completed', () => {
+  function setup() {
+    const rootRoute = createRootRoute()
+
+    const IndexComponent = () => {
+      const navigate = useNavigate()
+      return (
+        <React.Fragment>
+          <h1>Index</h1>
+          <button onClick={() => navigate({ to: '/' })}>Index</button>
+          <button onClick={() => navigate({ to: '/posts' })}>Posts</button>
+        </React.Fragment>
+      )
+    }
+
+    const indexRoute = createRoute({
+      getParentRoute: () => rootRoute,
+      path: '/',
+      component: IndexComponent,
+    })
+
+    const postsRoute = createRoute({
+      getParentRoute: () => rootRoute,
+      path: '/posts',
+      component: () => {
+        return (
+          <React.Fragment>
+            <h1>Posts</h1>
+          </React.Fragment>
+        )
+      },
+    })
+    const initRouter = () =>
+      createRouter({
+        routeTree: rootRoute.addChildren([indexRoute, postsRoute]),
+      })
+    return { initRouter, rootRoute, indexRoute, postsRoute }
+  }
+
+  it('to', () => {
+    const { initRouter, indexRoute, postsRoute } = setup()
+
+    const expectedError = `Invariant failed: trying to access property 'to' on a route which is not initialized yet. Route properties are only available after 'createRouter' completed.`
+    expect(() => indexRoute.to).toThrowError(expectedError)
+    expect(() => postsRoute.to).toThrowError(expectedError)
+
+    initRouter()
+
+    expect(indexRoute.to).toBe('/')
+    expect(postsRoute.to).toBe('/posts')
+  })
+
+  it('fullPath', () => {
+    const { initRouter, indexRoute, postsRoute } = setup()
+
+    const expectedError = `trying to access property 'fullPath' on a route which is not initialized yet. Route properties are only available after 'createRouter' completed.`
+    expect(() => indexRoute.fullPath).toThrowError(expectedError)
+    expect(() => postsRoute.fullPath).toThrowError(expectedError)
+
+    initRouter()
+
+    expect(indexRoute.fullPath).toBe('/')
+    expect(postsRoute.fullPath).toBe('/posts')
+  })
+
+  it('id', () => {
+    const { initRouter, indexRoute, postsRoute } = setup()
+
+    const expectedError = `Invariant failed: trying to access property 'id' on a route which is not initialized yet. Route properties are only available after 'createRouter' completed.`
+    expect(() => indexRoute.id).toThrowError(expectedError)
+    expect(() => postsRoute.id).toThrowError(expectedError)
+
+    initRouter()
+
+    expect(indexRoute.to).toBe('/')
+    expect(postsRoute.to).toBe('/posts')
+  })
+})