diff --git a/docs/content/2.guide/2.features/4.head-management.md b/docs/content/2.guide/2.features/4.head-management.md index cbeca416f0..7ffa13bd9a 100644 --- a/docs/content/2.guide/2.features/4.head-management.md +++ b/docs/content/2.guide/2.features/4.head-management.md @@ -49,6 +49,28 @@ The `titleTemplate` can either be a string, where `%s` is replaced with the titl Now, if you set the title to `My Page` with `useHead` on another page of your site, the title would appear as 'My Page - Site Title' in the browser tab. You could also pass `null` to default to the site title. +## Body Meta Tags + +::StabilityEdge{title="Body Meta Tags"} +:: + +You can use the `body: true` option on the `link` and `script` meta tags to append them to the end of the `` tag. + +For example: + +```vue + +``` + ## Meta Components Nuxt provides ``, `<Base>`, `<Script>`, `<Style>`, `<Meta>`, `<Link>`, `<Body>`, `<Html>` and `<Head>` components so that you can interact directly with your metadata within your component's template. diff --git a/packages/nuxt/src/core/runtime/nitro/renderer.ts b/packages/nuxt/src/core/runtime/nitro/renderer.ts index b046437ec0..7f157c76bf 100644 --- a/packages/nuxt/src/core/runtime/nitro/renderer.ts +++ b/packages/nuxt/src/core/runtime/nitro/renderer.ts @@ -171,6 +171,7 @@ export default eventHandler(async (event) => { bodyAppend: normalizeChunks([ `<script>window.__NUXT__=${devalue(ssrContext.payload)}</script>`, _rendered.renderScripts(), + // Note: bodyScripts may contain tags other than <script> renderedMeta.bodyScripts ]) } diff --git a/packages/nuxt/src/head/runtime/lib/vueuse-head.plugin.ts b/packages/nuxt/src/head/runtime/lib/vueuse-head.plugin.ts index 224a3facef..61620f1cdd 100644 --- a/packages/nuxt/src/head/runtime/lib/vueuse-head.plugin.ts +++ b/packages/nuxt/src/head/runtime/lib/vueuse-head.plugin.ts @@ -55,6 +55,13 @@ export default defineNuxtPlugin((nuxtApp) => { } if (process.server) { - nuxtApp.ssrContext.renderMeta = () => renderHeadToString(head) + nuxtApp.ssrContext.renderMeta = () => { + const meta = renderHeadToString(head) + return { + ...meta, + // resolves naming difference with NuxtMeta and @vueuse/head + bodyScripts: meta.bodyTags + } + } } }) diff --git a/test/basic.test.ts b/test/basic.test.ts index 524676238a..98ea1156a6 100644 --- a/test/basic.test.ts +++ b/test/basic.test.ts @@ -140,6 +140,7 @@ describe('head tags', () => { expect(headHtml).toMatch(/<html[^>]*class="html-attrs-test"/) expect(headHtml).toMatch(/<body[^>]*class="body-attrs-test"/) expect(headHtml).toContain('script>console.log("works with useMeta too")</script>') + expect(headHtml).toContain('<script src="https://a-body-appended-script.com" data-meta-body="true"></script></body>') const indexHtml = await $fetch('/') // should render charset by default diff --git a/test/fixtures/basic/pages/head.vue b/test/fixtures/basic/pages/head.vue index 61a6cbca9a..a34a3171bc 100644 --- a/test/fixtures/basic/pages/head.vue +++ b/test/fixtures/basic/pages/head.vue @@ -6,6 +6,12 @@ useHead({ bodyAttrs: { class: 'body-attrs-test' }, + script: [ + { + src: 'https://a-body-appended-script.com', + body: true + } + ], meta: [{ name: 'description', content: 'first' }] }) useHead({ charset: 'utf-16', meta: [{ name: 'description', content: computed(() => `${a.value} with an inline useHead call`) }] })