Skip to content

feat(nuxt): support named layout slots#20

Merged
KazariEX merged 20 commits into
mainfrom
feat/nuxt-layout-slots
Jun 15, 2026
Merged

feat(nuxt): support named layout slots#20
KazariEX merged 20 commits into
mainfrom
feat/nuxt-layout-slots

Conversation

@KazariEX

@KazariEX KazariEX commented May 10, 2026

Copy link
Copy Markdown
Owner
language runtime
image image

Runtime support might need to be implemented within the nuxt core to ensure its compatibility with other features.

The current approach results in the entire layout component being remounted when switching between pages with the same layout.

Done!!!

@udrzbarik

Copy link
Copy Markdown

Hello, thanks for this great feature! I wanted to ask you whether it's intended to be used with the explicit wrapping within NuxtLayout, because without it I get the following issue:

[plugin:vite:vue] Codegen node is missing for element/if/for node. Apply appropriate transforms first.

This issue appears only when using the named slots feature you implemented in this PR without wrapping the page's content in an explicit NuxtLayout component and by just using definePageMeta({ layout: 'myLayout' }).

@KazariEX

KazariEX commented Jun 7, 2026

Copy link
Copy Markdown
Owner Author

This may be due to the heuristic page component matching I'm currently using https://github.com/KazariEX/dxup/pull/20/changes#diff-eb05991fdc60c6791b6cb89225aeaa94dac24d257ec24e0673d19d13fe9e3625R18-R22. You can clone this branch and check the result in the local playground.

@KazariEX KazariEX changed the title feat(nuxt-layout-slots): implement feat(nuxt): support named layout slots Jun 15, 2026
@KazariEX
KazariEX marked this pull request as ready for review June 15, 2026 08:21
@KazariEX
KazariEX merged commit b38aa7b into main Jun 15, 2026
2 checks passed
@KazariEX
KazariEX deleted the feat/nuxt-layout-slots branch June 15, 2026 09:11
@danielroe danielroe mentioned this pull request Jun 18, 2026
@KazariEX

KazariEX commented Jun 18, 2026

Copy link
Copy Markdown
Owner Author

@udrzbarik Could you try again to see if the problem still occurs? If so, open an issue with a minimal reproduction.

Before the next minor release of Nuxt, you can enable this feature early using overrides: - @dxup/nuxt@0.5.0.

@udrzbarik

Copy link
Copy Markdown

Hi, I'm using the following setup in my package.json

  "overrides": {
    "@dxup/nuxt": "0.5.0"
  },

And the above setup for my template inside of the page, but I still seem to hit the Codegen node issue.

@KazariEX

KazariEX commented Jun 21, 2026

Copy link
Copy Markdown
Owner Author

Have you enabled dxup.features.namedLayoutSlots in your nuxt.config.ts? See https://github.com/KazariEX/dxup/tree/main/packages/nuxt#usage.

@udrzbarik

udrzbarik commented Jul 10, 2026

Copy link
Copy Markdown

Hi, I can confirm it no longer throws an error, but it doesn't seem to work for me either way.

<template>
  <!-- Slot inside of the `layouts/legislative.vue` -->
  <slot name="breadcrumbs" />
</template>
<template>
   <!-- Component inside of the `pages/[...slug].vue` -->
  <template #breadcrumbs>
    <ResolutionBreadcrumbs :canSaveDraft="isNew" />
  </template>
</template>

<script setup lang="ts">
  definePageMeta({
    layout: {
      name: 'legislative',
      props: {
        class: 'pb-7 max-w-325'
      }
    }
  })
</script>

It appears it doesn't throw an error regarding the codegen anymore, but it doesn't display the content of the slot either. I am using "nuxt": "^4.4.2" and "@dxup/nuxt": "^0.5.3" together with "@dxup/unimport": "^0.1.2".

// nuxt.config.ts
// ...

  // dxup setup
  experimental: {
    typescriptPlugin: true,
  },
  dxup: {
    features: {
      // enable namedSlots in pages
      namedLayoutSlots: true,
    },
  },

// ...

@KazariEX

KazariEX commented Jul 10, 2026

Copy link
Copy Markdown
Owner Author

Does your layout component render default slots? Under the current implementation, not having a default slot will cause the page to hang forever. If that's not the reason, I think it's best to provide a minimal reproduction for me to investigate.

@udrzbarik

udrzbarik commented Jul 10, 2026

Copy link
Copy Markdown

Hi, yes, I have a default slot as well, here. is a the whole version of the layout.

<template>
  <NuxtLayout name="dashboard" :submenu :breadcrumbs>
    
    <template v-if="$slots.breadcrumbs" #breadcrumbs>
      <slot name="breadcrumbs" />
    </template>

    <div class="px-20 py-14">
      <div :class="['mx-auto w-full', layoutClass]">
        <slot />
      </div>
    </div>
  </NuxtLayout>
</template>

I even tried turning off the v-if to no avail.

@KazariEX

KazariEX commented Jul 10, 2026

Copy link
Copy Markdown
Owner Author

I’m not sure exactly where the component file you listed are located, but this feature currently only applies to components in the layouts directory.

Maybe that's a pattern I'm not familiar with, but why is your layout wrapped in a <NuxtLayout> at the top level? Perhaps nested layout?

@udrzbarik

udrzbarik commented Jul 10, 2026

Copy link
Copy Markdown

The ResolutionBreadcrumbs.vue is in my /pages/[...slug]/components which gets (ignored) via the following Nuxt setting and auto-imported via the components Nuxt setting. The previous component (with the nested layout) is /layouts/legislative.vue.

// nuxt.config.ts
pages: {
  // Ignore `/components` so that it doesn't get picked up as a route
  pattern: ['**/*.vue', '!**/components/**']
}

As for why I have nested Layouts, I have a main layout called dashboard which has a general set of components, such as the sidebar, breadcrumbs, etc. and then I have app section specific layouts nested inside of it, because otherwise I'd have to define some settings and components on each page. This way, I just define it once in the nested layout which allows me to:

  1. Get the benefit of having a core dashboard template
  2. Don't have to re-write redundant code in each page just because I'd be using dashboard template

I'm not sure if this answers your questions, but it's due to the specificity. The interesting thing is, that the pattern above where I wrap the same component with a named slot and the <NuxtLayout/> works.

<template>
  <NuxtLayout name="legislative" v-bind="layoutProps">
  
    <!--- This works --->
    <template #breadcrumbs>
       <ResolutionBreadcrumbs :canSaveDraft @draftSubmit="handleDraftSubmit" />
    </template>
  </NuxtLayout>
</template>

<script setup lang="ts">
// but seems to require ..

definePageMeta({
  layout: false
})

</script>

@KazariEX

KazariEX commented Jul 10, 2026

Copy link
Copy Markdown
Owner Author

Does this patch fix the issue?

diff --git a/dist/runtime/layouts.mjs b/dist/runtime/layouts.mjs
index 9d581e0014a0b3901a3900972c5921b5966ffa62..1acc2b64c075ef0fd294623912ff11d34e2c0064 100644
--- a/dist/runtime/layouts.mjs
+++ b/dist/runtime/layouts.mjs
@@ -1,4 +1,4 @@
-import { defineComponent, h, inject, provide, shallowRef } from "vue";
+import { defineComponent, getCurrentInstance, h, inject, provide, shallowRef } from "vue";
 import { NuxtLayout } from "#build/dxup/layouts.mjs";
 //#region src/module/named-layout-slots/runtime/layouts.ts
 const injectionKey = Symbol.for("dxup:layout-slots");
@@ -24,7 +24,8 @@ const LayoutSlot = defineComponent({
 	} },
 	setup(props, ctx) {
 		const { slots, ready } = inject(injectionKey);
-		const render = () => slots.value[props.name]?.(ctx.attrs);
+		const currentInstance = getCurrentInstance();
+		const render = () => (slots.value[props.name] ?? currentInstance?.parent?.slots[props.name])?.(ctx.attrs);
 		if (import.meta.server && !slots.value[props.name]) return ready.then(() => render);
 		return render;
 	}

I suspect v-if="$slots.breadcrumbs" will still prevent it from working (due to design limitation), so you might need to remove it temporarily.

@danielroe danielroe mentioned this pull request Jul 18, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants