Skip to content

Conversation

@imsherrill
Copy link

@imsherrill imsherrill commented Jan 12, 2026

Summary

  • Adds fullPath and to getters to RouteApi (returned by getRouteApi)
  • Provides parity with Route objects which already expose fullPath and to
  • Enables type-safe navigation: <Link from={routeApi.fullPath} to="./relative">
  • Uses the global router reference to resolve the correct fullPath at runtime
  • Correctly handles pathless/layout routes where id differs from fullPath

Test plan

  • Runtime tests for standard routes (id === fullPath)
  • Runtime tests for pathless layout routes (id = '/_layout/posts', fullPath = '/posts')
  • Type tests verifying correct typing for both cases
  • Tests added for react/vue/solid-router

Adds fullPath and to getters to RouteApi, enabling type-safe navigation
patterns like <Link from={routeApi.fullPath} to="./relative">.

Uses the global router reference (window.__TSR_ROUTER__) to resolve the
correct fullPath at runtime, correctly handling pathless/layout routes
where id differs from fullPath.
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 12, 2026

📝 Walkthrough

Walkthrough

This PR adds two new public properties (fullPath and to) to the getRouteApi return value across multiple router packages. The fullPath property resolves the route's URL path, with special handling for pathless layout routes that strips layout segments from the route ID. The to property mirrors fullPath. Changes include core implementation in BaseRouteApi and corresponding test validations across React, Solid, and Vue router packages.

Changes

Cohort / File(s) Summary
Core Implementation
packages/router-core/src/route.ts
Added fullPath and to getter methods to BaseRouteApi class. Both properties return RouteTypesById<TRouter, TId>['fullPath'], with fullPath preferring a global window.__TSR_ROUTER__ map and to serving as an alias.
React Router Tests
packages/react-router/tests/route.test.tsx, packages/react-router/tests/routeApi.test-d.tsx
Extended test coverage to validate fullPath and to properties for standard routes and pathless layout routes. Tests verify that fullPath strips layout segments from nested route IDs while to equals fullPath.
Solid Router Tests
packages/solid-router/tests/route.test.tsx, packages/solid-router/tests/routeApi.test-d.tsx
Added tests validating fullPath and to properties. Introduced layoutRoute and postsRoute definitions to test pathless layout behavior, asserting correct path resolution when layout segments are omitted.
Vue Router Tests
packages/vue-router/tests/route.test.tsx, packages/vue-router/tests/routeApi.test-d.tsx
Added test coverage for fullPath and to properties with pathless layout route scenarios. Tests verify type correctness and semantic behavior for both standard routes and nested layout configurations.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~15 minutes

Suggested labels

package: router-core, package: react-router, package: solid-router, package: vue-router

Suggested reviewers

  • schiller-manuel
  • nlynzaad

Poem

🐰 hops excitedly

Two paths now revealed with a single call,
fullPath and to, they answer it all!
Layout segments fade in the pathless night,
Route resolution shines ever so bright,
Tested in React, Solid, and Vue's delight! 🚀

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Expose fullPath and to on RouteApi from getRouteApi' is a concise, specific description of the main change: adding two new properties (fullPath and to) to the RouteApi object returned by getRouteApi.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In @packages/router-core/src/route.ts:
- Around line 1915-1927: Update the JSDoc for the fullPath getter to document
its SSR/runtime fallback: note that fullPath (getter fullPath on the route API)
returns the computed route.fullPath when window.__TSR_ROUTER__ and
routesById[this.id] are available at runtime, but during SSR or before
createRouter completes (i.e., when window.__TSR_ROUTER__ is absent) layout
routes without a path will return this.id (e.g., "__root_/posts") instead of a
resolved path (e.g., "/posts"); mention when this limitation occurs and suggest
checking for a router instance or using client-side code if the resolved path is
required.
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bc4e8d3 and a9c839d.

📒 Files selected for processing (7)
  • packages/react-router/tests/route.test.tsx
  • packages/react-router/tests/routeApi.test-d.tsx
  • packages/router-core/src/route.ts
  • packages/solid-router/tests/route.test.tsx
  • packages/solid-router/tests/routeApi.test-d.tsx
  • packages/vue-router/tests/route.test.tsx
  • packages/vue-router/tests/routeApi.test-d.tsx
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Use TypeScript strict mode with extensive type safety for all code

Files:

  • packages/react-router/tests/routeApi.test-d.tsx
  • packages/router-core/src/route.ts
  • packages/solid-router/tests/route.test.tsx
  • packages/vue-router/tests/routeApi.test-d.tsx
  • packages/solid-router/tests/routeApi.test-d.tsx
  • packages/vue-router/tests/route.test.tsx
  • packages/react-router/tests/route.test.tsx
**/*.{js,ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Implement ESLint rules for router best practices using the ESLint plugin router

Files:

  • packages/react-router/tests/routeApi.test-d.tsx
  • packages/router-core/src/route.ts
  • packages/solid-router/tests/route.test.tsx
  • packages/vue-router/tests/routeApi.test-d.tsx
  • packages/solid-router/tests/routeApi.test-d.tsx
  • packages/vue-router/tests/route.test.tsx
  • packages/react-router/tests/route.test.tsx
🧠 Learnings (7)
📓 Common learnings
Learnt from: schiller-manuel
Repo: TanStack/router PR: 6120
File: packages/router-generator/src/generator.ts:654-657
Timestamp: 2025-12-17T02:17:55.086Z
Learning: In `packages/router-generator/src/generator.ts`, pathless_layout routes must receive a `path` property when they have a `cleanedPath`, even though they are non-path routes. This is necessary because child routes inherit the path from their parent, and without this property, child routes would not have the correct full path at runtime.
Learnt from: schiller-manuel
Repo: TanStack/router PR: 5330
File: packages/router-core/src/router.ts:2231-2245
Timestamp: 2025-10-01T18:30:26.591Z
Learning: In `packages/router-core/src/router.ts`, the `resolveRedirect` method intentionally strips the router's origin from redirect URLs when they match (e.g., `https://foo.com/bar` → `/bar` for same-origin redirects) while preserving the full URL for cross-origin redirects. This logic should not be removed or simplified to use `location.publicHref` directly.
📚 Learning: 2025-12-17T02:17:55.086Z
Learnt from: schiller-manuel
Repo: TanStack/router PR: 6120
File: packages/router-generator/src/generator.ts:654-657
Timestamp: 2025-12-17T02:17:55.086Z
Learning: In `packages/router-generator/src/generator.ts`, pathless_layout routes must receive a `path` property when they have a `cleanedPath`, even though they are non-path routes. This is necessary because child routes inherit the path from their parent, and without this property, child routes would not have the correct full path at runtime.

Applied to files:

  • packages/react-router/tests/routeApi.test-d.tsx
  • packages/router-core/src/route.ts
  • packages/solid-router/tests/route.test.tsx
  • packages/vue-router/tests/routeApi.test-d.tsx
  • packages/solid-router/tests/routeApi.test-d.tsx
  • packages/vue-router/tests/route.test.tsx
  • packages/react-router/tests/route.test.tsx
📚 Learning: 2025-12-21T12:52:35.231Z
Learnt from: Sheraff
Repo: TanStack/router PR: 6171
File: packages/router-core/src/new-process-route-tree.ts:898-898
Timestamp: 2025-12-21T12:52:35.231Z
Learning: In `packages/router-core/src/new-process-route-tree.ts`, the matching logic intentionally allows paths without trailing slashes to match index routes with trailing slashes (e.g., `/a` can match `/a/` route), but not vice-versa (e.g., `/a/` cannot match `/a` layout route). This is implemented via the condition `!pathIsIndex || node.kind === SEGMENT_TYPE_INDEX` and is a deliberate design decision to provide better UX by being permissive with missing trailing slashes.

Applied to files:

  • packages/react-router/tests/routeApi.test-d.tsx
  • packages/router-core/src/route.ts
  • packages/solid-router/tests/route.test.tsx
  • packages/vue-router/tests/routeApi.test-d.tsx
  • packages/solid-router/tests/routeApi.test-d.tsx
  • packages/vue-router/tests/route.test.tsx
  • packages/react-router/tests/route.test.tsx
📚 Learning: 2025-10-08T08:11:47.088Z
Learnt from: nlynzaad
Repo: TanStack/router PR: 5402
File: packages/router-generator/tests/generator/no-formatted-route-tree/routeTree.nonnested.snapshot.ts:19-21
Timestamp: 2025-10-08T08:11:47.088Z
Learning: Test snapshot files in the router-generator tests directory (e.g., files matching the pattern `packages/router-generator/tests/generator/**/routeTree*.snapshot.ts` or `routeTree*.snapshot.js`) should not be modified or have issues flagged, as they are fixtures used to verify the generator's output and are intentionally preserved as-is.

Applied to files:

  • packages/react-router/tests/routeApi.test-d.tsx
  • packages/solid-router/tests/route.test.tsx
  • packages/vue-router/tests/routeApi.test-d.tsx
  • packages/solid-router/tests/routeApi.test-d.tsx
  • packages/vue-router/tests/route.test.tsx
  • packages/react-router/tests/route.test.tsx
📚 Learning: 2025-12-06T15:03:07.223Z
Learnt from: CR
Repo: TanStack/router PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-06T15:03:07.223Z
Learning: Applies to **/*.{js,ts,tsx} : Implement ESLint rules for router best practices using the ESLint plugin router

Applied to files:

  • packages/react-router/tests/routeApi.test-d.tsx
  • packages/solid-router/tests/route.test.tsx
  • packages/vue-router/tests/routeApi.test-d.tsx
  • packages/solid-router/tests/routeApi.test-d.tsx
  • packages/vue-router/tests/route.test.tsx
📚 Learning: 2025-10-14T18:59:33.990Z
Learnt from: FatahChan
Repo: TanStack/router PR: 5475
File: e2e/react-start/basic-prerendering/src/routes/redirect/$target/via-beforeLoad.tsx:8-0
Timestamp: 2025-10-14T18:59:33.990Z
Learning: In TanStack Router e2e test files, when a route parameter is validated at the route level (e.g., using zod in validateSearch or param validation), switch statements on that parameter do not require a default case, as the validation ensures only expected values will reach the switch.

Applied to files:

  • packages/solid-router/tests/route.test.tsx
  • packages/solid-router/tests/routeApi.test-d.tsx
📚 Learning: 2025-10-01T18:31:35.420Z
Learnt from: schiller-manuel
Repo: TanStack/router PR: 5330
File: e2e/react-start/custom-basepath/src/routeTree.gen.ts:58-61
Timestamp: 2025-10-01T18:31:35.420Z
Learning: Do not review files named `routeTree.gen.ts` in TanStack Router repositories, as these are autogenerated files that should not be manually modified.

Applied to files:

  • packages/vue-router/tests/routeApi.test-d.tsx
  • packages/solid-router/tests/routeApi.test-d.tsx
🧬 Code graph analysis (5)
packages/react-router/tests/routeApi.test-d.tsx (1)
packages/virtual-file-routes/src/api.ts (1)
  • rootRoute (10-19)
packages/router-core/src/route.ts (1)
packages/virtual-file-routes/src/api.ts (1)
  • route (66-84)
packages/solid-router/tests/route.test.tsx (1)
packages/virtual-file-routes/src/api.ts (1)
  • rootRoute (10-19)
packages/solid-router/tests/routeApi.test-d.tsx (1)
packages/virtual-file-routes/src/api.ts (1)
  • rootRoute (10-19)
packages/react-router/tests/route.test.tsx (1)
packages/virtual-file-routes/src/api.ts (1)
  • rootRoute (10-19)
🔇 Additional comments (15)
packages/router-core/src/route.ts (1)

1929-1935: LGTM! to as an alias for fullPath maintains API parity with Route.to.

This provides a consistent API surface where both Route and RouteApi expose to for navigation purposes.

packages/solid-router/tests/routeApi.test-d.tsx (2)

33-45: LGTM! Good test setup for pathless layout route scenario.

The route definitions correctly set up the layout route with id: '_layout' (no path) and a child postsRoute with path: 'posts', which creates the expected id = '/_layout/posts' and fullPath = '/posts' relationship.


119-145: LGTM! Comprehensive type tests for the new fullPath and to properties.

The tests correctly verify:

  • Standard routes: fullPath, to, and id all resolve to the same type ('/invoices/$invoiceId')
  • Pathless layout routes: id includes the layout segment ('/_layout/posts') while fullPath and to exclude it ('/posts')

This ensures type-safe navigation patterns like <Link from={routeApi.fullPath}> will work correctly.

packages/solid-router/tests/route.test.tsx (2)

64-79: LGTM! Good runtime validation for standard route behavior.

These tests verify that fullPath and to are accessible and return expected values. For standard routes without a pathless layout prefix, the fallback behavior (returning id) produces the correct result.


82-99: LGTM! Critical test for pathless layout route resolution.

This test correctly:

  1. Sets up a router with a pathless layout route (id: '_layout') and child route
  2. Calls createRouter which populates window.__TSR_ROUTER__
  3. Verifies that api.id remains '/_layout/posts' while api.fullPath and api.to resolve to '/posts'

This validates the core functionality of the PR - resolving the actual navigable path for routes nested under pathless layouts.

packages/vue-router/tests/routeApi.test-d.tsx (2)

32-44: LGTM! Consistent test setup across frameworks.

The pathless layout route setup mirrors the Solid and React router tests, ensuring consistent behavior validation across all supported frameworks.


110-136: LGTM! Type tests maintain cross-framework consistency.

The Vue router type tests correctly verify the same type relationships as the other frameworks:

  • Standard routes: fullPath === to === id
  • Pathless layout routes: id includes layout segment, fullPath and to exclude it
packages/react-router/tests/routeApi.test-d.tsx (2)

32-44: LGTM! Consistent pathless layout route setup.

The route definitions follow the same pattern as the other framework tests, ensuring uniform behavior across React, Solid, and Vue routers.


108-140: LGTM! Comprehensive type tests with practical RoutePaths validation.

The React tests include an additional valuable test at lines 136-140 that verifies fullPath is assignable to RoutePaths, confirming it can be used in <Link from={routeApi.fullPath}>. This demonstrates the practical use case that motivated the feature.

packages/vue-router/tests/route.test.tsx (3)

65-73: LGTM!

These tests correctly verify that the fullPath and to properties are exposed on the route API object. The tests run without a router initialized, which appropriately validates the fallback behavior where these properties equal the route ID.


75-80: LGTM!

Good coverage for standard routes where fullPath should equal id. The additional assertion expect(api.fullPath).toBe(api.id) explicitly documents this invariant.


82-99: LGTM - properly tests pathless layout route handling.

This test correctly validates the key feature: for pathless layout routes where id = '/_layout/posts', the fullPath resolves to /posts by stripping the layout segment. The router is properly created before calling getRouteApi, enabling the global router lookup. Based on learnings, pathless layout routes need special handling because child routes inherit paths from their parent.

packages/react-router/tests/route.test.tsx (3)

66-74: LGTM!

Property existence tests are correctly implemented and consistent with the Vue router tests.


76-81: LGTM!

Standard route test correctly verifies that fullPath === id for routes without pathless layout ancestors.


83-100: LGTM - pathless layout route test is well-structured.

The test properly sets up a layout route hierarchy and validates that getRouteApi('/_layout/posts') returns fullPath: '/posts' while preserving id: '/_layout/posts'. This is consistent with the Vue router test and correctly exercises the global router lookup mechanism.

Comment on lines +1915 to +1927
/**
* The full path of the route, which can be used as the `from` parameter
* in navigation APIs like `<Link from={routeApi.fullPath}>` or `navigate({ from: routeApi.fullPath })`.
*/
get fullPath(): RouteTypesById<TRouter, TId>['fullPath'] {
if (typeof window !== 'undefined' && window.__TSR_ROUTER__) {
const route = window.__TSR_ROUTER__.routesById[this.id as string]
if (route) {
return route.fullPath as RouteTypesById<TRouter, TId>['fullPath']
}
}
return this.id as RouteTypesById<TRouter, TId>['fullPath']
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# Search for __TSR_ROUTER__ type declaration and definition
rg -n "__TSR_ROUTER__" --type=ts -B2 -A2 | head -100

Repository: TanStack/router

Length of output: 4492


🏁 Script executed:

# Look at the actual file to understand the context better
head -n 1940 packages/router-core/src/route.ts | tail -n 50

Repository: TanStack/router

Length of output: 1403


🏁 Script executed:

# Search for fullPath property definition and assignments
rg -n "fullPath" packages/router-core/src/ --type=ts -B1 -A1 | head -150

Repository: TanStack/router

Length of output: 9142


🏁 Script executed:

# Search for documentation about BaseRouteApi.fullPath or routeApi behavior
rg -n "routeApi\.fullPath|BaseRouteApi" --type=ts packages/router-core/src/ -B3 -A3 | head -80

Repository: TanStack/router

Length of output: 1529


🏁 Script executed:

# Check if there are any test cases that document this behavior
rg -n "fullPath" packages/router-core/tests/ --type=ts -B2 -A2 | head -100

Repository: TanStack/router

Length of output: 7889


🏁 Script executed:

# Search for tests involving routeApi or BaseRouteApi
rg -n "routeApi|BaseRouteApi" packages/router-core/tests/ --type=ts -B2 -A2 | head -100

Repository: TanStack/router

Length of output: 41


🏁 Script executed:

# Look for tests that verify fullPath getter specifically
rg -n "\.fullPath" packages/router-core/tests/ --type=ts -B1 -A1 | head -50

Repository: TanStack/router

Length of output: 41


🏁 Script executed:

# Search for usage of routeApi.fullPath or routeApi.to in the codebase
rg -n "routeApi\.(fullPath|to)" --type=ts --type=tsx | head -50

Repository: TanStack/router

Length of output: 86


🏁 Script executed:

# Look for any examples or tests that use BaseRouteApi
find packages -name "*.test.ts" -o -name "*.test.tsx" -o -name "*.spec.ts" | xargs grep -l "fullPath" | head -5

Repository: TanStack/router

Length of output: 289


🏁 Script executed:

# Check if there's SSR-specific documentation or comments about BaseRouteApi
rg -n "SSR|server.*render|isomorphic" packages/router-core/src/route.ts -B2 -A2 | head -50

Repository: TanStack/router

Length of output: 1425


🏁 Script executed:

# Look at the broader context of how fullPath is used in actual route implementation vs API
rg -n "class Route|class BaseRouteApi" packages/router-core/src/route.ts -A15 | grep -A15 "get fullPath"

Repository: TanStack/router

Length of output: 375


🏁 Script executed:

# Check if there's any documentation about BaseRouteApi in comments or README
rg -n "BaseRouteApi" packages/ -B3 -A3 --type=ts | head -80

Repository: TanStack/router

Length of output: 3463


🏁 Script executed:

# Search for any JSDoc updates or issues related to fullPath fallback behavior
rg -n "id.*fallback|fallback.*id" packages/router-core/src/ --type=ts -B2 -A2

Repository: TanStack/router

Length of output: 1464


Update JSDoc to document SSR fallback behavior.

The implementation correctly guards against SSR and falls back to id when the router isn't available. However, the JSDoc comment (lines 1915-1917) only describes the intended purpose and doesn't document the runtime limitation. For SSR or before createRouter completes, pathless layout routes will return their id (e.g., __root_/posts) instead of their computed fullPath (e.g., /posts). Add a note to the JSDoc explaining this behavior and when it occurs, so developers understand the constraint when using routeApi.fullPath in SSR contexts.

🤖 Prompt for AI Agents
In @packages/router-core/src/route.ts around lines 1915 - 1927, Update the JSDoc
for the fullPath getter to document its SSR/runtime fallback: note that fullPath
(getter fullPath on the route API) returns the computed route.fullPath when
window.__TSR_ROUTER__ and routesById[this.id] are available at runtime, but
during SSR or before createRouter completes (i.e., when window.__TSR_ROUTER__ is
absent) layout routes without a path will return this.id (e.g., "__root_/posts")
instead of a resolved path (e.g., "/posts"); mention when this limitation occurs
and suggest checking for a router instance or using client-side code if the
resolved path is required.

* in navigation APIs like `<Link from={routeApi.fullPath}>` or `navigate({ from: routeApi.fullPath })`.
*/
get fullPath(): RouteTypesById<TRouter, TId>['fullPath'] {
if (typeof window !== 'undefined' && window.__TSR_ROUTER__) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this doesnt work during SSR correctly, so we cant use this

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants