--- title: SEO and Meta description: Improve your Nuxt app's SEO with powerful head config, composables and components. navigation.icon: i-ph-file-search --- ## Defaults Out-of-the-box, Nuxt provides sensible defaults, which you can override if needed. ```ts twoslash [nuxt.config.ts] export default defineNuxtConfig({ app: { head: { charset: 'utf-8', viewport: 'width=device-width, initial-scale=1', } } }) ``` Providing an [`app.head`](/docs/api/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. ::important This method does not allow you to provide reactive data. We recommend to use `useHead()` in `app.vue`. :: Shortcuts are available to make configuration easier: `charset` and `viewport`. You can also provide any of the keys listed below in [Types](#types). ## `useHead` The [`useHead`](/docs/api/composables/use-head) composable function allows you to manage your head tags programmatically and reactively, powered by [Unhead](https://unhead.unjs.io). As with all composables, it can only be used with a components `setup` and lifecycle hooks. ```vue twoslash [app.vue] <script setup lang="ts"> useHead({ title: 'My App', meta: [ { name: 'description', content: 'My amazing site.' } ], bodyAttrs: { class: 'test' }, script: [ { innerHTML: 'console.log(\'Hello world\')' } ] }) </script> ``` We recommend to take a look at the [`useHead`](/docs/api/composables/use-head) and [`useHeadSafe`](/docs/api/composables/use-head-safe) composables. ## `useSeoMeta` The [`useSeoMeta`](/docs/api/composables/use-seo-meta) composable lets you define your site's SEO meta tags as a flat object with full TypeScript support. This helps you avoid typos and common mistakes, such as using `name` instead of `property`. ```vue twoslash [app.vue] <script setup lang="ts"> useSeoMeta({ 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> ``` :read-more{to="/docs/api/composables/use-seo-meta"} ## Components 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. Because these component names match native HTML elements, they must be capitalized in the template. `<Head>` and `<Body>` can accept nested meta tags (for aesthetic reasons) but this does not affect _where_ the nested meta tags are rendered in the final HTML. <!-- @case-police-ignore html --> ```vue [app.vue] <script setup lang="ts"> const title = ref('Hello World') </script> <template> <div> <Head> <Title>{{ title }}</Title> <Meta name="description" :content="title" /> <Style type="text/css" children="body { background-color: green; }" ></Style> </Head> <h1>{{ title }}</h1> </div> </template> ``` ## Types Below are the non-reactive types used for [`useHead`](/docs/api/composables/use-head), [`app.head`](/docs/api/nuxt-config#head) and components. ```ts interface MetaObject { title?: string titleTemplate?: string | ((title?: string) => string) templateParams?: Record<string, string | Record<string, string>> base?: Base link?: Link[] meta?: Meta[] style?: Style[] script?: Script[] noscript?: Noscript[]; htmlAttrs?: HtmlAttributes; bodyAttrs?: BodyAttributes; } ``` See [@unhead/schema](https://github.com/unjs/unhead/blob/main/packages/schema/src/schema.ts) for more detailed types. ## Features ### Reactivity Reactivity is supported on all properties, by providing a computed value, a getter, or a reactive object. ::code-group ```vue twoslash [useHead] <script setup lang="ts"> const description = ref('My amazing site.') useHead({ meta: [ { name: 'description', content: description } ], }) </script> ``` ```vue twoslash [useSeoMeta] <script setup lang="ts"> const description = ref('My amazing site.') useSeoMeta({ description }) </script> ``` ```vue [Components] <script setup lang="ts"> const description = ref('My amazing site.') </script> <template> <div> <Meta name="description" :content="description" /> </div> </template> ``` :: ### Title Template You can use the `titleTemplate` option to provide a dynamic template for customizing the title of your site. For example, you could add 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`. 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 twoslash [useHead] <script setup lang="ts"> useHead({ titleTemplate: (titleChunk) => { return titleChunk ? `${titleChunk} - Site Title` : 'Site Title'; } }) </script> ``` :: 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 'Site Title'. ### Body Tags You can use the `tagPosition: 'bodyClose'` option on applicable tags to append them to the end of the `<body>` tag. For example: ```vue twoslash <script setup lang="ts"> useHead({ script: [ { src: 'https://third-party-script.com', // valid options are: 'head' | 'bodyClose' | 'bodyOpen' tagPosition: 'bodyClose' } ] }) </script> ``` ## Examples ### With `definePageMeta` 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. 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): ```vue twoslash [pages/some-page.vue] <script setup lang="ts"> definePageMeta({ title: 'Some Page' }) </script> ``` And then in your layout file, you might use the route's metadata you have previously set: ```vue twoslash [layouts/default.vue] <script setup lang="ts"> const route = useRoute() useHead({ meta: [{ property: 'og:title', content: `App Name - ${route.meta.title}` }] }) </script> ``` :link-example{to="/docs/examples/features/meta-tags"} :read-more{to="/docs/guide/directory-structure/pages/#page-metadata"} ### Dynamic Title 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 twoslash [app.vue] <script setup lang="ts"> useHead({ // as a string, // where `%s` is replaced with the title titleTemplate: '%s - Site Title', }) </script> ``` ```vue twoslash [app.vue] <script setup lang="ts"> useHead({ // or as a function titleTemplate: (productCategory) => { return productCategory ? `${productCategory} - Site Title` : 'Site Title' } }) </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. ### External CSS 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: ::code-group ```vue twoslash [useHead] <script setup lang="ts"> useHead({ link: [ { rel: 'preconnect', href: 'https://fonts.googleapis.com' }, { rel: 'stylesheet', href: 'https://fonts.googleapis.com/css2?family=Roboto&display=swap', crossorigin: '' } ] }) </script> ``` ```vue [Components] <template> <div> <Link rel="preconnect" href="https://fonts.googleapis.com" /> <Link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Roboto&display=swap" crossorigin="" /> </div> </template> ``` ::