title | description | navigation | ||
---|---|---|---|---|
Migrate to Unhead v2 |
Learn about how to migrate to Unhead v2 from v1 |
|
The goal of Unhead v2 was to remove deprecations and remove the implicit context implementation.
Unhead v2 is mostly fully backwards compatible with Unhead v1.
While not recommended, if upgrading is not possible for you, you can change your imports to the following:
::code-group
-import { createServerHead, useHead } from 'unhead'
+import { createServerHead, useHead } from 'unhead/legacy'
-import { createServerHead, useHead } from '@unhead/vue'
+import { createServerHead, useHead } from '@unhead/vue/legacy'
::
This will be removed in a future minor version, so you should lock your dependencies to the version that works for you.
🚦 Impact Level: Critical
::tip Nuxt should not be effected by this change. ::
createServerHead()
{lang="ts"} andcreateHead()
{lang="ts"} exports fromunhead
are removed
The path where you import createHead
from has been updated to be a subpath export.
Please follow the updated installation instructions or simply update the import to use the subpath.
::TabComparison
Client bundle:
-import { createServerHead } from 'unhead'
+import { createHead } from 'unhead/client'
// avoids bundling server plugins
createHead()
Server bundle:
-import { createServerHead } from 'unhead'
+import { createHead } from 'unhead/server'
// avoids bundling server plugins
-createServerHead()
+createHead()
Client bundle:
-import { createHead } from '@unhead/vue'
+import { createHead } from '@unhead/vue/client'
import { createApp } from 'vue'
const app = createApp()
const head = createHead()
app.use(head)
Server bundle:
-import { createServerHead } from '@unhead/vue'
+import { createHead } from '@unhead/vue/server'
import { createApp } from 'vue'
const app = createApp()
-const head = createServerHead()
+const head = createHead()
app.use(head)
::
🚦 Impact Level: Critical
::tip Nuxt should not be effected by this change. ::
getActiveHead()
{lang="ts"},activeHead
{lang="ts"} exports are removed- Vue Only:
setHeadInjectionHandler()
{lang="ts"} is removed - Vue Only: Error may be thrown when using
useHead()
{lang="ts"} after async operations
The implicit context implementation kept a global instance of Unhead available so that you could use the useHead()
{lang="ts"} composables
anywhere in your application.
useHead({
title: 'This just worked!'
})
While safe client side, this was a leaky abstraction server side and led to memory leaks in some cases.
In v2, the core composables no longer have access to the Unhead instance. Instead, you must pass the Unhead instance to the composables.
::note
Passing the instance is only relevant if you're importing from unhead
. In JavaScript frameworks we tie the context to the framework itself so you
don't need to worry about this.
::
::code-group
import { useHead } from 'unhead'
// example of getting the instance
const unheadInstance = useMyApp().unhead
useHead(unheadInstance, {
title: 'Looks good'
})
import { useHead } from 'unhead'
useHead({
title: 'Just worked! But with SSR issues'
})
::
For frameworks users, you may run into issues with the context being lost.
::code-group
<script setup lang="ts">
// In Vue this happens in lifecycle hooks where we have async operations.
onMounted(async () => {
await fetchSomeData()
useHead({
title: 'This will not work'
})
})
</script>
::
If you're getting errors on your useHead()
{lang="ts"} about context, check the new documentation:
🚦 Impact Level: High
For legacy support with Vue Meta we allowed end users to provide deprecated properties: vmid
, hid
, children
and body
.
You must either update these properties to the appropriate replacement, remove them, or you can use the DeprecationsPlugin
.
Meta tags with vmid
, hid
These are already deduped magically so you can safely remove them there.
useHead({
meta: [{
name: 'description',
- vmid: 'description'
- hid: 'description'
}]
})
Other Tags with vmid
, hid
Use key
if you need the deduplication feature. This is useful for tags that may change from server to client
rendering.
useHead({
script: [{
- vmid: 'my-key'
- hid: 'my-key'
+ key: 'my-key',
}]
})
Using children
The children
key is a direct replacement of innerHTML
which you should use instead.
::Caution
When migrating your code ensure that you're not dynamically setting innerHTML
as this can lead to XSS vulnerabilities.
::
useHead({
script: [
{
- children: '..'
+ innerHTML: '..'
}
]
})
Using body
The body
key should be updated to use the Tag Position feature.
useHead({
script: [
{
- body: true
+ tagPosition: 'bodyClose'
}
]
})
Use Deprecations Plugin
import { createHead } from 'unhead'
import { DeprecationsPlugin } from 'unhead/optionalPlugins'
const unhead = createHead({
plugins: [DeprecationsPlugin]
})
🚦 Impact Level: Critical
Unhead v2 no longer supports Vue v2. If you're using Vue v2, you will need to lock your dependencies to the latest v1 version of Unhead.
🚦 Impact Level: Medium
If you have promises as input they will no longer be resolved, either await the promise before passing it along or register the optional promises plugin.
Option 1: Await Promise
useHead({
link: [
{
- href: import('~/assets/MyFont.css?url'),
+ href: await import('~/assets/MyFont.css?url'),
rel: 'stylesheet',
type: 'text/css'
}
]
})
Option 2: Promise Plugin
import { PromisePlugin } from 'unhead/optionalPlugins'
const unhead = createHead({
plugins: [PromisePlugin]
})
🚦 Impact Level: High
useScript()
{lang="ts"} composable is now exported from@unhead/scripts
- Script instance is no longer augmented as a proxy and promise
script.proxy
{lang="ts"} is rewritten for simpler, more stable behaviorstub()
{lang="ts"} and runtime hookscript:instance-fn
are removed
You will need to update any useScript()
{lang="ts"} calls to be from @unhead/scripts
.
::TabComparison
-import { useScript } from 'unhead'
+import { useScript } from '@unhead/scripts'
-import { useScript } from '@unhead/vue'
+import { useScript } from '@unhead/scripts/vue'
::
Legacy Subpath Export
For the underlying logic changes, you can opt-in the previous behavior by importing from the legacy subpath export.
import { useScript } from '@unhead/scripts/legacy'
// same behavior as v1
useScript()
import { useScript } from '@unhead/scripts/vue-legacy'
// same behavior as v1
useScript()
Replacing promise usage
If you're using the script as a promise you should instead opt to use the onLoaded()
functions.
const script = useScript()
-script.then(() => console.log('loaded')
+script.onLoaded(() => console.log('loaded'))
Replacing proxy usage
If you're accessing the underlying API directly from the script instance, you will now need to only access it from the .proxy
.
const script = useScript('..', {
use() { return { foo: [] } }
})
-script.foo.push('bar')
+script.proxy.foo.push('bar')
Replacing stub()
If you were using stub for anything you should replace this with either custom use()
behavior.
const script = useScript('...', {
- stub() { return { foo: import.meta.server ? [] : undefined } }
})
+script.proxy = {} // your own implementation
🚦 Impact Level: :UBadge{color="success" variant="subtle" size="sm" label="Low"}
An optional Capo.js plugin was added to Unhead, in v2 we make this the default sorting behavior.
::warning As all head tags may be re-ordered this will break any snapshot tests that you have in place and in some rare cases may lead to performance regressions. ::
You can opt-out of Capo.js sorting by providing the option.
createHead({
disableCapoSorting: true,
})