Skip to content

Conversation

@imsherrill
Copy link

@imsherrill imsherrill commented Jan 12, 2026

Summary

  • Adds Route.createLink(Comp) method for type-safe relative navigation from a route
  • Also adds { from } option to standalone createLink() for the same functionality

Usage

// From route instance
const DashboardButton = dashboardRoute.createLink(MyButton)

// From route API  
const route = getRouteApi('/dashboard')
const DashboardButton = route.createLink(MyButton)

// Standalone with from option
const DashboardButton = createLink(MyButton, { from: '/dashboard' })

// All give type-safe relative navigation:
<DashboardButton to="settings" />
<DashboardButton to="$userId" params={{ userId: '123' }} />

Test plan

  • Type tests for valid relative paths
  • Type tests for invalid paths (caught as errors)
  • Type tests for required params on parameterized routes

Adds optional `{ from }` config to `createLink` for fixing the origin path.
Enables type-safe relative navigation (e.g., `./child`, `../sibling`) from a specific route.
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 12, 2026

📝 Walkthrough

Walkthrough

Adds a new CreateLinkOptions type with a required from field and extends createLink to accept an optional options parameter and a from generic, forwarding from={options?.from} into the rendered Link. Also exposes route-level createLink helpers and adds TypeScript tests for relative navigation.

Changes

Cohort / File(s) Summary
Link API update
packages/react-router/src/link.tsx
Added CreateLinkOptions<TRouter, TFrom> with from: TFrom. Updated createLink signature to <TRouter, const TComp, const TFrom>(..., options?: CreateLinkOptions<TRouter, TFrom>) and return LinkComponent<TComp, TFrom>. Implementation forwards from={options?.from} into <Link />.
Route API additions
packages/react-router/src/route.tsx
Added createLink methods to RouteExtensions, RouteApi, Route, and RootRoute, importing LinkComponent, LinkComponentRoute, CreateLinkProps, and Constrain. Implementations wrap provided components with a forwarded-ref CreatedLink that supplies the route's path as from.
Type-level tests
packages/react-router/tests/createLink-from.test-d.tsx
New DT test file validating type-safe relative navigation: valid/invalid relative paths, absolute paths, parent navigation, and parameterized route param requirements. Adds module augmentation to expose the test router.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested reviewers

  • schiller-manuel
  • SeanCassiere

Poem

🐰 I hop from route to route with cheer,
A tiny "from" to guide me near,
Types hold paws and show the way,
Relative hops safe for play,
✨🔗

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Title check ✅ Passed The PR title accurately describes the main change: adding Route.createLink() method for type-safe relative links. It aligns with the primary objective of enabling type-safe relative navigation through the new createLink API.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ 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: 0

🧹 Nitpick comments (1)
packages/react-router/tests/createLink-from.test-d.tsx (1)

1-1: Remove unused import expectTypeOf.

The expectTypeOf function is imported but never used in this file. The tests rely on @ts-expect-error annotations and standalone JSX expressions instead.

Suggested fix
-import { expectTypeOf, test } from 'vitest'
+import { test } from 'vitest'
📜 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 0f3b09c.

📒 Files selected for processing (2)
  • packages/react-router/src/link.tsx
  • packages/react-router/tests/createLink-from.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/src/link.tsx
  • packages/react-router/tests/createLink-from.test-d.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/src/link.tsx
  • packages/react-router/tests/createLink-from.test-d.tsx
🧠 Learnings (4)
📚 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/createLink-from.test-d.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/createLink-from.test-d.tsx
📚 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/createLink-from.test-d.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/react-router/tests/createLink-from.test-d.tsx
🧬 Code graph analysis (2)
packages/react-router/src/link.tsx (3)
packages/router-core/src/index.ts (4)
  • AnyRouter (208-208)
  • RoutePaths (55-55)
  • RegisteredRouter (210-210)
  • Constrain (306-306)
packages/router-core/src/router.ts (2)
  • AnyRouter (787-787)
  • RegisteredRouter (117-121)
packages/router-core/src/utils.ts (1)
  • Constrain (131-133)
packages/react-router/tests/createLink-from.test-d.tsx (1)
packages/virtual-file-routes/src/api.ts (1)
  • rootRoute (10-19)
🔇 Additional comments (6)
packages/react-router/tests/createLink-from.test-d.tsx (2)

18-65: LGTM!

The route tree setup is well-structured, matching the documented hierarchy. The module augmentation correctly registers the router type for type inference in tests.


72-108: Comprehensive type coverage for the new from option.

The test cases effectively validate:

  • Valid relative paths (./settings, ./users)
  • Absolute paths (/posts)
  • Parent navigation (..)
  • Invalid relative paths with proper @ts-expect-error annotations
  • Required params enforcement for parameterized routes
packages/react-router/src/link.tsx (4)

550-555: LGTM!

The CreateLinkOptions interface correctly constrains TFrom to valid route paths. The required from field ensures type-safe relative navigation is properly configured when options are provided.


574-583: Well-designed generic signature for type inference.

The const modifiers on TComp and TFrom ensure literal type inference. When calling createLink(Button, { from: '/dashboard' }), TypeScript correctly infers:

  • TComp as typeof Button
  • TFrom as the literal '/dashboard'

The return type LinkComponent<TComp, TFrom> properly propagates the fixed from path as the default for the component's navigation props.


584-588: LGTM!

The implementation correctly forwards the from value using optional chaining (options?.from), which gracefully handles the case when options is undefined by falling back to Link's default context-based behavior.


566-572: Good documentation updates.

The JSDoc clearly documents the new options parameter and provides practical examples showing both basic createLink usage and the new from option for type-safe relative navigation.

- RouteApi.createLink(Comp) for getRouteApi usage
- Route.createLink(Comp) for createFileRoute/createRoute usage
- Updated tests to show both syntaxes work (with and without ./ prefix)
@imsherrill imsherrill changed the title Add from option to createLink for type-safe relative navigation Add Route.createLink() for type-safe relative links Jan 12, 2026
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.

1 participant