Repository: TanStack/router (2025-08-17T13:50:03Z)

Issue:
Relative routing to index route does not navigate
### Which project does this relate to?

Router

### Describe the bug

Performing relative navigation to the index route does not navigate.

E.g., if I have a route of `/posts/`, navigating `from="/posts/` `to="."` does not navigate, even if current URL is a child route (e.g. `/posts/$postId`).

### Your Example Website or App

https://stackblitz.com/edit/tanstack-router-vlwhvtl4?file=src%2Froutes%2Fposts.route.tsx

### Steps to Reproduce the Bug or Issue

* Navigate to a specific post (e.g. /posts/1)
* Click the `To Index` link on the left hand side above the post links
* The page does not navigate back to the index

### Expected behavior

I would expect the route to successfully navigate to the index route if it is currently on a child route.

### Screenshots or Videos

https://github.com/user-attachments/assets/63f28f8e-c6b8-4df2-b30f-2a41ee2f0837

### Platform

- Router Version: 1.130.9
- OS: macOS
- Browser: Chrome
- Browser Version: 138.0
- Bundler: vite
- Bundler Version: 6.3.5


### Additional context

_No response_


Discussion:
whereas ".." is used to navigate one route back based on the route it is rendered on or the from specified, "." will always reload the current location irrespective of from specified. Any reason you are looking to use this instead of specifying to="/posts"? Would like to understand the use case where this would be preferred
Thanks for the clarification @nlynzaad.

Our use case is tabs that live at the parent level which itself includes params (e.g. `/accounts/$accountId`), and ideally we'd like to specify links to child routes (including index) without needing to pass around the params. E.g. navigate from `/accounts/$accountId/details` to `/accounts/$accountId/`.

We could just use absolute paths, and pass through the params for each link option.

FWIW this did used to work, the behaviour changed at some point between 1.124 and 1.130
you can just do this

```tsx
<Link from="/accounts/$accountId/details" to="/accounts/$accountId/">...</Link>
```

param should not be required to be passed in
@schiller-manuel the navigation lives in the parent (`/accounts/$accountId`).

Do you think it would be reasonable to change the types so `"."` is not possible when using a `from`? Just to prevent this confusion.
I had the same confusion (and note, this USED to work). My use case was that I have a route with some tabs on it.  The page that displays the top is handling the navigation when a tab is clicked. One of the tabs is an index page. 

The page with the tabs is `$stationId.tsx` and the tabs are things like `$stationId.foo.tsx` and `$stationId.index.tsx`.

I had code that used to work properly
```
const navigate = useNavigate({ from: '$stationId' })

function handleTabClick(tabId: string) {
  if (tabId === 'foo') {
    navigate({ to: './foo' })
  } else {
    navigate({ to: '.' })
  }
}
```

I was able to fix this by changing the `to: '.'` to use an absolute path but it does look like something broke and if it's undesirable to fix, I agree that it would be good to prevent the use of `'.'` in relative path navigation.  
I'm also experiencing this issue using a tabbed solution like the example defined [here](https://tanstack.com/router/latest/docs/framework/react/guide/type-utilities#type-checking-an-array-of-link-options-with-validatelinkoptionsarray) in the docs.

```ts
<Menu
  from="/posts"
  items={[{ to: '.' }, { to: './$postId', params: { postId: 'postId' } }]}
/>
```

Adding to that though when rendered using SSR with start the exact match logic seems to not work - but it does on client side navigation?

```ts
<Link
  {...item}
  from={props.from}
  activeOptions={{ exact: true, includeSearch: false }}
  activeProps={{ 'data-state': 'active' }}
  className="data-[state=active]:text-red-600"
>
```

<img width="210" height="95" alt="Image" src="https://github.com/user-attachments/assets/23e9ab92-be9d-4b99-a165-7a1a89109cad" />


https://stackblitz.com/edit/tanstack-router-1yctepja?file=src%2Fcomponents%2Fmenu.tsx,src%2Fmain.tsx,src%2Froutes%2Fitems%2Froute.tsx


I'm also experiencing this issue. Any progress on the PR?



Patch:
diff --git a/docs/router/framework/react/guide/navigation.md b/docs/router/framework/react/guide/navigation.md
index 26b8a02ce3f..3305a61bf3c 100644
--- a/docs/router/framework/react/guide/navigation.md
+++ b/docs/router/framework/react/guide/navigation.md
@@ -28,7 +28,7 @@ type ToOptions<
   TTo extends string = '',
 > = {
   // `from` is an optional route ID or path. If it is not supplied, only absolute paths will be auto-completed and type-safe. It's common to supply the route.fullPath of the origin route you are rendering from for convenience. If you don't know the origin route, leave this empty and work with absolute paths or unsafe relative paths.
-  from: string
+  from?: string
   // `to` can be an absolute route path or a relative path from the `from` option to a valid route path. ⚠️ Do not interpolate path params, hash or search params into the `to` options. Use the `params`, `search`, and `hash` options instead.
   to: string
   // `params` is either an object of path params to interpolate into the `to` option or a function that supplies the previous params and allows you to return new ones. This is the only way to interpolate dynamic parameters into the final URL. Depending on the `from` and `to` route, you may need to supply none, some or all of the path params. TypeScript will notify you of the required params if there are any.
@@ -183,7 +183,7 @@ Keep in mind that normally dynamic segment params are `string` values, but they
 
 By default, all links are absolute unless a `from` route path is provided. This means that the above link will always navigate to the `/about` route regardless of what route you are currently on.
 
-If you want to make a link that is relative to the current route, you can provide a `from` route path:
+Relative links can be combined with a `from` route path. If a from route path isn't provided, relative paths default to the current active location.
 
 ```tsx
 const postIdRoute = createRoute({
@@ -201,9 +201,9 @@ As seen above, it's common to provide the `route.fullPath` as the `from` route p
 
 ### Special relative paths: `"."` and `".."`
 
-Quite often you might want to reload the current location, for example, to rerun the loaders on the current and/or parent routes, or maybe there was a change in search parameters. This can be achieved by specifying a `to` route path of `"."` which will reload the current location. This is only applicable to the current location, and hence any `from` route path specified is ignored.
+Quite often you might want to reload the current location or another `from` path, for example, to rerun the loaders on the current and/or parent routes, or maybe navigate back to a parent route. This can be achieved by specifying a `to` route path of `"."` which will reload the current location or provided `from` path.
 
-Another common need is to navigate one route back relative to the current location or some other matched route in the current tree. By specifying a `to` route path of `".."` navigation will be resolved to either the first parent route preceding the current location or, if specified, preceding the `"from"` route path.
+Another common need is to navigate one route back relative to the current location or another path. By specifying a `to` route path of `".."` navigation will be resolved to the first parent route preceding the current location.
 
 ```tsx
 export const Route = createFileRoute('/posts/$postId')({
@@ -214,7 +214,14 @@ function PostComponent() {
   return (
     <div>
       <Link to=".">Reload the current route of /posts/$postId</Link>
-      <Link to="..">Navigate to /posts</Link>
+      <Link to="..">Navigate back to /posts</Link>
+      // the below are all equivalent
+      <Link to="/posts">Navigate back to /posts</Link>
+      <Link from="/posts" to=".">
+        Navigate back to /posts
+      </Link>
+      // the below are all equivalent
+      <Link to="/">Navigate to root</Link>
       <Link from="/posts" to="..">
         Navigate to root
       </Link>
diff --git a/packages/react-router/src/link.tsx b/packages/react-router/src/link.tsx
index c72d0e85678..6fea974ffeb 100644
--- a/packages/react-router/src/link.tsx
+++ b/packages/react-router/src/link.tsx
@@ -7,12 +7,12 @@ import {
   preloadWarning,
   removeTrailingSlash,
 } from '@tanstack/router-core'
+import { useActiveLocation } from './useActiveLocation'
 import { useRouterState } from './useRouterState'
 import { useRouter } from './useRouter'
 
 import { useForwardedRef, useIntersectionObserver } from './utils'
 
-import { useMatch } from './useMatch'
 import type {
   AnyRouter,
   Constrain,
@@ -99,19 +99,27 @@ export function useLinkProps<
     structuralSharing: true as any,
   })
 
-  const from = useMatch({
-    strict: false,
-    select: (match) => options.from ?? match.fullPath,
+  // subscribe to location here to re-build fromPath if it changes
+  const routerLocation = useRouterState({
+    select: (s) => s.location,
+    structuralSharing: true as any,
   })
 
-  const next = React.useMemo(
-    () => router.buildLocation({ ...options, from } as any),
+  const { getFromPath } = useActiveLocation()
+
+  const from = getFromPath(options.from)
+
+  const _options = React.useMemo(
+    () => {
+      return { ...options, from }
+    },
     // eslint-disable-next-line react-hooks/exhaustive-deps
     [
       router,
+      routerLocation,
       currentSearch,
-      options._fromLocation,
       from,
+      options._fromLocation,
       options.hash,
       options.to,
       options.search,
@@ -122,6 +130,11 @@ export function useLinkProps<
     ],
   )
 
+  const next = React.useMemo(
+    () => router.buildLocation({ ..._options } as any),
+    [router, _options],
+  )
+
   const isExternal = type === 'external'
 
   const preload =
@@ -180,34 +193,12 @@ export function useLinkProps<
     },
   })
 
-  const doPreload = React.useCallback(
-    () => {
-      router.preloadRoute({ ...options, from } as any).catch((err) => {
-        console.warn(err)
-        console.warn(preloadWarning)
-      })
-    },
-    // eslint-disable-next-line react-hooks/exhaustive-deps
-    [
-      router,
-      options.to,
-      options._fromLocation,
-      from,
-      options.search,
-      options.hash,
-      options.params,
-      options.state,
-      options.mask,
-      options.unsafeRelative,
-      options.hashScrollIntoView,
-      options.href,
-      options.ignoreBlocker,
-      options.reloadDocument,
-      options.replace,
-      options.resetScroll,
-      options.viewTransition,
-    ],
-  )
+  const doPreload = React.useCallback(() => {
+    router.preloadRoute({ ..._options } as any).catch((err) => {
+      console.warn(err)
+      console.warn(preloadWarning)
+    })
+  }, [router, _options])
 
   const preloadViewportIoCallback = React.useCallback(
     (entry: IntersectionObserverEntry | undefined) => {
@@ -235,25 +226,6 @@ export function useLinkProps<
     }
   }, [disabled, doPreload, preload])
 
-  if (isExternal) {
-    return {
-      ...propsSafeToSpread,
-      ref: innerRef as React.ComponentPropsWithRef<'a'>['ref'],
-      type,
-      href: to,
-      ...(children && { children }),
-      ...(target && { target }),
-      ...(disabled && { disabled }),
-      ...(style && { style }),
-      ...(className && { className }),
-      ...(onClick && { onClick }),
-      ...(onFocus && { onFocus }),
-      ...(onMouseEnter && { onMouseEnter }),
-      ...(onMouseLeave && { onMouseLeave }),
-      ...(onTouchStart && { onTouchStart }),
-    }
-  }
-
   // The click handler
   const handleClick = (e: React.MouseEvent) => {
     if (
@@ -277,8 +249,7 @@ export function useLinkProps<
       // All is well? Navigate!
       // N.B. we don't call `router.commitLocation(next) here because we want to run `validateSearch` before committing
       router.navigate({
-        ...options,
-        from,
+        ..._options,
         replace,
         resetScroll,
         hashScrollIntoView,
@@ -289,6 +260,25 @@ export function useLinkProps<
     }
   }
 
+  if (isExternal) {
+    return {
+      ...propsSafeToSpread,
+      ref: innerRef as React.ComponentPropsWithRef<'a'>['ref'],
+      type,
+      href: to,
+      ...(children && { children }),
+      ...(target && { target }),
+      ...(disabled && { disabled }),
+      ...(style && { style }),
+      ...(className && { className }),
+      ...(onClick && { onClick }),
+      ...(onFocus && { onFocus }),
+      ...(onMouseEnter && { onMouseEnter }),
+      ...(onMouseLeave && { onMouseLeave }),
+      ...(onTouchStart && { onTouchStart }),
+    }
+  }
+
   // The click handler
   const handleFocus = (_: React.MouseEvent) => {
     if (disabled) return
diff --git a/packages/react-router/src/useActiveLocation.ts b/packages/react-router/src/useActiveLocation.ts
new file mode 100644
index 00000000000..c7220fdaca0
--- /dev/null
+++ b/packages/react-router/src/useActiveLocation.ts
@@ -0,0 +1,57 @@
+import { last } from '@tanstack/router-core'
+import { useCallback, useEffect, useState } from 'react'
+import { useRouter } from './useRouter'
+import { useMatch } from './useMatch'
+import { useRouterState } from './useRouterState'
+import type { ParsedLocation } from '@tanstack/router-core'
+
+export type UseActiveLocationResult = {
+  activeLocation: ParsedLocation
+  getFromPath: (from?: string) => string
+  setActiveLocation: (location?: ParsedLocation) => void
+}
+
+export const useActiveLocation = (
+  location?: ParsedLocation,
+): UseActiveLocationResult => {
+  const router = useRouter()
+  const routerLocation = useRouterState({ select: (state) => state.location })
+  const [activeLocation, setActiveLocation] = useState<ParsedLocation>(
+    location ?? routerLocation,
+  )
+  const [customActiveLocation, setCustomActiveLocation] = useState<
+    ParsedLocation | undefined
+  >(location)
+
+  useEffect(() => {
+    setActiveLocation(customActiveLocation ?? routerLocation)
+  }, [routerLocation, customActiveLocation])
+
+  const matchIndex = useMatch({
+    strict: false,
+    select: (match) => match.index,
+  })
+
+  const getFromPath = useCallback(
+    (from?: string) => {
+      const activeLocationMatches = router.matchRoutes(activeLocation, {
+        _buildLocation: false,
+      })
+
+      const activeLocationMatch = last(activeLocationMatches)
+
+      return (
+        from ??
+        activeLocationMatch?.fullPath ??
+        router.state.matches[matchIndex]!.fullPath
+      )
+    },
+    [activeLocation, matchIndex, router],
+  )
+
+  return {
+    activeLocation,
+    getFromPath,
+    setActiveLocation: setCustomActiveLocation,
+  }
+}
diff --git a/packages/react-router/src/useNavigate.tsx b/packages/react-router/src/useNavigate.tsx
index 1fcef979673..c12cffb9c93 100644
--- a/packages/react-router/src/useNavigate.tsx
+++ b/packages/react-router/src/useNavigate.tsx
@@ -1,6 +1,6 @@
 import * as React from 'react'
 import { useRouter } from './useRouter'
-import { useMatch } from './useMatch'
+import { useActiveLocation } from './useActiveLocation'
 import type {
   AnyRouter,
   FromPathOption,
@@ -15,29 +15,21 @@ export function useNavigate<
 >(_defaultOpts?: {
   from?: FromPathOption<TRouter, TDefaultFrom>
 }): UseNavigateResult<TDefaultFrom> {
-  const { navigate, state } = useRouter()
+  const router = useRouter()
 
-  // Just get the index of the current match to avoid rerenders
-  // as much as possible
-  const matchIndex = useMatch({
-    strict: false,
-    select: (match) => match.index,
-  })
+  const { getFromPath, activeLocation } = useActiveLocation()
 
   return React.useCallback(
     (options: NavigateOptions) => {
-      const from =
-        options.from ??
-        _defaultOpts?.from ??
-        state.matches[matchIndex]!.fullPath
+      const from = getFromPath(options.from ?? _defaultOpts?.from)
 
-      return navigate({
+      return router.navigate({
         ...options,
         from,
       })
     },
     // eslint-disable-next-line react-hooks/exhaustive-deps
-    [_defaultOpts?.from, navigate],
+    [_defaultOpts?.from, router, getFromPath, activeLocation],
   ) as UseNavigateResult<TDefaultFrom>
 }
 
diff --git a/packages/router-core/src/router.ts b/packages/router-core/src/router.ts
index 6a132b57831..807c296abf9 100644
--- a/packages/router-core/src/router.ts
+++ b/packages/router-core/src/router.ts
@@ -1414,50 +1414,44 @@ export class RouterCore<
         _buildLocation: true,
       })
 
+      // Now let's find the starting pathname
+      // This should default to the current location if no from is provided
       const lastMatch = last(allCurrentLocationMatches)!
 
-      // First let's find the starting pathname
-      // By default, start with the current location
-      let fromPath = this.resolvePathWithBase(lastMatch.fullPath, '.')
-      const toPath = dest.to
-        ? this.resolvePathWithBase(fromPath, `${dest.to}`)
-        : this.resolvePathWithBase(fromPath, '.')
-
-      const routeIsChanging =
-        !!dest.to &&
-        !comparePaths(dest.to.toString(), fromPath) &&
-        !comparePaths(toPath, fromPath)
-
-      // If the route is changing we need to find the relative fromPath
-      if (dest.unsafeRelative === 'path') {
-        fromPath = currentLocation.pathname
-      } else if (routeIsChanging && dest.from) {
-        fromPath = dest.from
-
-        // do this check only on navigations during test or development
-        if (process.env.NODE_ENV !== 'production' && dest._isNavigate) {
-          const allFromMatches = this.getMatchedRoutes(
-            dest.from,
-            undefined,
-          ).matchedRoutes
+      // check that from path exists in the current route tree
+      // do this check only on navigations during test or development
+      if (
+        dest.from &&
+        process.env.NODE_ENV !== 'production' &&
+        dest._isNavigate
+      ) {
+        const allFromMatches = this.getMatchedRoutes(
+          dest.from,
+          undefined,
+        ).matchedRoutes
 
-          const matchedFrom = findLast(allCurrentLocationMatches, (d) => {
-            return comparePaths(d.fullPath, fromPath)
-          })
+        const matchedFrom = findLast(allCurrentLocationMatches, (d) => {
+          return comparePaths(d.fullPath, dest.from!)
+        })
 
-          const matchedCurrent = findLast(allFromMatches, (d) => {
-            return comparePaths(d.fullPath, currentLocation.pathname)
-          })
+        const matchedCurrent = findLast(allFromMatches, (d) => {
+          return comparePaths(d.fullPath, lastMatch.fullPath)
+        })
 
-          // for from to be invalid it shouldn't just be unmatched to currentLocation
-          // but the currentLocation should also be unmatched to from
-          if (!matchedFrom && !matchedCurrent) {
-            console.warn(`Could not find match for from: ${fromPath}`)
-          }
+        // for from to be invalid it shouldn't just be unmatched to currentLocation
+        // but the currentLocation should also be unmatched to from
+        if (!matchedFrom && !matchedCurrent) {
+          console.warn(`Could not find match for from: ${dest.from}`)
         }
       }
 
-      fromPath = this.resolvePathWithBase(fromPath, '.')
+      const defaultedFromPath =
+        dest.unsafeRelative === 'path'
+          ? currentLocation.pathname
+          : (dest.from ?? lastMatch.fullPath)
+
+      // ensure this includes the basePath if set
+      const fromPath = this.resolvePathWithBase(defaultedFromPath, '.')
 
       // From search should always use the current location
       const fromSearch = lastMatch.search
@@ -1465,6 +1459,7 @@ export class RouterCore<
       const fromParams = { ...lastMatch.params }
 
       // Resolve the next to
+      // ensure this includes the basePath if set
       const nextTo = dest.to
         ? this.resolvePathWithBase(fromPath, `${dest.to}`)
         : this.resolvePathWithBase(fromPath, '.')
diff --git a/packages/solid-router/src/link.tsx b/packages/solid-router/src/link.tsx
index 008483ac0d8..00c0c109972 100644
--- a/packages/solid-router/src/link.tsx
+++ b/packages/solid-router/src/link.tsx
@@ -15,7 +15,7 @@ import { useRouter } from './useRouter'
 
 import { useIntersectionObserver } from './utils'
 
-import { useMatch } from './useMatch'
+import { useActiveLocation } from './useActiveLocation'
 import type {
   AnyRouter,
   Constrain,
@@ -133,20 +133,20 @@ export function useLinkProps<
     select: (s) => s.location.searchStr,
   })
 
-  // when `from` is not supplied, use the route of the current match as the `from` location
-  // so relative routing works as expected
-  const from = useMatch({
-    strict: false,
-    select: (match) => options.from ?? match.fullPath,
-  })
+  const { getFromPath, activeLocation } = useActiveLocation()
 
-  const _options = () => ({
-    ...options,
-    from: from(),
-  })
+  const from = getFromPath(options.from)
+
+  const _options = () => {
+    return {
+      ...options,
+      from: from(),
+    }
+  }
 
   const next = Solid.createMemo(() => {
     currentSearch()
+    activeLocation()
     return router.buildLocation(_options() as any)
   })
 
diff --git a/packages/solid-router/src/useActiveLocation.ts b/packages/solid-router/src/useActiveLocation.ts
new file mode 100644
index 00000000000..268af7296dd
--- /dev/null
+++ b/packages/solid-router/src/useActiveLocation.ts
@@ -0,0 +1,61 @@
+import { last } from '@tanstack/router-core'
+import { createEffect, createMemo, createSignal } from 'solid-js'
+import { useMatch } from './useMatch'
+import { useRouter } from './useRouter'
+import { useRouterState } from './useRouterState'
+import type { Accessor } from 'solid-js'
+import type { ParsedLocation } from '@tanstack/router-core'
+
+export type UseLocationResult = {
+  activeLocation: Accessor<ParsedLocation>
+  getFromPath: (from?: string) => Accessor<string>
+  setActiveLocation: (location?: ParsedLocation) => void
+}
+
+export function useActiveLocation(
+  location?: ParsedLocation,
+): UseLocationResult {
+  const router = useRouter()
+  // we are not using a variable here for router state location since we need to only calculate that if the location is not passed in. It can result in unnecessary history actions if we do that.
+  const [activeLocation, setActiveLocation] = createSignal<ParsedLocation>(
+    location ?? useRouterState({ select: (s) => s.location })(),
+  )
+  const [customActiveLocation, setCustomActiveLocation] = createSignal<
+    ParsedLocation | undefined
+  >(location)
+
+  createEffect(() => {
+    setActiveLocation(
+      customActiveLocation() ?? useRouterState({ select: (s) => s.location })(),
+    )
+  })
+
+  const matchIndex = useMatch({
+    strict: false,
+    select: (match) => match.index,
+  })
+
+  const getFromPath = (from?: string) =>
+    createMemo(() => {
+      const activeLocationMatches = router.matchRoutes(
+        customActiveLocation() ?? activeLocation(),
+        {
+          _buildLocation: false,
+        },
+      )
+
+      const activeLocationMatch = last(activeLocationMatches)
+
+      return (
+        from ??
+        activeLocationMatch?.fullPath ??
+        router.state.matches[matchIndex()]!.fullPath
+      )
+    })
+
+  return {
+    activeLocation,
+    getFromPath,
+    setActiveLocation: setCustomActiveLocation,
+  }
+}
diff --git a/packages/solid-router/src/useNavigate.tsx b/packages/solid-router/src/useNavigate.tsx
index 96d1207533b..0a7f7437531 100644
--- a/packages/solid-router/src/useNavigate.tsx
+++ b/packages/solid-router/src/useNavigate.tsx
@@ -1,6 +1,6 @@
 import * as Solid from 'solid-js'
 import { useRouter } from './useRouter'
-import { useMatch } from './useMatch'
+import { useActiveLocation } from './useActiveLocation'
 import type {
   AnyRouter,
   FromPathOption,
@@ -15,20 +15,19 @@ export function useNavigate<
 >(_defaultOpts?: {
   from?: FromPathOption<TRouter, TDefaultFrom>
 }): UseNavigateResult<TDefaultFrom> {
-  const { navigate, state } = useRouter()
+  const router = useRouter()
 
-  const matchIndex = useMatch({
-    strict: false,
-    select: (match) => match.index,
-  })
+  const { getFromPath, setActiveLocation } = useActiveLocation(
+    router.latestLocation,
+  )
 
   return ((options: NavigateOptions) => {
-    return navigate({
+    setActiveLocation(router.latestLocation)
+    const from = getFromPath(options.from ?? _defaultOpts?.from)
+
+    return router.navigate({
       ...options,
-      from:
-        options.from ??
-        _defaultOpts?.from ??
-        state.matches[matchIndex()]!.fullPath,
+      from: from(),
     })
   }) as UseNavigateResult<TDefaultFrom>
 }


Test patch:
diff --git a/packages/react-router/tests/link.test.tsx b/packages/react-router/tests/link.test.tsx
index 80578b6f4a3..cb6124db79e 100644
--- a/packages/react-router/tests/link.test.tsx
+++ b/packages/react-router/tests/link.test.tsx
@@ -24,6 +24,7 @@ import {
   createRoute,
   createRouteMask,
   createRouter,
+  getRouteApi,
   redirect,
   retainSearchParams,
   stripSearchParams,
@@ -5073,7 +5074,9 @@ describe.each([{ basepath: '' }, { basepath: '/basepath' }])(
           return (
             <>
               <h1>Param Route</h1>
-              <Link to="./a">Link to ./a</Link>
+              <Link from={paramRoute.fullPath} to="./a">
+                Link to ./a
+              </Link>
               <Link to="c" unsafeRelative="path">
                 Link to c
               </Link>
@@ -5093,7 +5096,12 @@ describe.each([{ basepath: '' }, { basepath: '/basepath' }])(
           return (
             <>
               <h1>Param A Route</h1>
-              <Link to="..">Link to .. from /param/foo/a</Link>
+              <Link from={paramARoute.fullPath} to="..">
+                Link to .. from /param/foo/a
+              </Link>
+              <Link to=".." data-testid={'link-to-previous'}>
+                Link to .. from current active route
+              </Link>
               <Outlet />
             </>
           )
@@ -5280,6 +5288,27 @@ describe.each([{ basepath: '' }, { basepath: '/basepath' }])(
       expect(window.location.pathname).toBe(`${basepath}/param/foo`)
     })
 
+    test('should navigate to a parent link based on active location', async () => {
+      const router = setupRouter()
+
+      render(<RouterProvider router={router} />)
+
+      await act(async () => {
+        history.push(`${basepath}/param/foo/a/b`)
+      })
+
+      const relativeLink = await screen.findByTestId('link-to-previous')
+
+      expect(relativeLink.getAttribute('href')).toBe(`${basepath}/param/foo/a`)
+
+      // Click the link and ensure the new location
+      await act(async () => {
+        fireEvent.click(relativeLink)
+      })
+
+      expect(window.location.pathname).toBe(`${basepath}/param/foo/a`)
+    })
+
     test('should navigate to a child link based on pathname', async () => {
       const router = setupRouter()
 
@@ -5407,3 +5436,712 @@ describe.each([{ basepath: '' }, { basepath: '/basepath' }])(
     })
   },
 )
+
+describe('relative links to current route', () => {
+  test.each([true, false])(
+    'should navigate to current route when using "." in nested route structure from Index Route with trailingSlash: %s',
+    async (trailingSlash: boolean) => {
+      const tail = trailingSlash ? '/' : ''
+
+      const rootRoute = createRootRoute()
+
+      const IndexComponent = () => {
+        return (
+          <>
+            <Link data-testid="posts-link" to="/post">
+              Post
+            </Link>
+            <Link
+              data-testid="search-link"
+              to="."
+              search={{ param1: 'value1' }}
+            >
+              Search
+            </Link>
+            <Link
+              data-testid="search2-link"
+              to="."
+              search={{ param1: 'value2' }}
+            >
+              Search2
+            </Link>
+            <Outlet />
+          </>
+        )
+      }
+
+      const indexRoute = createRoute({
+        getParentRoute: () => rootRoute,
+        path: '/',
+        component: IndexComponent,
+        validateSearch: z.object({
+          param1: z.string().optional(),
+        }),
+      })
+
+      const postRoute = createRoute({
+        getParentRoute: () => indexRoute,
+        path: 'post',
+        component: () => (
+          <>
+            <div>Post</div>
+          </>
+        ),
+      })
+
+      const router = createRouter({
+        routeTree: rootRoute.addChildren([indexRoute, postRoute]),
+        history,
+        trailingSlash: trailingSlash ? 'always' : 'never',
+      })
+
+      render(<RouterProvider router={router} />)
+
+      const postButton = await screen.findByTestId('posts-link')
+      const searchButton = await screen.findByTestId('search-link')
+      const searchButton2 = await screen.findByTestId('search2-link')
+
+      await act(() => fireEvent.click(postButton))
+
+      expect(window.location.pathname).toBe(`/post${tail}`)
+
+      await act(() => fireEvent.click(searchButton))
+
+      expect(router.state.location.pathname).toBe(`/post${tail}`)
+      expect(router.state.location.search).toEqual({ param1: 'value1' })
+
+      await act(() => fireEvent.click(searchButton2))
+
+      expect(router.state.location.pathname).toBe(`/post${tail}`)
+      expect(router.state.location.search).toEqual({ param1: 'value2' })
+    },
+  )
+
+  test.each([true, false])(
+    'should navigate to current route with search params when using "." in nested route structure from Index Route with trailingSlash: %s',
+    async (trailingSlash: boolean) => {
+      const tail = trailingSlash ? '/' : ''
+
+      const rootRoute = createRootRoute()
+
+      const IndexComponent = () => {
+        return (
+          <>
+            <Link data-testid="posts-link" to="/post">
+              Post
+            </Link>
+            <Link
+              data-testid="search-link"
+              to="."
+              search={{ param1: 'value1' }}
+            >
+              Search
+            </Link>
+            <Link
+              data-testid="search2-link"
+              to="/post"
+              search={{ param1: 'value2' }}
+            >
+              Search2
+            </Link>
+            <Outlet />
+          </>
+        )
+      }
+
+      const indexRoute = createRoute({
+        getParentRoute: () => rootRoute,
+        path: '/',
+        component: IndexComponent,
+        validateSearch: z.object({
+          param1: z.string().optional(),
+        }),
+      })
+
+      const postRoute = createRoute({
+        getParentRoute: () => indexRoute,
+        path: 'post',
+        component: () => <div>Post</div>,
+      })
+
+      const router = createRouter({
+        routeTree: rootRoute.addChildren([indexRoute, postRoute]),
+        history,
+        trailingSlash: trailingSlash ? 'always' : 'never',
+      })
+
+      render(<RouterProvider router={router} />)
+
+      const postButton = await screen.findByTestId('posts-link')
+
+      await act(() => fireEvent.click(postButton))
+
+      expect(window.location.pathname).toBe(`/post${tail}`)
+
+      const searchButton = await screen.findByTestId('search-link')
+
+      await act(() => fireEvent.click(searchButton))
+
+      expect(router.state.location.pathname).toBe(`/post${tail}`)
+      expect(router.state.location.search).toEqual({ param1: 'value1' })
+
+      const searchButton2 = await screen.findByTestId('search2-link')
+
+      await act(() => fireEvent.click(searchButton2))
+
+      expect(router.state.location.pathname).toBe(`/post${tail}`)
+      expect(router.state.location.search).toEqual({ param1: 'value2' })
+    },
+  )
+
+  test.each([true, false])(
+    'should navigate to current route with changing path params when using "." in nested route structure with trailingSlash: %s',
+    async (trailingSlash) => {
+      const tail = trailingSlash ? '/' : ''
+      const rootRoute = createRootRoute()
+
+      const IndexComponent = () => {
+        return (
+          <>
+            <h1 data-testid="index-heading">Index</h1>
+            <Link data-testid="posts-link" to="/posts">
+              Posts
+            </Link>
+          </>
+        )
+      }
+
+      const indexRoute = createRoute({
+        getParentRoute: () => rootRoute,
+        path: '/',
+        component: IndexComponent,
+      })
+
+      const layoutRoute = createRoute({
+        getParentRoute: () => rootRoute,
+        id: '_layout',
+        component: () => {
+          return (
+            <>
+              <h1>Layout</h1>
+              <Outlet />
+            </>
+          )
+        },
+      })
+
+      const PostsComponent = () => {
+        return (
+          <>
+            <h1 data-testid="posts-index-heading">Posts</h1>
+            <Link
+              data-testid="first-post-link"
+              to="$postId"
+              params={{ postId: 'id1' }}
+            >
+              To first post
+            </Link>
+            <Link
+              data-testid="second-post-link"
+              to="."
+              params={{ postId: 'id2' }}
+            >
+              To second post
+            </Link>
+            <Outlet />
+          </>
+        )
+      }
+
+      const postsRoute = createRoute({
+        getParentRoute: () => layoutRoute,
+        path: 'posts',
+        component: PostsComponent,
+      })
+
+      const PostComponent = () => {
+        const params = useParams({ strict: false })
+        return (
+          <>
+            <span data-testid={`post-${params.postId}`}>
+              Params: {params.postId}
+            </span>
+          </>
+        )
+      }
+
+      const postRoute = createRoute({
+        getParentRoute: () => postsRoute,
+        path: '$postId',
+        component: PostComponent,
+      })
+
+      const router = createRouter({
+        routeTree: rootRoute.addChildren([
+          indexRoute,
+          layoutRoute.addChildren([postsRoute.addChildren([postRoute])]),
+        ]),
+        trailingSlash: trailingSlash ? 'always' : 'never',
+      })
+
+      render(<RouterProvider router={router} />)
+
+      const postsButton = await screen.findByTestId('posts-link')
+
+      await act(() => fireEvent.click(postsButton))
+
+      expect(
+        await screen.findByTestId('posts-index-heading'),
+      ).toBeInTheDocument()
+      expect(window.location.pathname).toEqual(`/posts${tail}`)
+
+      const firstPostButton = await screen.findByTestId('first-post-link')
+
+      await act(() => fireEvent.click(firstPostButton))
+
+      expect(await screen.findByTestId('post-id1')).toBeInTheDocument()
+      expect(window.location.pathname).toEqual(`/posts/id1${tail}`)
+
+      const secondPostButton = await screen.findByTestId('second-post-link')
+
+      await act(() => fireEvent.click(secondPostButton))
+
+      expect(await screen.findByTestId('post-id2')).toBeInTheDocument()
+      expect(window.location.pathname).toEqual(`/posts/id2${tail}`)
+    },
+  )
+})
+
+describe('relative links to from route', () => {
+  test.each([true, false])(
+    'should navigate to from route when using "." in nested route structure from Index Route with trailingSlash: %s',
+    async (trailingSlash: boolean) => {
+      const tail = trailingSlash ? '/' : ''
+
+      const rootRoute = createRootRoute()
+
+      const IndexComponent = () => {
+        return (
+          <>
+            <Link data-testid="posts-link" to="/post">
+              Post
+            </Link>
+            <Link
+              data-testid="search-link"
+              to="."
+              search={{ param1: 'value1' }}
+            >
+              Search
+            </Link>
+            <Link data-testid="home-link" from="/" to=".">
+              Go To Home
+            </Link>
+            <Outlet />
+          </>
+        )
+      }
+
+      const indexRoute = createRoute({
+        getParentRoute: () => rootRoute,
+        path: '/',
+        component: IndexComponent,
+        validateSearch: z.object({
+          param1: z.string().optional(),
+        }),
+      })
+
+      const postRoute = createRoute({
+        getParentRoute: () => indexRoute,
+        path: 'post',
+        component: () => <div>Post</div>,
+      })
+
+      const router = createRouter({
+        routeTree: rootRoute.addChildren([indexRoute, postRoute]),
+        history,
+        trailingSlash: trailingSlash ? 'always' : 'never',
+      })
+
+      render(<RouterProvider router={router} />)
+
+      const postButton = await screen.findByTestId('posts-link')
+
+      await act(() => fireEvent.click(postButton))
+
+      expect(router.state.location.pathname).toBe(`/post${tail}`)
+
+      const searchButton = await screen.findByTestId('search-link')
+
+      await act(() => fireEvent.click(searchButton))
+
+      expect(router.state.location.pathname).toBe(`/post${tail}`)
+      expect(router.state.location.search).toEqual({ param1: 'value1' })
+
+      const homeBtn = await screen.findByTestId('home-link')
+
+      await act(() => fireEvent.click(homeBtn))
+
+      expect(router.state.location.pathname).toBe(`/`)
+      expect(router.state.location.search).toEqual({})
+    },
+  )
+
+  test.each([true, false])(
+    'should navigate to from route with path params when using "." in nested route structure with trailingSlash: %s',
+    async (trailingSlash) => {
+      const tail = trailingSlash ? '/' : ''
+      const rootRoute = createRootRoute()
+
+      const IndexComponent = () => {
+        return (
+          <>
+            <h1 data-testid="index-heading">Index</h1>
+            <Link data-testid="posts-link" to="/posts">
+              Posts
+            </Link>
+          </>
+        )
+      }
+
+      const indexRoute = createRoute({
+        getParentRoute: () => rootRoute,
+        path: '/',
+        component: IndexComponent,
+      })
+
+      const layoutRoute = createRoute({
+        getParentRoute: () => rootRoute,
+        id: '_layout',
+        component: () => {
+          return (
+            <>
+              <h1>Layout</h1>
+              <Outlet />
+            </>
+          )
+        },
+      })
+
+      const PostsComponent = () => {
+        return (
+          <>
+            <h1 data-testid="posts-index-heading">Posts</h1>
+            <Link
+              data-testid="first-post-link"
+              to="$postId"
+              params={{ postId: '1' }}
+            >
+              To first post
+            </Link>
+            <Link
+              data-testid="second-post-link"
+              to="$postId"
+              params={{ postId: '2' }}
+            >
+              To second post
+            </Link>
+            <Link data-testid="to-posts-index-link" from="/posts" to=".">
+              To posts list
+            </Link>
+            <Outlet />
+          </>
+        )
+      }
+
+      const PostDetailComponent = () => {
+        return (
+          <>
+            <h1 data-testid="post-detail-index-heading">Post Detail</h1>
+            <Link data-testid="post-info-link" to="info">
+              To post info
+            </Link>
+            <Link data-testid="post-notes-link" to="notes">
+              To post notes
+            </Link>
+            <Link
+              data-testid="to-post-detail-index-link"
+              from="/posts/$postId"
+              to="."
+            >
+              To index detail options
+            </Link>
+            <Outlet />
+          </>
+        )
+      }
+
+      const PostInfoComponent = () => {
+        return (
+          <>
+            <h1 data-testid="post-info-heading">Post Info</h1>
+          </>
+        )
+      }
+
+      const PostNotesComponent = () => {
+        return (
+          <>
+            <h1 data-testid="post-notes-heading">Post Notes</h1>
+          </>
+        )
+      }
+
+      const postsRoute = createRoute({
+        getParentRoute: () => layoutRoute,
+        path: 'posts',
+        component: PostsComponent,
+      })
+
+      const postDetailRoute = createRoute({
+        getParentRoute: () => postsRoute,
+        path: '$postId',
+        component: PostDetailComponent,
+      })
+
+      const postInfoRoute = createRoute({
+        getParentRoute: () => postDetailRoute,
+        path: 'info',
+        component: PostInfoComponent,
+      })
+
+      const postNotesRoute = createRoute({
+        getParentRoute: () => postDetailRoute,
+        path: 'notes',
+        component: PostNotesComponent,
+      })
+
+      const router = createRouter({
+        routeTree: rootRoute.addChildren([
+          indexRoute,
+          layoutRoute.addChildren([
+            postsRoute.addChildren([
+              postDetailRoute.addChildren([postInfoRoute, postNotesRoute]),
+            ]),
+          ]),
+        ]),
+        trailingSlash: trailingSlash ? 'always' : 'never',
+      })
+
+      render(<RouterProvider router={router} />)
+
+      const postsButton = await screen.findByTestId('posts-link')
+
+      fireEvent.click(postsButton)
+
+      expect(
+        await screen.findByTestId('posts-index-heading'),
+      ).toBeInTheDocument()
+      expect(window.location.pathname).toEqual(`/posts${tail}`)
+
+      const firstPostButton = await screen.findByTestId('first-post-link')
+
+      fireEvent.click(firstPostButton)
+
+      expect(
+        await screen.findByTestId('post-detail-index-heading'),
+      ).toBeInTheDocument()
+      expect(window.location.pathname).toEqual(`/posts/1${tail}`)
+
+      const postInfoButton = await screen.findByTestId('post-info-link')
+
+      fireEvent.click(postInfoButton)
+
+      expect(await screen.findByTestId('post-info-heading')).toBeInTheDocument()
+      expect(window.location.pathname).toEqual(`/posts/1/info${tail}`)
+
+      const toPostDetailIndexButton = await screen.findByTestId(
+        'to-post-detail-index-link',
+      )
+
+      fireEvent.click(toPostDetailIndexButton)
+
+      expect(
+        await screen.findByTestId('post-detail-index-heading'),
+      ).toBeInTheDocument()
+      expect(screen.queryByTestId('post-info-heading')).not.toBeInTheDocument()
+      expect(window.location.pathname).toEqual(`/posts/1${tail}`)
+
+      const postNotesButton = await screen.findByTestId('post-notes-link')
+
+      fireEvent.click(postNotesButton)
+
+      expect(
+        await screen.findByTestId('post-notes-heading'),
+      ).toBeInTheDocument()
+      expect(window.location.pathname).toEqual(`/posts/1/notes${tail}`)
+
+      const toPostsIndexButton = await screen.findByTestId(
+        'to-posts-index-link',
+      )
+
+      fireEvent.click(toPostsIndexButton)
+
+      expect(
+        await screen.findByTestId('posts-index-heading'),
+      ).toBeInTheDocument()
+      expect(screen.queryByTestId('post-notes-heading')).not.toBeInTheDocument()
+      expect(
+        screen.queryByTestId('post-detail-index-heading'),
+      ).not.toBeInTheDocument()
+      expect(window.location.pathname).toEqual(`/posts${tail}`)
+
+      const secondPostButton = await screen.findByTestId('second-post-link')
+
+      fireEvent.click(secondPostButton)
+
+      expect(
+        await screen.findByTestId('post-detail-index-heading'),
+      ).toBeInTheDocument()
+      expect(window.location.pathname).toEqual(`/posts/2${tail}`)
+    },
+  )
+})
+
+describe('when on /posts/$postId and navigating to ../ with default `from` /posts', () => {
+  async function runTest(navigateVia: 'Route' | 'RouteApi') {
+    const rootRoute = createRootRoute()
+
+    const IndexComponent = () => {
+      return (
+        <>
+          <h1 data-testid="index-heading">Index</h1>
+          <Link data-testid="posts-link" to="/posts">
+            Posts
+          </Link>
+          <Link
+            data-testid="index-to-first-post-link"
+            to="/posts/$postId/details"
+            params={{ postId: 'id1' }}
+          >
+            To first post
+          </Link>
+        </>
+      )
+    }
+
+    const indexRoute = createRoute({
+      getParentRoute: () => rootRoute,
+      path: '/',
+      component: IndexComponent,
+    })
+
+    const layoutRoute = createRoute({
+      getParentRoute: () => rootRoute,
+      id: '_layout',
+      component: () => {
+        return (
+          <>
+            <h1>Layout</h1>
+            <Outlet />
+          </>
+        )
+      },
+    })
+
+    const PostsComponent = () => {
+      const LinkViaRoute = () => (
+        <Link from={postsRoute.fullPath} to="../" data-testid="link-to-home">
+          To Home
+        </Link>
+      )
+
+      const LinkViaRouteApi = () => {
+        const RouteApiLink = getRouteApi('/_layout/posts').Link
+        return (
+          <RouteApiLink to="../" data-testid="link-to-home">
+            To Home
+          </RouteApiLink>
+        )
+      }
+
+      return (
+        <>
+          <h1>Posts</h1>
+          {navigateVia === 'Route' ? <LinkViaRoute /> : <LinkViaRouteApi />}
+          <Outlet />
+        </>
+      )
+    }
+
+    const postsRoute = createRoute({
+      getParentRoute: () => layoutRoute,
+      path: 'posts',
+      component: PostsComponent,
+    })
+
+    const PostComponent = () => {
+      const params = useParams({ strict: false })
+      return (
+        <>
+          <span>Params: {params.postId}</span>
+          <Outlet />
+        </>
+      )
+    }
+
+    const postRoute = createRoute({
+      getParentRoute: () => postsRoute,
+      path: '$postId',
+      component: PostComponent,
+    })
+
+    const PostIndexComponent = () => {
+      return (
+        <>
+          <h1>Post Index</h1>
+        </>
+      )
+    }
+
+    const postIndexRoute = createRoute({
+      getParentRoute: () => postRoute,
+      path: '/',
+      component: PostIndexComponent,
+    })
+
+    const DetailsComponent = () => {
+      return (
+        <>
+          <h1 data-testid="details-heading">Details!</h1>
+        </>
+      )
+    }
+
+    const detailsRoute = createRoute({
+      getParentRoute: () => postRoute,
+      path: 'details',
+      component: DetailsComponent,
+    })
+
+    const router = createRouter({
+      routeTree: rootRoute.addChildren([
+        indexRoute,
+        layoutRoute.addChildren([
+          postsRoute.addChildren([
+            postRoute.addChildren([postIndexRoute, detailsRoute]),
+          ]),
+        ]),
+      ]),
+    })
+
+    render(<RouterProvider router={router} />)
+
+    const postsButton = await screen.findByTestId('index-to-first-post-link')
+
+    fireEvent.click(postsButton)
+
+    expect(await screen.findByTestId('details-heading')).toBeInTheDocument()
+
+    expect(window.location.pathname).toEqual('/posts/id1/details')
+
+    const homeButton = await screen.findByTestId('link-to-home')
+
+    fireEvent.click(homeButton)
+
+    expect(await screen.findByTestId('index-heading')).toBeInTheDocument()
+    expect(window.location.pathname).toEqual('/')
+  }
+
+  test('Route', () => runTest('Route'))
+  test('RouteApi', () => runTest('RouteApi'))
+})
diff --git a/packages/react-router/tests/useNavigate.test.tsx b/packages/react-router/tests/useNavigate.test.tsx
index 8d98abdf286..fa731c0b16b 100644
--- a/packages/react-router/tests/useNavigate.test.tsx
+++ b/packages/react-router/tests/useNavigate.test.tsx
@@ -1504,7 +1504,8 @@ test.each([true, false])(
     })
 
     const PostsComponent = () => {
-      const navigate = postsRoute.useNavigate()
+      const navigate = useNavigate()
+
       return (
         <>
           <h1 data-testid="posts-index-heading">Posts</h1>
@@ -1619,7 +1620,7 @@ test.each([true, false])(
     })
 
     const PostsComponent = () => {
-      const navigate = postsRoute.useNavigate()
+      const navigate = useNavigate()
       return (
         <>
           <h1 data-testid="posts-index-heading">Posts</h1>
@@ -1776,6 +1777,343 @@ test.each([true, false])(
   },
 )
 
+test.each([true, false])(
+  'should navigate to from route when using "." in nested route structure from Index Route with trailingSlash: %s',
+  async (trailingSlash: boolean) => {
+    const tail = trailingSlash ? '/' : ''
+
+    const rootRoute = createRootRoute()
+
+    const IndexComponent = () => {
+      const navigate = useNavigate()
+      return (
+        <>
+          <button
+            data-testid="posts-btn"
+            onClick={() => {
+              navigate({
+                to: '/post',
+              })
+            }}
+          >
+            Post
+          </button>
+          <button
+            data-testid="search-btn"
+            onClick={() =>
+              navigate({
+                to: '.',
+                search: {
+                  param1: 'value1',
+                },
+              })
+            }
+          >
+            Search
+          </button>
+          <button
+            data-testid="home-btn"
+            onClick={() =>
+              navigate({
+                from: '/',
+                to: '.',
+              })
+            }
+          >
+            Go To Home
+          </button>
+          <Outlet />
+        </>
+      )
+    }
+
+    const indexRoute = createRoute({
+      getParentRoute: () => rootRoute,
+      path: '/',
+      component: IndexComponent,
+      validateSearch: z.object({
+        param1: z.string().optional(),
+      }),
+    })
+
+    const postRoute = createRoute({
+      getParentRoute: () => indexRoute,
+      path: 'post',
+      component: () => <div>Post</div>,
+    })
+
+    const router = createRouter({
+      routeTree: rootRoute.addChildren([indexRoute, postRoute]),
+      history,
+      trailingSlash: trailingSlash ? 'always' : 'never',
+    })
+
+    render(<RouterProvider router={router} />)
+
+    const postButton = await screen.findByTestId('posts-btn')
+
+    await act(() => fireEvent.click(postButton))
+
+    expect(router.state.location.pathname).toBe(`/post${tail}`)
+
+    const searchButton = await screen.findByTestId('search-btn')
+
+    await act(() => fireEvent.click(searchButton))
+
+    expect(router.state.location.pathname).toBe(`/post${tail}`)
+    expect(router.state.location.search).toEqual({ param1: 'value1' })
+
+    const homeBtn = await screen.findByTestId('home-btn')
+
+    await act(() => fireEvent.click(homeBtn))
+
+    expect(router.state.location.pathname).toBe(`/`)
+    expect(router.state.location.search).toEqual({})
+  },
+)
+
+test.each([true, false])(
+  'should navigate to from route with path params when using "." in nested route structure with trailingSlash: %s',
+  async (trailingSlash) => {
+    const tail = trailingSlash ? '/' : ''
+    const rootRoute = createRootRoute()
+
+    const IndexComponent = () => {
+      const navigate = useNavigate()
+      return (
+        <>
+          <h1 data-testid="index-heading">Index</h1>
+          <button
+            data-testid="posts-btn"
+            onClick={() => navigate({ to: '/posts' })}
+          >
+            Posts
+          </button>
+        </>
+      )
+    }
+
+    const indexRoute = createRoute({
+      getParentRoute: () => rootRoute,
+      path: '/',
+      component: IndexComponent,
+    })
+
+    const layoutRoute = createRoute({
+      getParentRoute: () => rootRoute,
+      id: '_layout',
+      component: () => {
+        return (
+          <>
+            <h1>Layout</h1>
+            <Outlet />
+          </>
+        )
+      },
+    })
+
+    const PostsComponent = () => {
+      const navigate = postsRoute.useNavigate()
+      return (
+        <>
+          <h1 data-testid="posts-index-heading">Posts</h1>
+          <button
+            data-testid="first-post-btn"
+            onClick={() =>
+              navigate({
+                to: '$postId',
+                params: { postId: '1' },
+              })
+            }
+          >
+            To first post
+          </button>
+          <button
+            data-testid="second-post-btn"
+            onClick={() =>
+              navigate({
+                to: '$postId',
+                params: { postId: '2' },
+              })
+            }
+          >
+            To second post
+          </button>
+          <button
+            data-testid="to-posts-index-btn"
+            onClick={() =>
+              navigate({
+                from: '/posts',
+                to: '.',
+              })
+            }
+          >
+            To posts list
+          </button>
+          <Outlet />
+        </>
+      )
+    }
+
+    const PostDetailComponent = () => {
+      const navigate = postDetailRoute.useNavigate()
+      return (
+        <>
+          <h1 data-testid="post-detail-index-heading">Post Detail</h1>
+          <button
+            data-testid="post-info-btn"
+            onClick={() =>
+              navigate({
+                to: 'info',
+              })
+            }
+          >
+            To post info
+          </button>
+          <button
+            data-testid="post-notes-btn"
+            onClick={() =>
+              navigate({
+                to: 'notes',
+              })
+            }
+          >
+            To post notes
+          </button>
+          <button
+            data-testid="to-post-detail-index-btn"
+            onClick={() =>
+              navigate({
+                from: '/posts/$postId',
+                to: '.',
+              })
+            }
+          >
+            To index detail options
+          </button>
+          <Outlet />
+        </>
+      )
+    }
+
+    const PostInfoComponent = () => {
+      return (
+        <>
+          <h1 data-testid="post-info-heading">Post Info</h1>
+        </>
+      )
+    }
+
+    const PostNotesComponent = () => {
+      return (
+        <>
+          <h1 data-testid="post-notes-heading">Post Notes</h1>
+        </>
+      )
+    }
+
+    const postsRoute = createRoute({
+      getParentRoute: () => layoutRoute,
+      path: 'posts',
+      component: PostsComponent,
+    })
+
+    const postDetailRoute = createRoute({
+      getParentRoute: () => postsRoute,
+      path: '$postId',
+      component: PostDetailComponent,
+    })
+
+    const postInfoRoute = createRoute({
+      getParentRoute: () => postDetailRoute,
+      path: 'info',
+      component: PostInfoComponent,
+    })
+
+    const postNotesRoute = createRoute({
+      getParentRoute: () => postDetailRoute,
+      path: 'notes',
+      component: PostNotesComponent,
+    })
+
+    const router = createRouter({
+      routeTree: rootRoute.addChildren([
+        indexRoute,
+        layoutRoute.addChildren([
+          postsRoute.addChildren([
+            postDetailRoute.addChildren([postInfoRoute, postNotesRoute]),
+          ]),
+        ]),
+      ]),
+      trailingSlash: trailingSlash ? 'always' : 'never',
+    })
+
+    render(<RouterProvider router={router} />)
+
+    const postsButton = await screen.findByTestId('posts-btn')
+
+    fireEvent.click(postsButton)
+
+    expect(await screen.findByTestId('posts-index-heading')).toBeInTheDocument()
+    expect(window.location.pathname).toEqual(`/posts${tail}`)
+
+    const firstPostButton = await screen.findByTestId('first-post-btn')
+
+    fireEvent.click(firstPostButton)
+
+    expect(
+      await screen.findByTestId('post-detail-index-heading'),
+    ).toBeInTheDocument()
+    expect(window.location.pathname).toEqual(`/posts/1${tail}`)
+
+    const postInfoButton = await screen.findByTestId('post-info-btn')
+
+    fireEvent.click(postInfoButton)
+
+    expect(await screen.findByTestId('post-info-heading')).toBeInTheDocument()
+    expect(window.location.pathname).toEqual(`/posts/1/info${tail}`)
+
+    const toPostDetailIndexButton = await screen.findByTestId(
+      'to-post-detail-index-btn',
+    )
+
+    fireEvent.click(toPostDetailIndexButton)
+
+    expect(
+      await screen.findByTestId('post-detail-index-heading'),
+    ).toBeInTheDocument()
+    expect(screen.queryByTestId('post-info-heading')).not.toBeInTheDocument()
+    expect(window.location.pathname).toEqual(`/posts/1${tail}`)
+
+    const postNotesButton = await screen.findByTestId('post-notes-btn')
+
+    fireEvent.click(postNotesButton)
+
+    expect(await screen.findByTestId('post-notes-heading')).toBeInTheDocument()
+    expect(window.location.pathname).toEqual(`/posts/1/notes${tail}`)
+
+    const toPostsIndexButton = await screen.findByTestId('to-posts-index-btn')
+
+    fireEvent.click(toPostsIndexButton)
+
+    expect(await screen.findByTestId('posts-index-heading')).toBeInTheDocument()
+    expect(screen.queryByTestId('post-notes-heading')).not.toBeInTheDocument()
+    expect(
+      screen.queryByTestId('post-detail-index-heading'),
+    ).not.toBeInTheDocument()
+    expect(window.location.pathname).toEqual(`/posts${tail}`)
+
+    const secondPostButton = await screen.findByTestId('second-post-btn')
+
+    fireEvent.click(secondPostButton)
+
+    expect(
+      await screen.findByTestId('post-detail-index-heading'),
+    ).toBeInTheDocument()
+    expect(window.location.pathname).toEqual(`/posts/2${tail}`)
+  },
+)
+
 describe('when on /posts/$postId and navigating to ../ with default `from` /posts', () => {
   async function runTest(navigateVia: 'Route' | 'RouteApi') {
     const rootRoute = createRootRoute()
@@ -1975,7 +2313,11 @@ describe.each([{ basepath: '' }, { basepath: '/basepath' }])(
           return (
             <>
               <h1>Param Route</h1>
-              <button onClick={() => navigate({ to: './a' })}>
+              <button
+                onClick={() =>
+                  navigate({ from: paramRoute.fullPath, to: './a' })
+                }
+              >
                 Link to ./a
               </button>
               <button
@@ -1997,9 +2339,19 @@ describe.each([{ basepath: '' }, { basepath: '/basepath' }])(
           return (
             <>
               <h1>Param A Route</h1>
-              <button onClick={() => navigate({ to: '..' })}>
+              <button
+                onClick={() =>
+                  navigate({ from: paramARoute.fullPath, to: '..' })
+                }
+              >
                 Link to .. from /param/foo/a
               </button>
+              <button
+                onClick={() => navigate({ to: '..' })}
+                data-testid={'link-to-previous'}
+              >
+                Link to .. from current active route
+              </button>
               <Outlet />
             </>
           )
@@ -2150,6 +2502,25 @@ describe.each([{ basepath: '' }, { basepath: '/basepath' }])(
       expect(window.location.pathname).toBe(`${basepath}/param/foo`)
     })
 
+    test('should navigate to a parent link based on active location', async () => {
+      const router = setupRouter()
+
+      render(<RouterProvider router={router} />)
+
+      await act(async () => {
+        history.push(`${basepath}/param/foo/a/b`)
+      })
+
+      const relativeLink = await screen.findByTestId('link-to-previous')
+
+      // Click the link and ensure the new location
+      await act(async () => {
+        fireEvent.click(relativeLink)
+      })
+
+      expect(window.location.pathname).toBe(`${basepath}/param/foo/a`)
+    })
+
     test('should navigate to same route with different params', async () => {
       const router = setupRouter()
 
diff --git a/packages/solid-router/tests/link.test.tsx b/packages/solid-router/tests/link.test.tsx
index 9eba08fd094..ae751d4386f 100644
--- a/packages/solid-router/tests/link.test.tsx
+++ b/packages/solid-router/tests/link.test.tsx
@@ -4636,7 +4636,9 @@ describe.each([{ basepath: '' }, { basepath: '/basepath' }])(
           return (
             <>
               <h1>Param Route</h1>
-              <Link to="./a">Link to ./a</Link>
+              <Link from={paramRoute.fullPath} to="./a">
+                Link to ./a
+              </Link>
               <Link to="c" unsafeRelative="path">
                 Link to c
               </Link>
@@ -4656,7 +4658,12 @@ describe.each([{ basepath: '' }, { basepath: '/basepath' }])(
           return (
             <>
               <h1>Param A Route</h1>
-              <Link to="..">Link to .. from /param/foo/a</Link>
+              <Link from={paramARoute.fullPath} to="..">
+                Link to .. from /param/foo/a
+              </Link>
+              <Link to=".." data-testid={'link-to-previous'}>
+                Link to .. from current active route
+              </Link>
               <Outlet />
             </>
           )
@@ -4833,6 +4840,25 @@ describe.each([{ basepath: '' }, { basepath: '/basepath' }])(
       )
     })
 
+    test('should navigate to a parent link based on active location', async () => {
+      const router = setupRouter()
+
+      render(() => <RouterProvider router={router} />)
+
+      window.history.replaceState(null, 'root', `${basepath}/param/foo/a/b`)
+
+      const relativeLink = await screen.findByTestId('link-to-previous')
+
+      expect(relativeLink.getAttribute('href')).toBe(`${basepath}/param/foo/a`)
+
+      // Click the link and ensure the new location
+      fireEvent.click(relativeLink)
+
+      await waitFor(() =>
+        expect(window.location.pathname).toBe(`${basepath}/param/foo/a`),
+      )
+    })
+
     test('should navigate to a child link based on pathname', async () => {
       const router = setupRouter()
 
@@ -4937,9 +4963,600 @@ describe.each([{ basepath: '' }, { basepath: '/basepath' }])(
 
       fireEvent.click(parentLink)
 
-      waitFor(() =>
+      await waitFor(() =>
         expect(window.location.pathname).toBe(`${basepath}/param/bar/a/b`),
       )
     })
   },
 )
+
+describe('relative links to current route', () => {
+  test.each([true, false])(
+    'should navigate to current route when using "." in nested route structure from Index Route with trailingSlash: %s',
+    async (trailingSlash: boolean) => {
+      const tail = trailingSlash ? '/' : ''
+
+      const rootRoute = createRootRoute()
+
+      const IndexComponent = () => {
+        return (
+          <>
+            <Link data-testid="posts-link" to="/post">
+              Post
+            </Link>
+            <Link
+              data-testid="search-link"
+              to="."
+              search={{ param1: 'value1' }}
+            >
+              Search
+            </Link>
+            <Link
+              data-testid="search2-link"
+              to="."
+              search={{ param1: 'value2' }}
+            >
+              Search2
+            </Link>
+            <Outlet />
+          </>
+        )
+      }
+
+      const indexRoute = createRoute({
+        getParentRoute: () => rootRoute,
+        path: '/',
+        component: IndexComponent,
+        validateSearch: z.object({
+          param1: z.string().optional(),
+        }),
+      })
+
+      const postRoute = createRoute({
+        getParentRoute: () => indexRoute,
+        path: 'post',
+        component: () => (
+          <>
+            <div>Post</div>
+          </>
+        ),
+      })
+
+      const router = createRouter({
+        routeTree: rootRoute.addChildren([indexRoute, postRoute]),
+        history,
+        trailingSlash: trailingSlash ? 'always' : 'never',
+      })
+
+      render(() => <RouterProvider router={router} />)
+
+      const postButton = await screen.findByTestId('posts-link')
+      const searchButton = await screen.findByTestId('search-link')
+      const searchButton2 = await screen.findByTestId('search2-link')
+
+      fireEvent.click(postButton)
+
+      await waitFor(() => {
+        expect(window.location.pathname).toBe(`/post${tail}`)
+      })
+
+      fireEvent.click(searchButton)
+
+      await waitFor(() => {
+        expect(router.state.location.pathname).toBe(`/post${tail}`)
+        expect(router.state.location.search).toEqual({ param1: 'value1' })
+      })
+
+      fireEvent.click(searchButton2)
+
+      await waitFor(() => {
+        expect(router.state.location.pathname).toBe(`/post${tail}`)
+        expect(router.state.location.search).toEqual({ param1: 'value2' })
+      })
+    },
+  )
+
+  test.each([true, false])(
+    'should navigate to current route with search params when using "." in nested route structure from Index Route with trailingSlash: %s',
+    async (trailingSlash: boolean) => {
+      const tail = trailingSlash ? '/' : ''
+
+      const rootRoute = createRootRoute()
+
+      const IndexComponent = () => {
+        return (
+          <>
+            <Link data-testid="posts-link" to="/post">
+              Post
+            </Link>
+            <Link
+              data-testid="search-link"
+              to="."
+              search={{ param1: 'value1' }}
+            >
+              Search
+            </Link>
+            <Link
+              data-testid="search2-link"
+              to="/post"
+              search={{ param1: 'value2' }}
+            >
+              Search2
+            </Link>
+            <Outlet />
+          </>
+        )
+      }
+
+      const indexRoute = createRoute({
+        getParentRoute: () => rootRoute,
+        path: '/',
+        component: IndexComponent,
+        validateSearch: z.object({
+          param1: z.string().optional(),
+        }),
+      })
+
+      const postRoute = createRoute({
+        getParentRoute: () => indexRoute,
+        path: 'post',
+        component: () => <div>Post</div>,
+      })
+
+      const router = createRouter({
+        routeTree: rootRoute.addChildren([indexRoute, postRoute]),
+        history,
+        trailingSlash: trailingSlash ? 'always' : 'never',
+      })
+
+      render(() => <RouterProvider router={router} />)
+
+      const postButton = await screen.findByTestId('posts-link')
+
+      fireEvent.click(postButton)
+
+      await waitFor(() => {
+        expect(window.location.pathname).toBe(`/post${tail}`)
+      })
+
+      const searchButton = await screen.findByTestId('search-link')
+
+      fireEvent.click(searchButton)
+
+      await waitFor(() => {
+        expect(router.state.location.pathname).toBe(`/post${tail}`)
+        expect(router.state.location.search).toEqual({ param1: 'value1' })
+      })
+
+      const searchButton2 = await screen.findByTestId('search2-link')
+
+      fireEvent.click(searchButton2)
+
+      await waitFor(() => {
+        expect(router.state.location.pathname).toBe(`/post${tail}`)
+        expect(router.state.location.search).toEqual({ param1: 'value2' })
+      })
+    },
+  )
+
+  test.each([true, false])(
+    'should navigate to current route with changing path params when using "." in nested route structure with trailingSlash: %s',
+    async (trailingSlash) => {
+      const tail = trailingSlash ? '/' : ''
+      const rootRoute = createRootRoute()
+
+      const IndexComponent = () => {
+        return (
+          <>
+            <h1 data-testid="index-heading">Index</h1>
+            <Link data-testid="posts-link" to="/posts">
+              Posts
+            </Link>
+          </>
+        )
+      }
+
+      const indexRoute = createRoute({
+        getParentRoute: () => rootRoute,
+        path: '/',
+        component: IndexComponent,
+      })
+
+      const layoutRoute = createRoute({
+        getParentRoute: () => rootRoute,
+        id: '_layout',
+        component: () => {
+          return (
+            <>
+              <h1>Layout</h1>
+              <Outlet />
+            </>
+          )
+        },
+      })
+
+      const PostsComponent = () => {
+        return (
+          <>
+            <h1 data-testid="posts-index-heading">Posts</h1>
+            <Link
+              data-testid="first-post-link"
+              to="$postId"
+              params={{ postId: 'id1' }}
+            >
+              To first post
+            </Link>
+            <Link
+              data-testid="second-post-link"
+              to="."
+              params={{ postId: 'id2' }}
+            >
+              To second post
+            </Link>
+            <Outlet />
+          </>
+        )
+      }
+
+      const postsRoute = createRoute({
+        getParentRoute: () => layoutRoute,
+        path: 'posts',
+        component: PostsComponent,
+      })
+
+      const PostComponent = () => {
+        const params = useParams({ strict: false })
+        return (
+          <>
+            <span data-testid={`post-${params().postId}`}>
+              Params: {params().postId}
+            </span>
+          </>
+        )
+      }
+
+      const postRoute = createRoute({
+        getParentRoute: () => postsRoute,
+        path: '$postId',
+        component: PostComponent,
+      })
+
+      const router = createRouter({
+        routeTree: rootRoute.addChildren([
+          indexRoute,
+          layoutRoute.addChildren([postsRoute.addChildren([postRoute])]),
+        ]),
+        trailingSlash: trailingSlash ? 'always' : 'never',
+      })
+
+      render(() => <RouterProvider router={router} />)
+
+      const postsButton = await screen.findByTestId('posts-link')
+
+      fireEvent.click(postsButton)
+
+      await waitFor(() => {
+        expect(window.location.pathname).toEqual(`/posts${tail}`)
+      })
+
+      const firstPostButton = await screen.findByTestId('first-post-link')
+
+      fireEvent.click(firstPostButton)
+
+      await waitFor(() => {
+        expect(window.location.pathname).toEqual(`/posts/id1${tail}`)
+      })
+
+      const secondPostButton = await screen.findByTestId('second-post-link')
+
+      fireEvent.click(secondPostButton)
+
+      await waitFor(() => {
+        expect(window.location.pathname).toEqual(`/posts/id2${tail}`)
+      })
+    },
+  )
+})
+
+describe('relative links to from route', () => {
+  test.each([true, false])(
+    'should navigate to from route when using "." in nested route structure from Index Route with trailingSlash: %s',
+    async (trailingSlash: boolean) => {
+      const tail = trailingSlash ? '/' : ''
+
+      const rootRoute = createRootRoute()
+
+      const IndexComponent = () => {
+        return (
+          <>
+            <Link data-testid="posts-link" to="/post">
+              Post
+            </Link>
+            <Link
+              data-testid="search-link"
+              to="."
+              search={{ param1: 'value1' }}
+            >
+              Search
+            </Link>
+            <Link data-testid="home-link" from="/" to=".">
+              Go To Home
+            </Link>
+            <Outlet />
+          </>
+        )
+      }
+
+      const indexRoute = createRoute({
+        getParentRoute: () => rootRoute,
+        path: '/',
+        component: IndexComponent,
+        validateSearch: z.object({
+          param1: z.string().optional(),
+        }),
+      })
+
+      const postRoute = createRoute({
+        getParentRoute: () => indexRoute,
+        path: 'post',
+        component: () => <div>Post</div>,
+      })
+
+      const router = createRouter({
+        routeTree: rootRoute.addChildren([indexRoute, postRoute]),
+        history,
+        trailingSlash: trailingSlash ? 'always' : 'never',
+      })
+
+      render(() => <RouterProvider router={router} />)
+
+      const postButton = await screen.findByTestId('posts-link')
+
+      fireEvent.click(postButton)
+
+      await waitFor(() => {
+        expect(router.state.location.pathname).toBe(`/post${tail}`)
+      })
+
+      const searchButton = await screen.findByTestId('search-link')
+
+      fireEvent.click(searchButton)
+
+      await waitFor(() => {
+        expect(router.state.location.pathname).toBe(`/post${tail}`)
+        expect(router.state.location.search).toEqual({ param1: 'value1' })
+      })
+
+      const homeBtn = await screen.findByTestId('home-link')
+
+      fireEvent.click(homeBtn)
+
+      await waitFor(() => {
+        expect(router.state.location.pathname).toBe(`/`)
+        expect(router.state.location.search).toEqual({})
+      })
+    },
+  )
+
+  test.each([true, false])(
+    'should navigate to from route with path params when using "." in nested route structure with trailingSlash: %s',
+    async (trailingSlash) => {
+      const tail = trailingSlash ? '/' : ''
+      const rootRoute = createRootRoute()
+
+      const IndexComponent = () => {
+        return (
+          <>
+            <h1 data-testid="index-heading">Index</h1>
+            <Link data-testid="posts-link" to="/posts">
+              Posts
+            </Link>
+          </>
+        )
+      }
+
+      const indexRoute = createRoute({
+        getParentRoute: () => rootRoute,
+        path: '/',
+        component: IndexComponent,
+      })
+
+      const layoutRoute = createRoute({
+        getParentRoute: () => rootRoute,
+        id: '_layout',
+        component: () => {
+          return (
+            <>
+              <h1>Layout</h1>
+              <Outlet />
+            </>
+          )
+        },
+      })
+
+      const PostsComponent = () => {
+        return (
+          <>
+            <h1 data-testid="posts-index-heading">Posts</h1>
+            <Link
+              data-testid="first-post-link"
+              to="$postId"
+              params={{ postId: '1' }}
+            >
+              To first post
+            </Link>
+            <Link
+              data-testid="second-post-link"
+              to="$postId"
+              params={{ postId: '2' }}
+            >
+              To second post
+            </Link>
+            <Link data-testid="to-posts-index-link" from="/posts" to=".">
+              To posts list
+            </Link>
+            <Outlet />
+          </>
+        )
+      }
+
+      const PostDetailComponent = () => {
+        return (
+          <>
+            <h1 data-testid="post-detail-index-heading">Post Detail</h1>
+            <Link data-testid="post-info-link" to="info">
+              To post info
+            </Link>
+            <Link data-testid="post-notes-link" to="notes">
+              To post notes
+            </Link>
+            <Link
+              data-testid="to-post-detail-index-link"
+              from="/posts/$postId"
+              to="."
+            >
+              To index detail options
+            </Link>
+            <Outlet />
+          </>
+        )
+      }
+
+      const PostInfoComponent = () => {
+        return (
+          <>
+            <h1 data-testid="post-info-heading">Post Info</h1>
+          </>
+        )
+      }
+
+      const PostNotesComponent = () => {
+        return (
+          <>
+            <h1 data-testid="post-notes-heading">Post Notes</h1>
+          </>
+        )
+      }
+
+      const postsRoute = createRoute({
+        getParentRoute: () => layoutRoute,
+        path: 'posts',
+        component: PostsComponent,
+      })
+
+      const postDetailRoute = createRoute({
+        getParentRoute: () => postsRoute,
+        path: '$postId',
+        component: PostDetailComponent,
+      })
+
+      const postInfoRoute = createRoute({
+        getParentRoute: () => postDetailRoute,
+        path: 'info',
+        component: PostInfoComponent,
+      })
+
+      const postNotesRoute = createRoute({
+        getParentRoute: () => postDetailRoute,
+        path: 'notes',
+        component: PostNotesComponent,
+      })
+
+      const router = createRouter({
+        routeTree: rootRoute.addChildren([
+          indexRoute,
+          layoutRoute.addChildren([
+            postsRoute.addChildren([
+              postDetailRoute.addChildren([postInfoRoute, postNotesRoute]),
+            ]),
+          ]),
+        ]),
+        trailingSlash: trailingSlash ? 'always' : 'never',
+      })
+
+      render(() => <RouterProvider router={router} />)
+
+      const postsButton = await screen.findByTestId('posts-link')
+
+      fireEvent.click(postsButton)
+
+      await waitFor(() => {
+        expect(screen.queryByTestId('posts-index-heading')).toBeInTheDocument()
+        expect(window.location.pathname).toEqual(`/posts${tail}`)
+      })
+
+      const firstPostButton = await screen.findByTestId('first-post-link')
+
+      fireEvent.click(firstPostButton)
+
+      await waitFor(() => {
+        expect(
+          screen.queryByTestId('post-detail-index-heading'),
+        ).toBeInTheDocument()
+        expect(window.location.pathname).toEqual(`/posts/1${tail}`)
+      })
+
+      const postInfoButton = await screen.findByTestId('post-info-link')
+
+      fireEvent.click(postInfoButton)
+
+      await waitFor(() => {
+        expect(screen.queryByTestId('post-info-heading')).toBeInTheDocument()
+        expect(window.location.pathname).toEqual(`/posts/1/info${tail}`)
+      })
+
+      const toPostDetailIndexButton = await screen.findByTestId(
+        'to-post-detail-index-link',
+      )
+
+      fireEvent.click(toPostDetailIndexButton)
+
+      await waitFor(() => {
+        expect(
+          screen.queryByTestId('post-detail-index-heading'),
+        ).toBeInTheDocument()
+        expect(
+          screen.queryByTestId('post-info-heading'),
+        ).not.toBeInTheDocument()
+        expect(window.location.pathname).toEqual(`/posts/1${tail}`)
+      })
+
+      const postNotesButton = await screen.findByTestId('post-notes-link')
+
+      fireEvent.click(postNotesButton)
+
+      await waitFor(() => {
+        expect(screen.queryByTestId('post-notes-heading')).toBeInTheDocument()
+        expect(window.location.pathname).toEqual(`/posts/1/notes${tail}`)
+      })
+
+      const toPostsIndexButton = await screen.findByTestId(
+        'to-posts-index-link',
+      )
+
+      fireEvent.click(toPostsIndexButton)
+
+      await waitFor(() => {
+        expect(screen.queryByTestId('posts-index-heading')).toBeInTheDocument()
+        expect(
+          screen.queryByTestId('post-notes-heading'),
+        ).not.toBeInTheDocument()
+        expect(
+          screen.queryByTestId('post-detail-index-heading'),
+        ).not.toBeInTheDocument()
+        expect(window.location.pathname).toEqual(`/posts${tail}`)
+      })
+
+      const secondPostButton = await screen.findByTestId('second-post-link')
+
+      fireEvent.click(secondPostButton)
+
+      await waitFor(() => {
+        expect(
+          screen.queryByTestId('post-detail-index-heading'),
+        ).toBeInTheDocument()
+        expect(window.location.pathname).toEqual(`/posts/2${tail}`)
+      })
+    },
+  )
+})
diff --git a/packages/solid-router/tests/useNavigate.test.tsx b/packages/solid-router/tests/useNavigate.test.tsx
index cf9ef4831db..d3eb186cb21 100644
--- a/packages/solid-router/tests/useNavigate.test.tsx
+++ b/packages/solid-router/tests/useNavigate.test.tsx
@@ -1,6 +1,6 @@
 import * as Solid from 'solid-js'
 import '@testing-library/jest-dom/vitest'
-import { afterEach, describe, expect, test } from 'vitest'
+import { afterEach, beforeEach, describe, expect, test } from 'vitest'
 import {
   cleanup,
   fireEvent,
@@ -13,6 +13,7 @@ import { z } from 'zod'
 import {
   Outlet,
   RouterProvider,
+  createBrowserHistory,
   createRootRoute,
   createRoute,
   createRouteMask,
@@ -21,8 +22,17 @@ import {
   useNavigate,
   useParams,
 } from '../src'
+import type { RouterHistory } from '../src'
+
+let history: RouterHistory
+
+beforeEach(() => {
+  history = createBrowserHistory()
+  expect(window.location.pathname).toBe('/')
+})
 
 afterEach(() => {
+  history.destroy()
   window.history.replaceState(null, 'root', '/')
   cleanup()
 })
@@ -1509,7 +1519,11 @@ describe.each([{ basepath: '' }, { basepath: '/basepath' }])(
           return (
             <>
               <h1>Param Route</h1>
-              <button onClick={() => navigate({ to: './a' })}>
+              <button
+                onClick={() =>
+                  navigate({ from: paramRoute.fullPath, to: './a' })
+                }
+              >
                 Link to ./a
               </button>
               <button
@@ -1531,9 +1545,19 @@ describe.each([{ basepath: '' }, { basepath: '/basepath' }])(
           return (
             <>
               <h1>Param A Route</h1>
-              <button onClick={() => navigate({ to: '..' })}>
+              <button
+                onClick={() =>
+                  navigate({ from: paramARoute.fullPath, to: '..' })
+                }
+              >
                 Link to .. from /param/foo/a
               </button>
+              <button
+                onClick={() => navigate({ to: '..' })}
+                data-testid={'link-to-previous'}
+              >
+                Link to .. from current active route
+              </button>
               <Outlet />
             </>
           )
@@ -1668,6 +1692,23 @@ describe.each([{ basepath: '' }, { basepath: '/basepath' }])(
       )
     })
 
+    test('should navigate to a parent link based on active location', async () => {
+      const router = setupRouter()
+
+      render(() => <RouterProvider router={router} />)
+
+      window.history.replaceState(null, 'root', `${basepath}/param/foo/a/b`)
+
+      const relativeLink = await screen.findByTestId('link-to-previous')
+
+      // Click the link and ensure the new location
+      fireEvent.click(relativeLink)
+
+      await waitFor(() =>
+        expect(window.location.pathname).toBe(`${basepath}/param/foo/a`),
+      )
+    })
+
     test('should navigate to same route with different params', async () => {
       const router = setupRouter()
 
@@ -1686,3 +1727,799 @@ describe.each([{ basepath: '' }, { basepath: '/basepath' }])(
     })
   },
 )
+
+describe('relative navigate to current route', () => {
+  test.each([true, false])(
+    'should navigate to current route with search params when using "." in nested route structure from Index Route with trailingSlash: %s',
+    async (trailingSlash: boolean) => {
+      const tail = trailingSlash ? '/' : ''
+
+      const rootRoute = createRootRoute()
+
+      const IndexComponent = () => {
+        const navigate = useNavigate()
+        return (
+          <>
+            <button
+              data-testid="posts-btn"
+              onClick={() => {
+                navigate({
+                  to: '/post',
+                })
+              }}
+            >
+              Post
+            </button>
+            <button
+              data-testid="search-btn"
+              onClick={() =>
+                navigate({
+                  to: '.',
+                  search: {
+                    param1: 'value1',
+                  },
+                })
+              }
+            >
+              Search
+            </button>
+            <button
+              data-testid="search2-btn"
+              onClick={() =>
+                navigate({
+                  to: '/post',
+                  search: {
+                    param1: 'value2',
+                  },
+                })
+              }
+            >
+              Search2
+            </button>
+            <Outlet />
+          </>
+        )
+      }
+
+      const indexRoute = createRoute({
+        getParentRoute: () => rootRoute,
+        path: '/',
+        component: IndexComponent,
+        validateSearch: z.object({
+          param1: z.string().optional(),
+        }),
+      })
+
+      const postRoute = createRoute({
+        getParentRoute: () => indexRoute,
+        path: 'post',
+        component: () => <div>Post</div>,
+      })
+
+      const router = createRouter({
+        routeTree: rootRoute.addChildren([indexRoute, postRoute]),
+        history,
+        trailingSlash: trailingSlash ? 'always' : 'never',
+      })
+
+      render(() => <RouterProvider router={router} />)
+
+      const postButton = await screen.findByTestId('posts-btn')
+
+      fireEvent.click(postButton)
+
+      await waitFor(() => {
+        expect(router.state.location.pathname).toBe(`/post${tail}`)
+      })
+
+      const searchButton = await screen.findByTestId('search-btn')
+
+      fireEvent.click(searchButton)
+
+      await waitFor(() => {
+        expect(router.state.location.pathname).toBe(`/post${tail}`)
+        expect(router.state.location.search).toEqual({ param1: 'value1' })
+      })
+
+      const searchButton2 = await screen.findByTestId('search2-btn')
+
+      fireEvent.click(searchButton2)
+
+      await waitFor(() => {
+        expect(router.state.location.pathname).toBe(`/post${tail}`)
+        expect(router.state.location.search).toEqual({ param1: 'value2' })
+      })
+    },
+  )
+
+  test.each([true, false])(
+    'should navigate to current route with changing path params when using "." in nested route structure with trailingSlash: %s',
+    async (trailingSlash) => {
+      const tail = trailingSlash ? '/' : ''
+      const rootRoute = createRootRoute()
+
+      const IndexComponent = () => {
+        const navigate = useNavigate()
+        return (
+          <>
+            <h1 data-testid="index-heading">Index</h1>
+            <button
+              data-testid="posts-btn"
+              onClick={() => navigate({ to: '/posts' })}
+            >
+              Posts
+            </button>
+          </>
+        )
+      }
+
+      const indexRoute = createRoute({
+        getParentRoute: () => rootRoute,
+        path: '/',
+        component: IndexComponent,
+      })
+
+      const layoutRoute = createRoute({
+        getParentRoute: () => rootRoute,
+        id: '_layout',
+        component: () => {
+          return (
+            <>
+              <h1>Layout</h1>
+              <Outlet />
+            </>
+          )
+        },
+      })
+
+      const PostsComponent = () => {
+        const navigate = useNavigate()
+
+        return (
+          <>
+            <h1 data-testid="posts-index-heading">Posts</h1>
+            <button
+              data-testid="first-post-btn"
+              onClick={() =>
+                navigate({
+                  to: '$postId',
+                  params: { postId: 'id1' },
+                })
+              }
+            >
+              To first post
+            </button>
+            <button
+              data-testid="second-post-btn"
+              onClick={() =>
+                navigate({
+                  to: '.',
+                  params: { postId: 'id2' },
+                })
+              }
+            >
+              To second post
+            </button>
+            <Outlet />
+          </>
+        )
+      }
+
+      const postsRoute = createRoute({
+        getParentRoute: () => layoutRoute,
+        path: 'posts',
+        component: PostsComponent,
+      })
+
+      const PostComponent = () => {
+        const params = useParams({ strict: false })
+        return (
+          <>
+            <span data-testid={`post-${params().postId}`}>
+              Params: {params().postId}
+            </span>
+          </>
+        )
+      }
+
+      const postRoute = createRoute({
+        getParentRoute: () => postsRoute,
+        path: '$postId',
+        component: PostComponent,
+      })
+
+      const router = createRouter({
+        routeTree: rootRoute.addChildren([
+          indexRoute,
+          layoutRoute.addChildren([postsRoute.addChildren([postRoute])]),
+        ]),
+        trailingSlash: trailingSlash ? 'always' : 'never',
+      })
+
+      render(() => <RouterProvider router={router} />)
+
+      const postsButton = await screen.findByTestId('posts-btn')
+
+      fireEvent.click(postsButton)
+
+      await waitFor(() => {
+        expect(screen.queryByTestId('posts-index-heading')).toBeInTheDocument()
+        expect(window.location.pathname).toEqual(`/posts${tail}`)
+      })
+
+      const firstPostButton = await screen.findByTestId('first-post-btn')
+
+      fireEvent.click(firstPostButton)
+
+      await waitFor(() => {
+        expect(screen.queryByTestId('post-id1')).toBeInTheDocument()
+        expect(window.location.pathname).toEqual(`/posts/id1${tail}`)
+      })
+
+      const secondPostButton = await screen.findByTestId('second-post-btn')
+
+      fireEvent.click(secondPostButton)
+
+      await waitFor(() => {
+        expect(screen.queryByTestId('post-id2')).toBeInTheDocument()
+        expect(window.location.pathname).toEqual(`/posts/id2${tail}`)
+      })
+    },
+  )
+
+  test.each([true, false])(
+    'should navigate to current route with search params when using "." in nested route structure from non-Index Route with trailingSlash: %s',
+    async (trailingSlash) => {
+      const tail = trailingSlash ? '/' : ''
+      const rootRoute = createRootRoute()
+
+      const IndexComponent = () => {
+        const navigate = useNavigate()
+        return (
+          <>
+            <h1 data-testid="index-heading">Index</h1>
+            <button
+              data-testid="posts-btn"
+              onClick={() => navigate({ to: '/posts', params: { lang: '1' } })}
+            >
+              Posts
+            </button>
+          </>
+        )
+      }
+
+      const indexRoute = createRoute({
+        getParentRoute: () => rootRoute,
+        path: '/',
+        component: IndexComponent,
+      })
+
+      const PostsComponent = () => {
+        const navigate = postsRoute.useNavigate()
+        return (
+          <>
+            <h1 data-testid="posts-index-heading">Posts</h1>
+            <button
+              data-testid="first-post-btn"
+              onClick={() =>
+                navigate({
+                  to: '$postId/detail',
+                  params: { postId: 'id1' },
+                })
+              }
+            >
+              To first post
+            </button>
+            <Outlet />
+          </>
+        )
+      }
+
+      const postsRoute = createRoute({
+        getParentRoute: () => rootRoute,
+        path: 'posts',
+        component: PostsComponent,
+      })
+
+      const useModal = (name: string) => {
+        const currentOpen = postRoute.useSearch({
+          select: (search) => search[`_${name}`],
+        })
+
+        const navigate = useNavigate()
+
+        const setModal = (open: boolean) => {
+          navigate({
+            to: '.',
+            search: (prev: {}) => ({
+              ...prev,
+              [`_${name}`]: open ? true : undefined,
+            }),
+            resetScroll: false,
+          })
+        }
+
+        return [currentOpen, setModal] as const
+      }
+
+      function DetailComponent(props: { id: string }) {
+        const params = useParams({ strict: false })
+        const [currentTest, setTest] = useModal('test')
+
+        return (
+          <>
+            <div data-testid={`detail-heading-${props.id}`}>
+              Post Path "/{params().postId}/detail-{props.id}"!
+            </div>
+            {currentTest() ? (
+              <button
+                data-testid={`detail-btn-remove-${props.id}`}
+                onClick={() => setTest(false)}
+              >
+                Remove test
+              </button>
+            ) : (
+              <button
+                data-testid={`detail-btn-add-${props.id}`}
+                onClick={() => setTest(true)}
+              >
+                Add test
+              </button>
+            )}
+          </>
+        )
+      }
+
+      const PostComponent = () => {
+        const params = useParams({ strict: false })
+
+        return (
+          <div>
+            <div data-testid="post-heading">Post "{params().postId}"!</div>
+            <DetailComponent id={'1'} />
+            <Outlet />
+          </div>
+        )
+      }
+
+      const postRoute = createRoute({
+        getParentRoute: () => postsRoute,
+        path: '$postId',
+        component: PostComponent,
+        validateSearch: z.object({
+          _test: z.boolean().optional(),
+        }),
+      })
+
+      const detailRoute = createRoute({
+        getParentRoute: () => postRoute,
+        path: 'detail',
+        component: () => <DetailComponent id={'2'} />,
+      })
+
+      const router = createRouter({
+        routeTree: rootRoute.addChildren([
+          indexRoute,
+          postsRoute.addChildren([postRoute.addChildren([detailRoute])]),
+        ]),
+        trailingSlash: trailingSlash ? 'always' : 'never',
+      })
+
+      render(() => <RouterProvider router={router} />)
+
+      const postsButton = await screen.findByTestId('posts-btn')
+
+      fireEvent.click(postsButton)
+
+      await waitFor(() => {
+        expect(screen.queryByTestId('posts-index-heading')).toBeInTheDocument()
+      })
+
+      const post1Button = await screen.findByTestId('first-post-btn')
+
+      fireEvent.click(post1Button)
+
+      await waitFor(() => {
+        expect(screen.queryByTestId('post-heading')).toBeInTheDocument()
+        expect(screen.queryByTestId('detail-heading-1')).toBeInTheDocument()
+        expect(screen.queryByTestId('detail-heading-2')).toBeInTheDocument()
+        expect(screen.queryByTestId('detail-heading-1')).toHaveTextContent(
+          'Post Path "/id1/detail-1',
+        )
+        expect(screen.queryByTestId('detail-heading-2')).toHaveTextContent(
+          'Post Path "/id1/detail-2',
+        )
+      })
+
+      const detail1AddBtn = await screen.findByTestId('detail-btn-add-1')
+
+      fireEvent.click(detail1AddBtn)
+
+      await waitFor(() => {
+        expect(router.state.location.pathname).toBe(`/posts/id1/detail${tail}`)
+        expect(router.state.location.search).toEqual({ _test: true })
+      })
+
+      const detail1RemoveBtn = await screen.findByTestId('detail-btn-remove-1')
+
+      fireEvent.click(detail1RemoveBtn)
+
+      await waitFor(() => {
+        expect(router.state.location.pathname).toBe(`/posts/id1/detail${tail}`)
+        expect(router.state.location.search).toEqual({})
+      })
+
+      const detail2AddBtn = await screen.findByTestId('detail-btn-add-2')
+
+      fireEvent.click(detail2AddBtn)
+
+      await waitFor(() => {
+        expect(router.state.location.pathname).toBe(`/posts/id1/detail${tail}`)
+        expect(router.state.location.search).toEqual({ _test: true })
+      })
+    },
+  )
+})
+
+describe('relative navigate to from route', () => {
+  test.each([true, false])(
+    'should navigate to from route when using "." in nested route structure from Index Route with trailingSlash: %s',
+    async (trailingSlash: boolean) => {
+      const tail = trailingSlash ? '/' : ''
+
+      const rootRoute = createRootRoute()
+
+      const IndexComponent = () => {
+        const navigate = useNavigate()
+        return (
+          <>
+            <button
+              data-testid="posts-btn"
+              onClick={() => {
+                navigate({
+                  to: '/post',
+                })
+              }}
+            >
+              Post
+            </button>
+            <button
+              data-testid="search-btn"
+              onClick={() =>
+                navigate({
+                  to: '.',
+                  search: {
+                    param1: 'value1',
+                  },
+                })
+              }
+            >
+              Search
+            </button>
+            <button
+              data-testid="home-btn"
+              onClick={() =>
+                navigate({
+                  from: '/',
+                  to: '.',
+                })
+              }
+            >
+              Go To Home
+            </button>
+            <Outlet />
+          </>
+        )
+      }
+
+      const indexRoute = createRoute({
+        getParentRoute: () => rootRoute,
+        path: '/',
+        component: IndexComponent,
+        validateSearch: z.object({
+          param1: z.string().optional(),
+        }),
+      })
+
+      const postRoute = createRoute({
+        getParentRoute: () => indexRoute,
+        path: 'post',
+        component: () => <div>Post</div>,
+      })
+
+      const router = createRouter({
+        routeTree: rootRoute.addChildren([indexRoute, postRoute]),
+        history,
+        trailingSlash: trailingSlash ? 'always' : 'never',
+      })
+
+      render(() => <RouterProvider router={router} />)
+
+      const postButton = await screen.findByTestId('posts-btn')
+
+      fireEvent.click(postButton)
+
+      await waitFor(() => {
+        expect(router.state.location.pathname).toBe(`/post${tail}`)
+      })
+
+      const searchButton = await screen.findByTestId('search-btn')
+
+      fireEvent.click(searchButton)
+
+      await waitFor(() => {
+        expect(router.state.location.pathname).toBe(`/post${tail}`)
+        expect(router.state.location.search).toEqual({ param1: 'value1' })
+      })
+
+      const homeBtn = await screen.findByTestId('home-btn')
+
+      fireEvent.click(homeBtn)
+
+      await waitFor(() => {
+        expect(router.state.location.pathname).toBe(`/`)
+        expect(router.state.location.search).toEqual({})
+      })
+    },
+  )
+
+  test.each([true, false])(
+    'should navigate to from route with path params when using "." in nested route structure with trailingSlash: %s',
+    async (trailingSlash) => {
+      const tail = trailingSlash ? '/' : ''
+      const rootRoute = createRootRoute()
+
+      const IndexComponent = () => {
+        const navigate = useNavigate()
+        return (
+          <>
+            <h1 data-testid="index-heading">Index</h1>
+            <button
+              data-testid="posts-btn"
+              onClick={() => navigate({ to: '/posts' })}
+            >
+              Posts
+            </button>
+          </>
+        )
+      }
+
+      const indexRoute = createRoute({
+        getParentRoute: () => rootRoute,
+        path: '/',
+        component: IndexComponent,
+      })
+
+      const layoutRoute = createRoute({
+        getParentRoute: () => rootRoute,
+        id: '_layout',
+        component: () => {
+          return (
+            <>
+              <h1>Layout</h1>
+              <Outlet />
+            </>
+          )
+        },
+      })
+
+      const PostsComponent = () => {
+        const navigate = postsRoute.useNavigate()
+        return (
+          <>
+            <h1 data-testid="posts-index-heading">Posts</h1>
+            <button
+              data-testid="first-post-btn"
+              onClick={() =>
+                navigate({
+                  to: '$postId',
+                  params: { postId: '1' },
+                })
+              }
+            >
+              To first post
+            </button>
+            <button
+              data-testid="second-post-btn"
+              onClick={() =>
+                navigate({
+                  to: '$postId',
+                  params: { postId: '2' },
+                })
+              }
+            >
+              To second post
+            </button>
+            <button
+              data-testid="to-posts-index-btn"
+              onClick={() =>
+                navigate({
+                  from: '/posts',
+                  to: '.',
+                })
+              }
+            >
+              To posts list
+            </button>
+            <Outlet />
+          </>
+        )
+      }
+
+      const PostDetailComponent = () => {
+        const navigate = postDetailRoute.useNavigate()
+        return (
+          <>
+            <h1 data-testid="post-detail-index-heading">Post Detail</h1>
+            <button
+              data-testid="post-info-btn"
+              onClick={() =>
+                navigate({
+                  to: 'info',
+                })
+              }
+            >
+              To post info
+            </button>
+            <button
+              data-testid="post-notes-btn"
+              onClick={() =>
+                navigate({
+                  to: 'notes',
+                })
+              }
+            >
+              To post notes
+            </button>
+            <button
+              data-testid="to-post-detail-index-btn"
+              onClick={() =>
+                navigate({
+                  from: '/posts/$postId',
+                  to: '.',
+                })
+              }
+            >
+              To index detail options
+            </button>
+            <Outlet />
+          </>
+        )
+      }
+
+      const PostInfoComponent = () => {
+        return (
+          <>
+            <h1 data-testid="post-info-heading">Post Info</h1>
+          </>
+        )
+      }
+
+      const PostNotesComponent = () => {
+        return (
+          <>
+            <h1 data-testid="post-notes-heading">Post Notes</h1>
+          </>
+        )
+      }
+
+      const postsRoute = createRoute({
+        getParentRoute: () => layoutRoute,
+        path: 'posts',
+        component: PostsComponent,
+      })
+
+      const postDetailRoute = createRoute({
+        getParentRoute: () => postsRoute,
+        path: '$postId',
+        component: PostDetailComponent,
+      })
+
+      const postInfoRoute = createRoute({
+        getParentRoute: () => postDetailRoute,
+        path: 'info',
+        component: PostInfoComponent,
+      })
+
+      const postNotesRoute = createRoute({
+        getParentRoute: () => postDetailRoute,
+        path: 'notes',
+        component: PostNotesComponent,
+      })
+
+      const router = createRouter({
+        routeTree: rootRoute.addChildren([
+          indexRoute,
+          layoutRoute.addChildren([
+            postsRoute.addChildren([
+              postDetailRoute.addChildren([postInfoRoute, postNotesRoute]),
+            ]),
+          ]),
+        ]),
+        trailingSlash: trailingSlash ? 'always' : 'never',
+      })
+
+      render(() => <RouterProvider router={router} />)
+
+      const postsButton = await screen.findByTestId('posts-btn')
+
+      fireEvent.click(postsButton)
+
+      await waitFor(() => {
+        expect(screen.queryByTestId('posts-index-heading')).toBeInTheDocument()
+        expect(window.location.pathname).toEqual(`/posts${tail}`)
+      })
+
+      const firstPostButton = await screen.findByTestId('first-post-btn')
+
+      fireEvent.click(firstPostButton)
+
+      await waitFor(() => {
+        expect(
+          screen.queryByTestId('post-detail-index-heading'),
+        ).toBeInTheDocument()
+        expect(window.location.pathname).toEqual(`/posts/1${tail}`)
+      })
+
+      const postInfoButton = await screen.findByTestId('post-info-btn')
+
+      fireEvent.click(postInfoButton)
+
+      await waitFor(() => {
+        expect(screen.queryByTestId('post-info-heading')).toBeInTheDocument()
+        expect(window.location.pathname).toEqual(`/posts/1/info${tail}`)
+      })
+
+      const toPostDetailIndexButton = await screen.findByTestId(
+        'to-post-detail-index-btn',
+      )
+
+      fireEvent.click(toPostDetailIndexButton)
+
+      await waitFor(() => {
+        expect(
+          screen.queryByTestId('post-detail-index-heading'),
+        ).toBeInTheDocument()
+        expect(
+          screen.queryByTestId('post-info-heading'),
+        ).not.toBeInTheDocument()
+        expect(window.location.pathname).toEqual(`/posts/1${tail}`)
+      })
+
+      const postNotesButton = await screen.findByTestId('post-notes-btn')
+
+      fireEvent.click(postNotesButton)
+
+      await waitFor(() => {
+        expect(screen.queryByTestId('post-notes-heading')).toBeInTheDocument()
+        expect(window.location.pathname).toEqual(`/posts/1/notes${tail}`)
+      })
+
+      const toPostsIndexButton = await screen.findByTestId('to-posts-index-btn')
+
+      fireEvent.click(toPostsIndexButton)
+
+      await waitFor(() => {
+        expect(screen.queryByTestId('posts-index-heading')).toBeInTheDocument()
+        expect(
+          screen.queryByTestId('post-notes-heading'),
+        ).not.toBeInTheDocument()
+        expect(
+          screen.queryByTestId('post-detail-index-heading'),
+        ).not.toBeInTheDocument()
+        expect(window.location.pathname).toEqual(`/posts${tail}`)
+      })
+
+      const secondPostButton = await screen.findByTestId('second-post-btn')
+
+      fireEvent.click(secondPostButton)
+
+      await waitFor(() => {
+        expect(
+          screen.queryByTestId('post-detail-index-heading'),
+        ).toBeInTheDocument()
+        expect(window.location.pathname).toEqual(`/posts/2${tail}`)
+      })
+    },
+  )
+})