Repository: nuxt/nuxt
File: packages/nuxt/src/app/components/nuxt-layout.ts (TypeScript)

Code before:

import type { PageMeta } from '../../pages/runtime/composables'

import { useRoute, useRouter } from '../composables/router'
import { useNuxtApp } from '../nuxt'
import { _wrapIf } from './utils'
import { LayoutMetaSymbol, PageRouteSymbol } from './injections'

// @ts-expect-error virtual file
import { useRoute as useVueRouterRoute } from '#build/pages'
// @ts-expect-error virtual file
import layouts from '#build/layouts'
// @ts-expect-error virtual file
import { appLayoutTransition as defaultLayoutTransition } from '#build/nuxt.config.mjs'

// TODO: revert back to defineAsyncComponent when https://github.com/vuejs/core/issues/6638 is resolved
const LayoutLoader = defineComponent({
  name: 'LayoutLoader',
  inheritAttrs: false,
  props: {
    name: String,
    layoutProps: Object,
  },
  async setup (props, context) {
    // This is a deliberate hack - this component must always be called with an explicit key to ensure
    // that setup reruns when the name changes.

    const LayoutComponent = await layouts[props.name]().then((r: any) => r.default || r)

    return () => h(LayoutComponent, props.layoutProps, context.slots)
  },
})

export default defineComponent({
  name: 'NuxtLayout',
  inheritAttrs: false,
  props: {
    name: {
      type: [String, Boolean, Object] as unknown as () => unknown extends PageMeta['layout'] ? MaybeRef<string | false> : PageMeta['layout'],
      default: null,
    },
    fallback: {
      type: [String, Object] as unknown as () => unknown extends PageMeta['layout'] ? MaybeRef<string> : PageMeta['layout'],
      default: null,
    },
  },
  setup (props, context) {
    const nuxtApp = useNuxtApp()
    // Need to ensure (if we are not a child of `<NuxtPage>`) that we use synchronous route (not deferred)
    const injectedRoute = inject(PageRouteSymbol)
    const route = injectedRoute === useRoute() ? useVueRouterRoute() : injectedRoute

Review comment (suggestion):
@danielroe is there any chance that the removal of `await` before calling `layouts[props.name]` here (or another change in this PR) could cause a hydration error in some scenarios? 

After upgrading to `v3.14.1592` (the only change in my project) I'm now seeing `Hydration completed but contains mismatches` on a page that only contains a NuxtLayout and a paragraph tag in the default slot.

<img width="640" alt="hydration error" src="https://github.com/user-attachments/assets/99c8e051-0db9-4842-9c52-9783d6d2d1d8">


It doesn't occur when the dev server is running; only when building and running the build (e.g. with `wrangler`).

I've tried putting together a reproduction but (of course) it doesn't happen there as of yet. 

Do you have any suggestions on how to further triage? Typically I'd start disabling modules, etc. but in a larger project, that isn't the easiest task since so many things depend on others 😬 

Code after:

import type { PageMeta } from '../../pages/runtime/composables'

import { useRoute, useRouter } from '../composables/router'
import { useNuxtApp } from '../nuxt'
import { _wrapIf } from './utils'
import { LayoutMetaSymbol, PageRouteSymbol } from './injections'

// @ts-expect-error virtual file
import { useRoute as useVueRouterRoute } from '#build/pages'
// @ts-expect-error virtual file
import layouts from '#build/layouts'
// @ts-expect-error virtual file
import { appLayoutTransition as defaultLayoutTransition } from '#build/nuxt.config.mjs'

const LayoutLoader = defineComponent({
  name: 'LayoutLoader',
  inheritAttrs: false,
  props: {
    name: String,
    layoutProps: Object,
  },
  setup (props, context) {
    // This is a deliberate hack - this component must always be called with an explicit key to ensure
    // that setup reruns when the name changes.
    return () => h(layouts[props.name], props.layoutProps, context.slots)
  },
})

export default defineComponent({
  name: 'NuxtLayout',
  inheritAttrs: false,
  props: {
    name: {
      type: [String, Boolean, Object] as unknown as () => unknown extends PageMeta['layout'] ? MaybeRef<string | false> : PageMeta['layout'],
      default: null,
    },
    fallback: {
      type: [String, Object] as unknown as () => unknown extends PageMeta['layout'] ? MaybeRef<string> : PageMeta['layout'],
      default: null,
    },
  },
  setup (props, context) {
    const nuxtApp = useNuxtApp()
    // Need to ensure (if we are not a child of `<NuxtPage>`) that we use synchronous route (not deferred)
    const injectedRoute = inject(PageRouteSymbol)
    const route = injectedRoute === useRoute() ? useVueRouterRoute() : injectedRoute

    const layout = computed(() => {
      let layout = unref(props.name) ?? route.meta.layout as string ?? 'default'
      if (layout && !(layout in layouts)) {