nuxt-modules / nuxt-modules/ionic
`useHead` does not resolve reactive input on the client
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 457
- Forks
- 53
- Avg merge
- 7h 32m
- Merged PRs (30d)
- 21
Description
🐛 The bug
The module's useHead replacement (src/runtime/composables/head.ts) never resolves reactive input. All of its activeHead.push() / patch() sites hand the raw object to unhead, while @unhead/vue's own clientUseHead runs walkResolver(input, VueResolver) inside a watchEffect.
This only bites on the client: @unhead/vue/server creates the head with propResolvers: [VueResolver], so refs resolve inside unhead during SSR. The client head has no prop resolver, so resolution exists only in the composable this module replaces.
Two consequences:
-
Crash — a ref on
innerHTML/textContentof astyle/scriptreaches unhead'snormalizeProps, which JSON-stringifies object values:TypeError: Converting circular structure to JSON --> starting at object with constructor 'ComputedRefImpl' | property 'dep' -> object with constructor 'Dep' --- property 'computed' closes the circle at JSON.stringify (<anonymous>) at normalizeProps (unhead.js:148:28) at normalizeTag (unhead.js:175:15) at normalizeEntryToTags (unhead.js:209:56) at resolveTags (unhead.js:362:16) at _renderDOMHead (unhead.js:183:21)It is thrown inside the debounced
_renderDOMHead, so the whole head render is abandoned: the tag never lands in the DOM, and it can abort router start. -
Silent, for every other prop: the ref object is used instead of its
.value, and since there is nowatchEffect, the tag never updates when the ref changes. Client-side reactive head does not work, though unhead v3 documents it as supported.
@nuxt/ui triggers case 1 out of the box: its colors plugin calls useHead({ style: [{ innerHTML: root, id: 'nuxt-ui-colors' }] }) where root is a computed. Any @nuxtjs/ionic + @nuxt/ui app loses its theme colors style. @nuxtjs/i18n's useLocaleHead() and @nuxtjs/seo hit case 2.
🛠️ To reproduce
https://github.com/Carpediem94/nuxt-ionic-head-repro
🌈 Expected behaviour
Reactive input resolved before being pushed to unhead, and kept in sync as it changes, matching the behaviour of @unhead/vue's useHead.
ℹ️ Additional context
@nuxtjs/ionic 1.0.2, nuxt 4.5.2, @unhead/vue 3.3.1, @nuxt/ui 4.10.0.
Same error class as https://github.com/unjs/unhead/issues/869, but that fix only covered the SSR streaming serializer.
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in src/runtime/composables/head.ts, inspecting each activeHead.push() and patch() site alongside @unhead/vue's clientUseHead behavior and its watchEffect-based resolution. Use the linked reproduction to verify that reactive props are resolved on the client, update when their refs change, and no longer prevent head tags from reaching the DOM.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- nuxtjs, typescript
- Domain
- frontend, web-dev
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 74/100