2022-10-06 09:15:30 +00:00
---
navigation.icon: uil:file-search-alt
2022-10-12 17:00:17 +00:00
description: Improve your Nuxt app's SEO with powerful head config, composables and components.
2022-10-06 09:15:30 +00:00
---
2022-09-13 12:54:31 +00:00
# SEO and Meta
2021-07-15 11:28:04 +00:00
2022-10-12 17:00:17 +00:00
Improve your Nuxt app's SEO with powerful head config, composables and components.
2022-04-11 09:03:31 +00:00
2023-03-14 14:24:41 +00:00
## Defaults
2022-04-06 05:56:08 +00:00
2022-10-12 17:00:17 +00:00
Out-of-the-box, Nuxt provides sane defaults, which you can override if needed.
2022-06-03 15:37:56 +00:00
2022-10-12 17:00:17 +00:00
- `charset` : `utf-8`
- `viewport` : `width=device-width, initial-scale=1`
2022-06-03 15:37:56 +00:00
2023-03-14 14:24:41 +00:00
```ts [nuxt.config.ts]
2022-10-12 17:00:17 +00:00
export default defineNuxtConfig({
app: {
head: {
2023-03-14 14:24:41 +00:00
charset: 'utf-8',
viewport: 'width=device-width, initial-scale=1',
2022-06-03 15:37:56 +00:00
}
2022-10-12 17:00:17 +00:00
}
})
2022-06-03 15:37:56 +00:00
```
2023-03-14 14:24:41 +00:00
Providing an [`app.head` ](/docs/api/configuration/nuxt-config#head ) property in your [`nuxt.config.ts` ](/docs/guide/directory-structure/nuxt.config ) allows you to customize the head for your entire app.
::alert{type=info}
This method does not allow you to provide reactive data. We recommend to use `useHead()` in `app.vue` .
::
2022-06-03 15:37:56 +00:00
2023-03-14 14:24:41 +00:00
Shortcuts are available to make configuration easier: `charset` and `viewport` . You can also provide any of the keys listed below in [Types ](#types ).
## `useHead`
2022-08-02 11:43:25 +00:00
2023-07-07 16:24:09 +00:00
The [`useHead` ](/docs/api/composables/use-head ) composable function allows you to manage your head tags in a programmatic and reactive way,
2023-09-03 15:37:22 +00:00
powered by [Unhead ](https://unhead.unjs.io/ ).
2022-08-02 11:43:25 +00:00
2022-10-12 17:00:17 +00:00
As with all composables, it can only be used with a components `setup` and lifecycle hooks.
2022-08-02 11:43:25 +00:00
2023-03-14 14:24:41 +00:00
```vue [app.vue]
2022-10-12 17:00:17 +00:00
< script setup lang = "ts" >
2022-08-02 11:43:25 +00:00
useHead({
2022-10-12 17:00:17 +00:00
title: 'My App',
meta: [
{ name: 'description', content: 'My amazing site.' }
],
bodyAttrs: {
class: 'test'
},
2023-03-08 15:32:24 +00:00
script: [ { innerHTML: 'console.log(\'Hello world\')' } ]
2022-08-02 11:43:25 +00:00
})
< / script >
```
2023-03-14 14:24:41 +00:00
We recommend to take a look at the [`useHead` ](/docs/api/composables/use-head ) and [`useHeadSafe` ](/docs/api/composables/use-head-safe ) composables.
2022-10-12 17:00:17 +00:00
2023-05-05 13:50:06 +00:00
## `useSeoMeta` and `useServerSeoMeta`
2023-01-23 11:39:17 +00:00
2023-07-07 16:24:09 +00:00
The `useSeoMeta` and [`useServerSeoMeta` ](/docs/api/composables/use-server-seo-meta ) composables let you define your site's SEO meta tags as a flat object with full TypeScript support.
2023-01-23 11:39:17 +00:00
This helps you avoid typos and common mistakes, such as using `name` instead of `property` .
2023-03-14 14:24:41 +00:00
```vue [app.vue]
2023-01-23 11:39:17 +00:00
< script setup lang = "ts" >
2023-05-05 13:50:06 +00:00
useSeoMeta({
2023-01-23 11:39:17 +00:00
title: 'My Amazing Site',
ogTitle: 'My Amazing Site',
description: 'This is my amazing site, let me tell you all about it.',
ogDescription: 'This is my amazing site, let me tell you all about it.',
ogImage: 'https://example.com/image.png',
twitterCard: 'summary_large_image',
})
< / script >
```
2023-05-05 13:50:06 +00:00
Read more on the [`useSeoMeta` ](/docs/api/composables/use-seo-meta ) and [`useServerSeoMeta` ](/docs/api/composables/use-server-seo-meta ) composables.
2023-03-14 14:24:41 +00:00
2022-10-12 17:00:17 +00:00
## Components
2021-07-15 11:28:04 +00:00
2022-11-16 02:26:35 +00:00
Nuxt provides `<Title>` , `<Base>` , `<NoScript>` , `<Style>` , `<Meta>` , `<Link>` , `<Body>` , `<Html>` and `<Head>` components so that you can interact directly with your metadata within your component's template.
2021-07-15 11:28:04 +00:00
Because these component names match native HTML elements, it is very important that they are capitalized in the template.
`<Head>` and `<Body>` can accept nested meta tags (for aesthetic reasons) but this has no effect on _where_ the nested meta tags are rendered in the final HTML.
2022-04-04 10:56:10 +00:00
<!-- @case -police-ignore html -->
2023-03-14 14:24:41 +00:00
```vue [app.vue]
2023-07-18 10:31:45 +00:00
< script setup lang = "ts" >
2022-04-21 14:58:43 +00:00
const title = ref('Hello World')
< / script >
2021-07-15 11:28:04 +00:00
< template >
< div >
2022-04-21 14:58:43 +00:00
< Head >
< Title > {{ title }}< / Title >
< Meta name = "description" :content = "title" / >
< Style type = "text/css" children = "body { background-color: green; }" / >
< / Head >
< h1 > {{ title }}< / h1 >
2021-07-15 11:28:04 +00:00
< / div >
< / template >
```
2022-03-08 18:02:10 +00:00
2022-10-12 17:00:17 +00:00
## Types
2023-07-07 16:24:09 +00:00
Below are the non-reactive types used for [`useHead` ](/docs/api/composables/use-head ), [`app.head` ](/docs/api/configuration/nuxt-config#head ) and components.
2022-10-12 17:00:17 +00:00
```ts
interface MetaObject {
title?: string
titleTemplate?: string | ((title?: string) => string)
2023-03-08 15:32:24 +00:00
templateParams?: Record< string , string | Record < string , string > >
2022-10-12 17:00:17 +00:00
base?: Base
link?: Link[]
meta?: Meta[]
style?: Style[]
script?: Script[]
noscript?: Noscript[];
htmlAttrs?: HtmlAttributes;
bodyAttrs?: BodyAttributes;
}
```
2023-03-08 15:32:24 +00:00
See [@unhead/schema ](https://github.com/unjs/unhead/blob/main/packages/schema/src/schema.ts ) for more detailed types.
2022-10-12 17:00:17 +00:00
2023-09-03 15:37:22 +00:00
## 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.
2022-10-12 17:00:17 +00:00
## Features
### Reactivity
2023-03-15 11:33:22 +00:00
Reactivity is supported on all properties, as computed, getters and reactive.
2022-10-12 17:00:17 +00:00
2023-03-15 11:33:22 +00:00
It's recommended to use getters (`() => value`) over computed (`computed(() => value)`).
2022-10-12 17:00:17 +00:00
::code-group
```vue [useHead]
< script setup lang = "ts" >
2023-03-15 11:33:22 +00:00
const description = ref('My amazing site.')
2022-11-16 02:26:35 +00:00
2022-10-12 17:00:17 +00:00
useHead({
meta: [
2023-03-15 11:33:22 +00:00
{ name: 'description', content: description }
2022-10-12 17:00:17 +00:00
],
})
< / script >
```
2023-03-15 11:33:22 +00:00
```vue [useSeoMeta]
< script setup lang = "ts" >
2023-03-18 06:53:54 +00:00
const description = ref('My amazing site.')
2023-03-15 11:33:22 +00:00
useSeoMeta({
description
})
< / script >
```
2022-10-12 17:00:17 +00:00
```vue [Components]
2023-07-18 10:31:45 +00:00
< script setup lang = "ts" >
2023-03-15 11:33:22 +00:00
const description = ref('My amazing site.')
2022-10-12 17:00:17 +00:00
< / script >
2023-02-28 21:31:41 +00:00
2022-10-12 17:00:17 +00:00
< template >
2023-02-28 21:31:41 +00:00
< div >
2023-03-15 11:33:22 +00:00
< Meta name = "description" :content = "description" / >
2023-02-28 21:31:41 +00:00
< / div >
2022-10-12 17:00:17 +00:00
< / template >
```
::
2023-03-14 14:24:41 +00:00
### Title Template
2022-10-12 17:00:17 +00:00
You can use the `titleTemplate` option to provide a dynamic template for customizing the title of your site. for example, by adding the name of your site to the title of every page.
The `titleTemplate` can either be a string, where `%s` is replaced with the title, or a function.
If you want to use a function (for full control), then this cannot be set in your `nuxt.config` , and it is recommended instead to set it within your `app.vue` file, where it will apply to all pages on your site:
::code-group
```vue [useHead]
< script setup lang = "ts" >
2023-02-28 21:31:41 +00:00
useHead({
titleTemplate: (titleChunk) => {
return titleChunk ? `${titleChunk} - Site Title` : 'Site Title';
}
})
2022-10-12 17:00:17 +00:00
< / script >
```
::
2023-07-07 16:24:09 +00:00
Now, if you set the title to `My Page` with [`useHead` ](/docs/api/composables/use-head ) 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.
2022-10-12 17:00:17 +00:00
### Body Tags
2023-03-08 15:32:24 +00:00
You can use the `tagPosition: 'bodyClose'` option on applicable tags to append them to the end of the `<body>` tag.
2022-10-12 17:00:17 +00:00
For example:
2022-11-16 02:26:35 +00:00
```vue
< script setup lang = "ts" >
useHead({
script: [
{
src: 'https://third-party-script.com',
2023-03-14 14:24:41 +00:00
// valid options are: 'head' | 'bodyClose' | 'bodyOpen'
tagPosition: 'bodyClose'
2022-11-16 02:26:35 +00:00
}
]
})
< / script >
```
2022-10-12 17:00:17 +00:00
## Examples
2023-03-14 14:24:41 +00:00
### With `definePageMeta`
2022-03-08 18:02:10 +00:00
2023-07-07 16:24:09 +00:00
Within your [`pages/` directory ](/docs/guide/directory-structure/pages ), you can use `definePageMeta` along with [`useHead` ](/docs/api/composables/use-head ) to set metadata based on the current route.
2022-03-08 18:02:10 +00:00
2022-04-11 09:03:31 +00:00
For example, you can first set the current page title (this is extracted at build time via a macro, so it can't be set dynamically):
2022-03-08 18:02:10 +00:00
```vue{}[pages/some-page.vue]
2023-07-18 10:31:45 +00:00
< script setup lang = "ts" >
2022-03-08 18:02:10 +00:00
definePageMeta({
title: 'Some Page'
})
< / script >
```
2022-04-16 13:53:36 +00:00
And then in your layout file, you might use the route's metadata you have previously set:
2022-03-08 18:02:10 +00:00
```vue{}[layouts/default.vue]
2023-07-18 10:31:45 +00:00
< script setup lang = "ts" >
2022-03-08 18:02:10 +00:00
const route = useRoute()
2022-04-05 14:02:29 +00:00
useHead({
2022-11-14 10:32:05 +00:00
meta: [{ property: 'og:title', content: `App Name - ${route.meta.title}` }]
2022-03-08 18:02:10 +00:00
})
< / script >
```
2022-04-09 09:25:13 +00:00
2023-06-27 11:27:11 +00:00
:LinkExample{link="/docs/examples/features/meta-tags"}
2022-06-30 09:37:24 +00:00
2022-11-17 12:09:43 +00:00
:ReadMore{link="/docs/guide/directory-structure/pages/#page-metadata"}
2022-10-12 17:00:17 +00:00
2023-03-14 14:24:41 +00:00
### Dynamic Title
2022-10-12 17:00:17 +00:00
In the example below, `titleTemplate` is set either as a string with the `%s` placeholder or as a `function` , which allows greater flexibility in setting the page title dynamically for each route of your Nuxt app:
```vue [app.vue]
2023-07-18 10:31:45 +00:00
< script setup lang = "ts" >
2023-02-28 21:31:41 +00:00
useHead({
// as a string,
// where `%s` is replaced with the title
titleTemplate: '%s - Site Title',
// ... or as a function
titleTemplate: (productCategory) => {
return productCategory
? `${productCategory} - Site Title`
: 'Site Title'
}
})
2022-10-12 17:00:17 +00:00
< / script >
```
`nuxt.config` is also used as an alternative way of setting the page title. However, `nuxt.config` does not allow the page title to be dynamic. Therefore, it is recommended to use `titleTemplate` in the `app.vue` file to add a dynamic title, which is then applied to all routes of your Nuxt app.
2023-03-14 14:24:41 +00:00
### External CSS
2022-10-12 17:00:17 +00:00
2023-07-07 16:24:09 +00:00
The example below shows how you might enable Google Fonts using either the `link` property of the [`useHead` ](/docs/api/composables/use-head ) composable or using the `<Link>` component:
2022-10-12 17:00:17 +00:00
::code-group
```vue [useHead]
2022-11-16 02:26:35 +00:00
< script setup lang = "ts" >
2022-10-12 17:00:17 +00:00
useHead({
link: [
2022-11-16 02:26:35 +00:00
{
rel: 'preconnect',
2022-10-12 17:00:17 +00:00
href: 'https://fonts.googleapis.com'
},
2022-11-16 02:26:35 +00:00
{
rel: 'stylesheet',
href: 'https://fonts.googleapis.com/css2?family=Roboto& display=swap',
crossorigin: ''
2022-10-12 17:00:17 +00:00
}
]
})
< / script >
```
```vue [Components]
< template >
2023-02-28 21:31:41 +00:00
< div >
< Link rel = "preconnect" href = "https://fonts.googleapis.com" / >
< Link rel = "stylesheet" href = "https://fonts.googleapis.com/css2?family=Roboto&display=swap" crossorigin = "" / >
< / div >
2022-10-12 17:00:17 +00:00
< / template >
```
::