diff --git a/packages/react-router/src/router.ts b/packages/react-router/src/router.ts index bf93c1983e7..7f8edebd7e9 100644 --- a/packages/react-router/src/router.ts +++ b/packages/react-router/src/router.ts @@ -1491,7 +1491,9 @@ export class Router< ) let pathname: string if (dest.to) { - pathname = this.resolvePathWithBase(fromPath, `${dest.to}`) + const resolvePathTo = + fromMatch?.fullPath || this.latestLocation.pathname + pathname = this.resolvePathWithBase(resolvePathTo, `${dest.to}`) } else { const fromRouteByFromPathRouteId = this.routesById[ diff --git a/packages/react-router/tests/navigate.test.tsx b/packages/react-router/tests/navigate.test.tsx index 3ef053e826e..c3c34edbb52 100644 --- a/packages/react-router/tests/navigate.test.tsx +++ b/packages/react-router/tests/navigate.test.tsx @@ -488,3 +488,63 @@ describe('router.navigate navigation using layout routes resolves correctly', () expect(router.state.location.search).toStrictEqual({ 'foo=bar': 3 }) }) }) + +describe('relative navigation', () => { + it('should navigate to a child route', async () => { + const { router } = createTestRouter( + createMemoryHistory({ initialEntries: ['/posts'] }), + ) + + await router.load() + + expect(router.state.location.pathname).toBe('/posts') + + await router.navigate({ + from: '/posts', + to: './$slug', + params: { slug: 'tkdodo' }, + }) + + await router.invalidate() + + expect(router.state.location.pathname).toBe('/posts/tkdodo') + }) + + it('should navigate to a parent route', async () => { + const { router } = createTestRouter( + createMemoryHistory({ initialEntries: ['/posts/tanner'] }), + ) + + await router.load() + + expect(router.state.location.pathname).toBe('/posts/tanner') + + await router.navigate({ + to: '..', + }) + + await router.invalidate() + + expect(router.state.location.pathname).toBe('/posts') + }) + + it('should navigate to a sibling route', async () => { + const { router } = createTestRouter( + createMemoryHistory({ initialEntries: ['/posts/tanner'] }), + ) + + await router.load() + + expect(router.state.location.pathname).toBe('/posts/tanner') + + await router.navigate({ + from: '/posts/$slug', + to: '.', + params: { slug: 'tkdodo' }, + }) + + await router.invalidate() + + expect(router.state.location.pathname).toBe('/posts/tkdodo') + }) +})