diff --git a/docs/content/3.docs/2.directory-structure/6.layouts.md b/docs/content/3.docs/2.directory-structure/6.layouts.md
index 65112b2b13..591d641d27 100644
--- a/docs/content/3.docs/2.directory-structure/6.layouts.md
+++ b/docs/content/3.docs/2.directory-structure/6.layouts.md
@@ -79,12 +79,10 @@ You can also use a ref or computed property for your layout.
```
diff --git a/packages/nuxt3/src/pages/module.ts b/packages/nuxt3/src/pages/module.ts
index 85300296a6..7e51754713 100644
--- a/packages/nuxt3/src/pages/module.ts
+++ b/packages/nuxt3/src/pages/module.ts
@@ -112,8 +112,27 @@ export default defineNuxtModule({
}
})
+ addTemplate({
+ filename: 'layouts.d.ts',
+ write: true,
+ getContents: async () => {
+ const composablesFile = resolve(runtimeDir, 'composables')
+ const layouts = await resolveLayouts(nuxt)
+ return [
+ 'import { ComputedRef, Ref } from \'vue\'',
+ `export type LayoutKey = ${layouts.map(layout => `"${layout.name}"`).join(' | ') || 'string'}`,
+ `declare module '${composablesFile}' {`,
+ ' interface PageMeta {',
+ ' layout?: false | LayoutKey | Ref | ComputedRef',
+ ' }',
+ '}'
+ ].join('\n')
+ }
+ })
+
nuxt.hook('prepare:types', ({ references }) => {
references.push({ path: resolve(nuxt.options.buildDir, 'middleware.d.ts') })
+ references.push({ path: resolve(nuxt.options.buildDir, 'layouts.d.ts') })
})
// Add layouts template
diff --git a/packages/nuxt3/src/pages/runtime/composables.ts b/packages/nuxt3/src/pages/runtime/composables.ts
index 68a68eb1bc..2072cf32e0 100644
--- a/packages/nuxt3/src/pages/runtime/composables.ts
+++ b/packages/nuxt3/src/pages/runtime/composables.ts
@@ -1,4 +1,4 @@
-import { ComputedRef, KeepAliveProps, Ref, TransitionProps } from 'vue'
+import { KeepAliveProps, TransitionProps, UnwrapRef } from 'vue'
import type { Router, RouteLocationNormalizedLoaded, NavigationGuard, RouteLocationNormalized, RouteLocationRaw } from 'vue-router'
import { useNuxtApp } from '#app'
@@ -14,13 +14,12 @@ export interface PageMeta {
[key: string]: any
pageTransition?: false | TransitionProps
layoutTransition?: false | TransitionProps
- layout?: false | string | Ref | ComputedRef
key?: string | ((route: RouteLocationNormalizedLoaded) => string)
keepalive?: false | KeepAliveProps
}
declare module 'vue-router' {
- interface RouteMeta extends PageMeta {}
+ interface RouteMeta extends UnwrapRef {}
}
const warnRuntimeUsage = (method: string) =>
diff --git a/packages/nuxt3/src/pages/runtime/page.ts b/packages/nuxt3/src/pages/runtime/page.ts
index b2d2f1b980..4dad687375 100644
--- a/packages/nuxt3/src/pages/runtime/page.ts
+++ b/packages/nuxt3/src/pages/runtime/page.ts
@@ -25,26 +25,34 @@ export default defineComponent({
const hasLayout = props.layout ?? route.meta.layout ?? 'default' in layouts
return h(RouterView, {}, {
- default: ({ Component }: RouterViewSlotProps) => Component && wrapIf(Transition, hasLayout && (route.meta.layoutTransition ?? defaultLayoutTransition), {
- default: () => wrapIf(NuxtLayout, hasLayout && { layout: props.layout ?? route.meta.layout }, {
- default: () => wrapIf(Transition, route.meta.pageTransition ?? defaultPageTransition, {
- default: () => wrapIf(KeepAlive, process.client && route.meta.keepalive, h(Suspense, {
+ default: ({ Component }: RouterViewSlotProps) => Component &&
+ wrapIf(Transition, hasLayout && (route.meta.layoutTransition ?? defaultLayoutTransition),
+ wrapIf(NuxtLayout, hasLayout && { name: props.layout ?? route.meta.layout },
+ wrapIf(Transition, route.meta.pageTransition ?? defaultPageTransition,
+ wrapInKeepAlive(route.meta.keepalive, h(Suspense, {
onPending: () => nuxtApp.callHook('page:start', Component),
onResolve: () => nuxtApp.callHook('page:finish', Component)
- }, { default: () => h(Component) }))
- })
- })
- })
+ }, { default: () => h(Component) })
+ )
+ )
+ )).default()
})
}
}
})
-const wrapIf = (component: Component, props: any, slotsOrChildren: any) => {
- if (props) {
- return h(component, props === true ? {} : props, slotsOrChildren)
+const Fragment = {
+ setup (props, { slots }) {
+ return () => slots.default()
}
- return slotsOrChildren.default?.() || slotsOrChildren
+}
+
+const wrapIf = (component: Component, props: any, slots: any) => {
+ return { default: () => props ? h(component, props === true ? {} : props, slots) : h(Fragment, {}, slots) }
+}
+
+const wrapInKeepAlive = (props: any, children: any) => {
+ return { default: () => process.client && props ? h(KeepAlive, props === true ? {} : props, children) : children }
}
const defaultLayoutTransition = { name: 'layout', mode: 'out-in' }
diff --git a/packages/nuxt3/src/pages/runtime/router.ts b/packages/nuxt3/src/pages/runtime/router.ts
index a8df95a41a..e0f0a2906e 100644
--- a/packages/nuxt3/src/pages/runtime/router.ts
+++ b/packages/nuxt3/src/pages/runtime/router.ts
@@ -66,6 +66,7 @@ export default defineNuxtPlugin((nuxtApp) => {
}
router.beforeEach(async (to, from) => {
+ to.meta = reactive(to.meta)
nuxtApp._processingMiddleware = true
type MiddlewareDef = string | NavigationGuard