-
Notifications
You must be signed in to change notification settings - Fork 1k
Description
Environment
- @nuxt/ui: 4.4.0
- @nuxt/icon: 2.2.1
- defu: 6.1.4
- nuxt: 4.x
- Using pnpm workspaces with Nuxt layers
Is this bug related to Nuxt or Vue?
Nuxt
Package
v4.x
Version
v4.4.0
Reproduction
Monorepo with two packages:
packages/shared/nuxt.config.ts (base layer):
import { createResolver } from '@nuxt/kit'
const { resolve } = createResolver(import.meta.url)
export default defineNuxtConfig({
modules: ['@nuxt/ui'],
icon: {
customCollections: [{
prefix: 'local',
dir: resolve('./app/assets/icons')
}]
}
})
packages/main/nuxt.config.ts:
export default defineNuxtConfig({
extends: ['@my/shared'],
modules: ['@nuxt/ui']
})
Put any SVG files in packages/shared/app/assets/icons/ and run the dev server from the main package. The icons fail to load at runtime.
Root cause
The issue is in the registerModule helper in @nuxt/ui:
// @nuxt/ui/dist/module.mjs
async function registerModule(name, key, options) {
if (!hasNuxtModule(name)) {
await installModule(name, defu(nuxt.options[key], options));
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
} else {
nuxt.options[key] = defu(nuxt.options[key], options);
}
}
await registerModule("@nuxt/icon", "icon", { cssLayer: "components" });
installModule is called with defu(nuxt.options.icon, { cssLayer: "components" }) as inline options. This object includes customCollections: [entry] from the merged layer config.
Then inside @nuxt/kit's defineNuxtModule, the module options are created via:
defu(inlineOptions, nuxt.options[configKey], defaults)
Since defu concatenates arrays when both sources have the same array key, customCollections from inlineOptions and nuxt.options.icon are merged:
[entry] + [entry] → [entry, entry]
This results in:
-
The server bundle having duplicate 'local' keys in the collections object
-
appConfig.icon.customCollections being ["local", "local"]
-
The client-side setCustomIconsLoader being called twice for the same prefix
-
The server icon endpoint returning cached error responses
Suggested fix
The registerModule helper should only pass its own options to installModule, not the full nuxt.options[key]:
async function registerModule(name, key, options) {
if (!hasNuxtModule(name)) {
await installModule(name, options); // Let defineNuxtModule handle merging with nuxt.options[key]
} else {
nuxt.options[key] = defu(nuxt.options[key], options);
}
}Or alternatively, @nuxt/icon could deduplicate customCollections by prefix in its setup().
Workaround
Add clientBundle.includeCustomCollections: true to bypass the server endpoint:
icon: {
customCollections: [{
prefix: 'local',
dir: resolve('./app/assets/icons')
}],
clientBundle: {
includeCustomCollections: true
}
}
Description
When defining icon.customCollections in a Nuxt layer's nuxt.config.ts, the custom collections array gets duplicated at runtime, causing the client-side icon loader to fail with:
ERROR Failed to load custom icons Invalid data404
Additional context
No response
Logs