nuxt / nuxt/ui

Function-based slot override (() => class) in app.config.ts merges instead of replacing when defined in multiple extended Nuxt layers

Open
#6,928 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

triage
Dominant language
TypeScript
Stars
6.9k
Forks
1.1k
Avg merge
1d 7h
Merged PRs (30d)
57

Description

Environment
  • Nuxt 4.5.2
  • NuxtUi 4.11.0
Is this bug related to Nuxt or Vue?

Nuxt

Package

v4.x

Version

v4.11.0

Reproduction

layers/base/app.config.ts:

export default defineAppConfig({
  ui: {
    drawer: {
      slots: {
        header: () => 'foo-class'
      }
    }
  }
})

app/app.config.ts (App layer extending base):

export default defineAppConfig({
  ui: {
    drawer: {
      slots: {
        header: () => 'bar-class'
      }
    }
  }
})

nuxt.config.ts:

export default defineNuxtConfig({
  extends: ['./layers/base']
})
Description

Per the Customize components docs, setting a slot to a function in app.config.ts should replace the component's default classes for that slot instead of merging them:

By default, these classes are merged onto the component's defaults. You can also set a slot to a function to
replace them instead.

This works correctly in a single-layer project. However, when using Nuxt layers (extends) and the same slot is overridden with a function in more than one layer's app.config.ts, the replace behavior breaks — the final result appears to merge/concatenate the classes instead of the last-applied function fully replacing the previous one.

Additional context

Expected: header slot classes should be exactly bar-class without default Nuxt classes. (App layer's function should fully replace, receiving Base layer's resolved defaults as its argument if needed, per docs).

Actual: The resulting classes on header appear to include both bar-class and default classes from Nuxt.

Additional observation: If the function override exists in only one of the two layers (either Base or App, doesn't matter which), it correctly replaces the component's built-in defaults as documented. The issue only manifests when two layers both provide a function for the same slot.

Suspected cause: The layer app.config.ts merging (likely via defu) doesn't have special handling for merging two function values for the same key — unlike the intentional function-detection/replace logic that exists for a single config source vs. component defaults. This may result in unexpected composition behavior when two function overrides for the same slot are merged across layers.

Logs

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Reproduce the issue with layers/base/app.config.ts, app/app.config.ts, and nuxt.config.ts using the two function-based header overrides. Start by tracing Nuxt layer app.config merging, including the suspected defu behavior, and the slot function handling described in the Customize components docs. Done means the app-layer function produces exactly bar-class without the default classes, while still receiving resolved defaults if needed.

Written by the indexing model from the issue text.

Assessment

Tech stack
nuxtjs, typescript
Domain
frontend
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
68/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.