diff --git a/packages/bridge/src/runtime/composables.ts b/packages/bridge/src/runtime/composables.ts
index dfb2ebc23e..f6afe057e0 100644
--- a/packages/bridge/src/runtime/composables.ts
+++ b/packages/bridge/src/runtime/composables.ts
@@ -4,6 +4,7 @@ import type { MetaInfo } from 'vue-meta'
import type VueRouter from 'vue-router'
import type { Location, Route } from 'vue-router'
import type { RuntimeConfig } from '@nuxt/schema'
+import { sendRedirect } from 'h3'
import defu from 'defu'
import { useNuxtApp } from './app'
@@ -172,12 +173,23 @@ const isProcessingMiddleware = () => {
return false
}
-export const navigateTo = (to: Route) => {
+export interface NavigateToOptions {
+ replace?: boolean
+}
+
+export const navigateTo = (to: Route, options: NavigateToOptions = {}) => {
if (isProcessingMiddleware()) {
return to
}
- const router: VueRouter = process.server ? useRouter() : (window as any).$nuxt.$router
- return router.push(to)
+ const router = useRouter()
+ if (process.server && useNuxtApp().ssrContext) {
+ // Server-side redirection using h3 res from ssrContext
+ const res = useNuxtApp().ssrContext?.res
+ const redirectLocation = router.resolve(to).route.fullPath
+ return sendRedirect(res, redirectLocation)
+ }
+ // Client-side redirection using vue-router
+ return options.replace ? router.replace(to) : router.push(to)
}
/** This will abort navigation within a Nuxt route middleware handler. */
diff --git a/packages/nuxt3/src/app/composables/router.ts b/packages/nuxt3/src/app/composables/router.ts
index f5daf0a345..c6c133b867 100644
--- a/packages/nuxt3/src/app/composables/router.ts
+++ b/packages/nuxt3/src/app/composables/router.ts
@@ -1,4 +1,5 @@
import type { Router, RouteLocationNormalizedLoaded, NavigationGuard, RouteLocationNormalized, RouteLocationRaw } from 'vue-router'
+import { sendRedirect } from 'h3'
import { useNuxtApp } from '#app'
export const useRouter = () => {
@@ -45,12 +46,23 @@ const isProcessingMiddleware = () => {
return false
}
-export const navigateTo = (to: RouteLocationRaw) => {
+export interface NavigateToOptions {
+ replace?: boolean
+}
+
+export const navigateTo = (to: RouteLocationRaw, options: NavigateToOptions = {}) => {
if (isProcessingMiddleware()) {
return to
}
- const router: Router = process.server ? useRouter() : (window as any).$nuxt.$router
- return router.push(to)
+ const router = useRouter()
+ if (process.server && useNuxtApp().ssrContext) {
+ // Server-side redirection using h3 res from ssrContext
+ const res = useNuxtApp().ssrContext?.res
+ const redirectLocation = router.resolve(to).fullPath
+ return sendRedirect(res, redirectLocation)
+ }
+ // Client-side redirection using vue-router
+ return options.replace ? router.replace(to) : router.push(to)
}
/** This will abort navigation within a Nuxt route middleware handler. */
diff --git a/test/basic.test.ts b/test/basic.test.ts
index a7ad8688c9..9bd324f3b0 100644
--- a/test/basic.test.ts
+++ b/test/basic.test.ts
@@ -94,6 +94,17 @@ describe('fixtures:basic', async () => {
})
})
+ describe('navigate', () => {
+ it('should redirect to index with navigateTo', async () => {
+ const html = await $fetch('/navigate-to/')
+
+ // Snapshot
+ // expect(html).toMatchInlineSnapshot()
+
+ expect(html).toContain('Hello Nuxt 3!')
+ })
+ })
+
describe('middlewares', () => {
it('should redirect to index with global middleware', async () => {
const html = await $fetch('/redirect/')
diff --git a/test/bridge.test.ts b/test/bridge.test.ts
index 4e375854dd..018920cd99 100644
--- a/test/bridge.test.ts
+++ b/test/bridge.test.ts
@@ -8,7 +8,16 @@ describe('fixtures:bridge', async () => {
server: true
})
- it('Render hello world', async () => {
- expect(await $fetch('/')).to.contain('Hello Vue 2!')
+ describe('pages', () => {
+ it('render hello world', async () => {
+ expect(await $fetch('/')).to.contain('Hello Vue 2!')
+ })
+ })
+
+ describe('navigate', () => {
+ it('should redirect to index with navigateTo', async () => {
+ const html = await $fetch('/navigate-to/')
+ expect(html).toContain('Hello Vue 2!')
+ })
})
})
diff --git a/test/fixtures/basic/pages/navigate-to.vue b/test/fixtures/basic/pages/navigate-to.vue
new file mode 100644
index 0000000000..717569e2df
--- /dev/null
+++ b/test/fixtures/basic/pages/navigate-to.vue
@@ -0,0 +1,7 @@
+
+ You should not see me
+
+
+
diff --git a/test/fixtures/basic/types.ts b/test/fixtures/basic/types.ts
index 20561e1231..fef78eedf5 100644
--- a/test/fixtures/basic/types.ts
+++ b/test/fixtures/basic/types.ts
@@ -51,7 +51,7 @@ describe('middleware', () => {
addRouteMiddleware('example', (to, from) => {
expectTypeOf(to).toMatchTypeOf()
expectTypeOf(from).toMatchTypeOf()
- expectTypeOf(navigateTo).toMatchTypeOf<(to: RouteLocationRaw) => RouteLocationRaw | Promise>()
+ expectTypeOf(navigateTo).toMatchTypeOf<(to: RouteLocationRaw) => RouteLocationRaw | Promise>()
navigateTo('/')
abortNavigation()
abortNavigation('error string')
diff --git a/test/fixtures/bridge/pages/navigate-to.vue b/test/fixtures/bridge/pages/navigate-to.vue
new file mode 100644
index 0000000000..717569e2df
--- /dev/null
+++ b/test/fixtures/bridge/pages/navigate-to.vue
@@ -0,0 +1,7 @@
+
+ You should not see me
+
+
+