Skip to content

fix(composables): stop shadowing Nuxt's built-in useLayout (#384) - #388

Open
AndreyYolkin wants to merge 8 commits into
mainfrom
fix/384-uselayout-collision
Open

fix(composables): stop shadowing Nuxt's built-in useLayout (#384)#388
AndreyYolkin wants to merge 8 commits into
mainfrom
fix/384-uselayout-collision

Conversation

@AndreyYolkin

Copy link
Copy Markdown
Contributor

Closes #384.

Problem

Nuxt 4.5.0 shipped a built-in useLayout. This module also auto-imports Vuetify's unrelated useLayout, and our registration silently won the name, so on every Nuxt >= 4.5 project with default options the framework's own composable became unreachable.

The two are not interchangeable:

Nuxt 4.5 Vuetify
returns Readonly<ComputedRef<LayoutName>> { getLayoutItem, mainRect, mainStyles }
outside <VApp> works throws [Vuetify] Could not find injected layout

Nuxt's documented example calls useLayout() in app.vue, and <VApp> normally lives in a layout below it — so the documented Nuxt pattern crashed for our users.

Reproduced on nuxt 4.5.2 before the fix (nuxi prepare in apps/playground):

WARN [NUXT_B6002] useLayout is already auto-imported by Nuxt as a built-in,
     and overriding it will likely cause issues.
     fix: Rename useLayout in vuetify so it no longer collides with the built-in auto-import.
WARN Duplicated imports "useLayout", the one from "#app/composables/layout"
     has been ignored and "vuetify" is used

Fix

moduleOptions.prefixComposables gains a new value 'auto', which becomes the default:

value behaviour
'auto' (new default) prefix only composables whose name a Nuxt/Vue auto-import source already owns
true / false / string[] unchanged

The invariant, which the implementation is written against:

'auto' prefixes exactly the set Nuxt would report as NUXT_B6002, and nothing beyond it.

Detection is dynamic, so there are no Nuxt or Vuetify version gates — on Nuxt < 4.5 the intersection is empty and nothing changes.

Why hooks instead of addImports

addImports needs the finished array at module-setup time, but the reserved names only exist once imports:sources fires on modules:done (measured: nuxt.options.imports.presets holds a single preset at our setup, and no useLayout). So we snapshot in imports:sources and push in imports:extend — which is literally what addImports does internally, one step later.

Scope

Only framework-owned sources count (from starting with #, or vue/vue-demi/vue-router). Third-party module presets are deliberately excluded, so the module's auto-import names never depend on which other modules a project installed. A probe of apps/playground (which has @nuxtjs/i18n) found 35 presets / 214 names, with useLayout the only intersection against our 11 composables.

Verification

nuxi prepare in apps/playground on nuxt 4.5.2, after:

before after
NUXT_B6002 + Duplicated imports 2 + 2 0
useLayout import('vuetify') nuxt/dist/app/composables/layout
useVLayout import('vuetify')
  • 24 new unit tests for the preset walking and the prefix decision; integration assertion on the basic fixture, checked to fail when prefixComposables: false.
  • Full suite 19 files / 82 tests, lint clean, prepack green.
  • A dev-only info line names the rename and how to override it; verified visible under nuxi dev and silent under prepare.

Upgrading

Projects that used Vuetify's useLayout should rename those call sites to useVLayout. TypeScript flags them, since Nuxt's composable returns a different type. Documented in the composables guide.

Code review found the preset walker diverged from unimport's own
`resolvePreset`: nested presets wrongly inherited the parent `from`,
and tuple entries used nullish instead of truthy fallback, so an empty
alias was collected as a name.

Also hardens the prefix decision, which returned the raw mode and so
renamed every composable when given an unrecognised string.
Nuxt 4.5 ships its own `useLayout`, and the module's auto-import silently
won the name, making the framework composable unreachable and crashing any
call outside `<VApp>`.

`prefixComposables` now defaults to `'auto'`, which prefixes only the
composables whose name a Nuxt or Vue auto-import source already owns — the
same set Nuxt reports as NUXT_B6002. Explicit `true`, `false` and array
values are unchanged.

Closes #384
@AndreyYolkin AndreyYolkin added this to the 1.0.0 milestone Aug 23, 2026
@AndreyYolkin AndreyYolkin added bug Something isn't working area: config Module configuration & options priority: p2: high High: common bug, strong friction labels Aug 23, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: config Module configuration & options bug Something isn't working priority: p2: high High: common bug, strong friction

Projects

None yet

Development

Successfully merging this pull request may close these issues.

prefixComposables in options is obsolete since nuxt 4.5 introduced useLayout

1 participant