SEO

Configure shared site metadata, page-level SEO tags, favicon links, robots.txt and sitemap generation for a Nuxt project.

Installation

npx alixan-ui-nuxt add seo
This command copies app/config/site/site.ts, app/config/site/favicon.ts and app/composables/usePageMeta.ts. After copying, replace the example values in site.ts with your own domain, project name, default title, description and OG image.

Sitemap

Install Nuxt Sitemap before adding sitemap options to nuxt.config.ts.

npx nuxt module add @nuxtjs/sitemap

Sitemap helps search robots find available routes and understand which pages should be crawled. After deployment, check that your sitemap is available from the URL used in robots.txt.

For more detailed information, visit the Nuxt Sitemap documentation.

Site Config

app/config/site/site.ts
export const siteConfig = {
  // Replace with your production domain.
  url: 'https://nuxt.ui.alixan.kz',
  // Replace with your project name.
  name: 'Alixan UI',
  // Replace with your default page title.
  title: 'Alixan UI - Nuxt UI Components',
  // Replace with your default SEO description.
  description:
    'Modern, minimal and production-ready UI components for Nuxt 4, Vue 3 and TypeScript.',
  // Replace with your default Open Graph image.
  ogImage: 'https://nuxt.ui.alixan.kz/og-image.png',
}
nuxt.config.ts
import { siteConfig } from './app/config/site/site'

export default defineNuxtConfig({
  site: {
    url: siteConfig.url,
    name: siteConfig.name,
  },
  modules: ['@nuxtjs/sitemap'],
  sitemap: {
    autoLastmod: true,
  },
})

Initial Head

For favicon file generation, see the IconKitchen guide .

app/config/site/favicon.ts
export const favicon = [
	{
		rel: 'icon',
		href: '/favicon.ico',
		sizes: 'any',
	},
	{
		rel: 'apple-touch-icon',
		href: '/icons/apple-touch-icon.png',
	},
	{
		rel: 'manifest',
		href: '/site.webmanifest',
	},
]
app.vue
<script setup lang="ts">
import { favicon } from './config/site/favicon'

const { locale } = useI18n()

usePageMeta() // initialize default SEO meta

useHead({
  htmlAttrs: { lang: locale.value }, // initialize lang attr for html
  link: favicon, // initialize favicon
})
</script>

Page Meta

app/composables/usePageMeta.ts
usePageMeta centralizes title, description, Open Graph and Twitter tags. Pages only pass values that differ from defaults. It uses useI18n().t internally, so page values can be translation keys while siteConfig defaults can stay plain strings when missingWarn is disabled.
import { siteConfig } from '~/config/site/site'

interface PageMetaOptions {
  title?: string
  description?: string
  img?: string
}

export const usePageMeta = (payload?: PageMetaOptions): void => {
  const { t } = useI18n()
  const route = useRoute()
  const metaTitle = t(payload?.title || siteConfig.title)
  const metaDescription = t(payload?.description || siteConfig.description)
  const metaImage = payload?.img || siteConfig.ogImage

  useSeoMeta({
    title: metaTitle,
    description: metaDescription,
    ogType: 'website',
    ogSiteName: siteConfig.name,
    ogTitle: metaTitle,
    ogDescription: metaDescription,
    ogUrl: siteConfig.url + route.path,
    ogImage: metaImage,
    twitterCard: 'summary_large_image',
    twitterTitle: metaTitle,
    twitterDescription: metaDescription,
    twitterImage: metaImage,
  })
}
pages/button.vue
Call usePageMeta in each page. If a field is omitted, the default from siteConfig is used.
<script setup lang="ts">
usePageMeta({
  title: 'componentDocs.button.metaTitle',
  description: 'componentDocs.button.description',
})
</script>

Robots

Add public/robots.txt so crawlers can discover your sitemap. Replace the example domain with your production domain.

public/robots.txt
User-Agent: *
Disallow:

# Replace this domain with your own.
Sitemap: https://nuxt.ui.alixan.kz/sitemap_index.xml