diff --git a/docs/1.getting-started/4.styling.md b/docs/1.getting-started/4.styling.md index 9178f331a..2059a445a 100644 --- a/docs/1.getting-started/4.styling.md +++ b/docs/1.getting-started/4.styling.md @@ -129,7 +129,7 @@ useHead({ }) ``` -Nuxt uses `unhead` under the hood, and you can refer to its full documentation [here](https://unhead.harlanzw.com/). +Nuxt uses `unhead` under the hood, and you can refer to its full documentation [here](https://unhead.unjs.io/). ### Modifying The Rendered Head With A Nitro Plugin diff --git a/docs/1.getting-started/5.seo-meta.md b/docs/1.getting-started/5.seo-meta.md index 5a9a336e6..342465f01 100644 --- a/docs/1.getting-started/5.seo-meta.md +++ b/docs/1.getting-started/5.seo-meta.md @@ -36,7 +36,7 @@ Shortcuts are available to make configuration easier: `charset` and `viewport`. ## `useHead` The [`useHead`](/docs/api/composables/use-head) composable function allows you to manage your head tags in a programmatic and reactive way, -powered by [Unhead](https://unhead.harlanzw.com/). +powered by [Unhead](https://unhead.unjs.io/). As with all composables, it can only be used with a components `setup` and lifecycle hooks. @@ -128,6 +128,29 @@ interface MetaObject { See [@unhead/schema](https://github.com/unjs/unhead/blob/main/packages/schema/src/schema.ts) for more detailed types. +## Pitfalls + +### Async useHead / useSeoMeta + +A common use case when working with SEO composables is to fetch data from an API and use it to set the page title. +However, running `useHead` after an `await` will cause the Nuxt app context to be lost. + +To solve this, you can either: + +- Use `runWithContext` to preserve to context: + +```ts +const nuxtApp = useNuxtApp(); +const data = await $fetch(`/api/data/${key}`) // context is lost from the await +nuxtApp.runWithContext(() => { + useHead({ + title: `${data.title}`, + }) +}); +``` + +- Enable the experimental flag `asyncContext` in your nuxt.config. + ## Features ### Reactivity diff --git a/docs/3.api/1.composables/use-head-safe.md b/docs/3.api/1.composables/use-head-safe.md index 5f44f93a7..62dc5c2f3 100644 --- a/docs/3.api/1.composables/use-head-safe.md +++ b/docs/3.api/1.composables/use-head-safe.md @@ -24,7 +24,7 @@ useHeadSafe({ // ``` -Read more on [unhead documentation](https://unhead.harlanzw.com/guide/composables/use-head-safe). +Read more on [unhead documentation](https://unhead.unjs.io/usage/composables/use-head-safe). ## Type diff --git a/docs/3.api/1.composables/use-head.md b/docs/3.api/1.composables/use-head.md index 71475a660..6a03e5ada 100644 --- a/docs/3.api/1.composables/use-head.md +++ b/docs/3.api/1.composables/use-head.md @@ -4,7 +4,7 @@ description: useHead customizes the head properties of individual pages of your # useHead -The [`useHead`](/docs/api/composables/use-head) composable function allows you to manage your head tags in a programmatic and reactive way, powered by [Unhead](https://unhead.harlanzw.com/). If the data comes from a user or other untrusted source, we recommend you check out [`useHeadSafe`](/docs/api/composables/use-head-safe) +The [`useHead`](/docs/api/composables/use-head) composable function allows you to manage your head tags in a programmatic and reactive way, powered by [Unhead](https://unhead.unjs.io/). If the data comes from a user or other untrusted source, we recommend you check out [`useHeadSafe`](/docs/api/composables/use-head-safe) :ReadMore{link="/docs/getting-started/seo-meta"} diff --git a/packages/nuxt/package.json b/packages/nuxt/package.json index c2d5de546..37ed453a4 100644 --- a/packages/nuxt/package.json +++ b/packages/nuxt/package.json @@ -58,9 +58,9 @@ "@nuxt/telemetry": "^2.4.1", "@nuxt/ui-templates": "^1.3.1", "@nuxt/vite-builder": "workspace:../vite", - "@unhead/dom": "^1.3.9", - "@unhead/ssr": "^1.3.9", - "@unhead/vue": "^1.3.9", + "@unhead/dom": "^1.5.0", + "@unhead/ssr": "^1.5.0", + "@unhead/vue": "^1.5.0", "@vue/shared": "^3.3.4", "acorn": "8.10.0", "c12": "^1.4.2", diff --git a/packages/nuxt/src/core/runtime/nitro/renderer.ts b/packages/nuxt/src/core/runtime/nitro/renderer.ts index 13c053a3c..a98f7839a 100644 --- a/packages/nuxt/src/core/runtime/nitro/renderer.ts +++ b/packages/nuxt/src/core/runtime/nitro/renderer.ts @@ -23,7 +23,7 @@ import { defineRenderHandler, getRouteRules, useRuntimeConfig, useStorage } from import { useNitroApp } from '#internal/nitro/app' import type { Link, Script } from '@unhead/vue' -import { createServerHead } from '@unhead/vue' +import { createServerHead, setFailedInjectionHandler as setFailedHeadInjectionHandler } from '@unhead/vue' // @ts-expect-error virtual file import unheadPlugins from '#internal/unhead-plugins.mjs' // eslint-disable-next-line import/no-restricted-paths @@ -247,6 +247,14 @@ export default defineRenderHandler(async (event): Promise { + console.warn('Unhead has fallen back to a shared context. Consider wrapping your code with nuxtApp.runWithContext. Learn more at https://nuxt.com/docs/getting-started/seo-meta#pitfalls.') + // dump stacktrace, we don't want to trigger a blocking SSR error + // remove the Error on first line + console.warn(new Error().stack!.split('\n').slice(5).join('\n')) + }) + } // needed for hash hydration plugin to work const headEntryOptions: HeadEntryOptions = { mode: 'server' } head.push(appHead, headEntryOptions) diff --git a/packages/schema/package.json b/packages/schema/package.json index f8618b6a9..296fd9f54 100644 --- a/packages/schema/package.json +++ b/packages/schema/package.json @@ -32,7 +32,7 @@ "@types/file-loader": "5.0.1", "@types/pug": "2.0.6", "@types/sass-loader": "8.0.5", - "@unhead/schema": "1.3.9", + "@unhead/schema": "1.5.0", "@vitejs/plugin-vue": "4.3.3", "@vitejs/plugin-vue-jsx": "3.0.2", "@vue/compiler-core": "3.3.4", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8fd266903..1cae12144 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -264,14 +264,14 @@ importers: specifier: ^14.18.0 || >=16.10.0 version: 18.17.11 '@unhead/dom': - specifier: ^1.3.9 - version: 1.3.9 + specifier: ^1.5.0 + version: 1.5.0 '@unhead/ssr': - specifier: ^1.3.9 - version: 1.3.9 + specifier: ^1.5.0 + version: 1.5.0 '@unhead/vue': - specifier: ^1.3.9 - version: 1.3.9(vue@3.3.4) + specifier: ^1.5.0 + version: 1.5.0(vue@3.3.4) '@vue/shared': specifier: ^3.3.4 version: 3.3.4 @@ -479,8 +479,8 @@ importers: specifier: 8.0.5 version: 8.0.5 '@unhead/schema': - specifier: 1.3.9 - version: 1.3.9 + specifier: 1.5.0 + version: 1.5.0 '@vitejs/plugin-vue': specifier: 4.3.3 version: 4.3.3(vite@4.4.9)(vue@3.3.4) @@ -2526,6 +2526,7 @@ packages: dependencies: is-glob: 4.0.3 micromatch: 4.0.5 + napi-wasm: 1.1.0 bundledDependencies: - napi-wasm @@ -3166,41 +3167,41 @@ packages: eslint-visitor-keys: 3.4.3 dev: true - /@unhead/dom@1.3.9: - resolution: {integrity: sha512-bTbPFjXjmk8MC0cBC+7Bgf0Mcw62gsE2XqOhMH/qQo6NP4vR2XGxqy054Y7MGurznR1JVAqxUiU3cR/oxWFk3g==} + /@unhead/dom@1.5.0: + resolution: {integrity: sha512-zJG35T4jz4i+r5t0+tiFgwY4+F+WLqrdZ2PMRaMXn5noSDMm1q+1X3EhTEgCKq20g+ktXUxv5nhQuDblO6L2pg==} dependencies: - '@unhead/schema': 1.3.9 - '@unhead/shared': 1.3.9 + '@unhead/schema': 1.5.0 + '@unhead/shared': 1.5.0 dev: false - /@unhead/schema@1.3.9: - resolution: {integrity: sha512-iIa0dczd2qTOxwYZbVR+iAKdlELnLTlKSFsN/YuJ/33sRi5VFa9D8TDBEPLec9gpcjB/bH0FhERfR4bb4UbRuA==} + /@unhead/schema@1.5.0: + resolution: {integrity: sha512-oalJHsxpcWGXUML62CwpFJaSrbKKbjU3c1eizu7d9/RJS42KKT1LVBXbKrtbR0kNyj4Fbf+MD/DwqZm4cUBcuA==} dependencies: hookable: 5.5.3 zhead: 2.0.10 - /@unhead/shared@1.3.9: - resolution: {integrity: sha512-lBXK1gzsg3XOnsOgYUVTT2RKOvM+AB0myDXkwQb0jsJB3Tc1qVOSz9JAOR+ZGrosSr7+Iv91+Fu/0E+knxaj2Q==} + /@unhead/shared@1.5.0: + resolution: {integrity: sha512-34qeSrkKz/KfkD/VR2oWpeOydH5M+8NqNBGwdM9vO/SNeBs+ZCEEO57ASJTSRjjve/rzcBhK6nQG6tOCJtAV1g==} dependencies: - '@unhead/schema': 1.3.9 + '@unhead/schema': 1.5.0 dev: false - /@unhead/ssr@1.3.9: - resolution: {integrity: sha512-FTt4IQOAxHiSfRM7IoJJiFnUEBH8CG5zkJOQ/LydG19QpYa9/AGOi4xvngeCr++1as51p2hWoRO6gPxSRhV8cA==} + /@unhead/ssr@1.5.0: + resolution: {integrity: sha512-lCGUuT5ZDCciyJo4CtM9ZKIB7i2y+FbC+NDi/yUGWnhrwXzKWGBxjktjspWfrwRIX2U1SIFaJk5xoYTMwguUXg==} dependencies: - '@unhead/schema': 1.3.9 - '@unhead/shared': 1.3.9 + '@unhead/schema': 1.5.0 + '@unhead/shared': 1.5.0 dev: false - /@unhead/vue@1.3.9(vue@3.3.4): - resolution: {integrity: sha512-rVAsRLBc+3Y//NRmr7vmRs5yhIf65jYSvcj0V5DtDfDwql7BbGgc3VIIEvY0+EjLQuNsS5kxwm78LSPCIl/3Xw==} + /@unhead/vue@1.5.0(vue@3.3.4): + resolution: {integrity: sha512-Xz8asfMiD6BAezQXR8XN7JdVdTqcX4T9iv0TL1zSk6SIpdiARLiQKU8inQRxOAxfxksW1+p3BU6siB2CVOcbwQ==} peerDependencies: vue: '>=2.7 || >=3' dependencies: - '@unhead/schema': 1.3.9 - '@unhead/shared': 1.3.9 + '@unhead/schema': 1.5.0 + '@unhead/shared': 1.5.0 hookable: 5.5.3 - unhead: 1.3.9 + unhead: 1.5.0 vue: 3.3.4 dev: false @@ -7063,7 +7064,7 @@ packages: graceful-fs: 4.2.11 /jstransformer@1.0.0: - resolution: {integrity: sha512-C9YK3Rf8q6VAPDCCU9fnqo3mAfOH6vUGnMcP4AQAYIEpWtfGLpwOTmZ+igtdK5y+VvI2n3CyYSzy4Qh34eq24A==} + resolution: {integrity: sha1-7Yvwkh4vPx7U1cGkT2hwntJHIsM=} dependencies: is-promise: 2.2.2 promise: 7.3.1 @@ -7947,6 +7948,9 @@ packages: engines: {node: ^14 || ^16 || >=18} hasBin: true + /napi-wasm@1.1.0: + resolution: {integrity: sha512-lHwIAJbmLSjF9VDRm9GoVOy9AGp3aIvkjv+Kvz9h16QR3uSVYH78PNQUnT2U4X53mhlnV2M7wrhibQ3GHicDmg==} + /natural-compare-lite@1.4.0: resolution: {integrity: sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==} dev: true @@ -10206,7 +10210,7 @@ packages: engines: {node: '>=0.6'} /token-stream@1.0.0: - resolution: {integrity: sha512-VSsyNPPW74RpHwR8Fc21uubwHY7wMDeJLys2IX5zJNih+OnAnaifKHo+1LHT7DAdloQ7apeaaWg8l7qnf/TnEg==} + resolution: {integrity: sha1-zCAOqyYT9BZtJ/+a/HylbUnfbrQ=} dev: false /totalist@1.1.0: @@ -10461,12 +10465,12 @@ packages: node-fetch-native: 1.4.0 pathe: 1.1.1 - /unhead@1.3.9: - resolution: {integrity: sha512-vzWZJW8l6dlNM5egJs3c7NMHWZ+iw2x7jCZtU2rrhwFINlKCaA3J42fvOeDxx6t5QR9dfZ96HF2AeNlCcPT+bQ==} + /unhead@1.5.0: + resolution: {integrity: sha512-dCbAcYUA5DeYg8+1UrTNhah8OnScuz6Bxc6NmzCl4gWl513aQDzSwRoNEKEhZ/j1vmXVwerUUZdKHTfNcS5NhQ==} dependencies: - '@unhead/dom': 1.3.9 - '@unhead/schema': 1.3.9 - '@unhead/shared': 1.3.9 + '@unhead/dom': 1.5.0 + '@unhead/schema': 1.5.0 + '@unhead/shared': 1.5.0 hookable: 5.5.3 dev: false