docs
  1. Developer Guide
  2. Features
  3. Seo

SEO

Overview

One of the biggest advantages of Nuxt is its built-in support for Search Engine Optimization (SEO), which helps your application rank better on search engines. Modern web applications are often built as JavaScript-generated Single Page Applications (SPAs), where content is populated at runtime. While SPAs provide a smooth user experience, they are not ideal for SEO because they lack initial content, making it difficult for Google and other crawlers to index the page effectively.

Nuxt addresses this issue by enabling server-side rendering (SSR), which pre-renders content before sending it to the client. This approach ensures that search engines can easily crawl and index the content, improving SEO performance.

Nuxt SEO Module

Nuxt includes a dedicated SEO module that provides essential features to optimize your application's metadata and improve discoverability.

SEO tags

Your Storefront project already includes a pre-configured SEO setup that automatically generates essential tags, such as title and meta, using Nuxt’s useSeoMeta composable. To learn more about its usage, refer to the official Nuxt documentation.

Structured data markups

Structured data is a standardized format for providing information about a page and classifying its content. Implementing structured data markups helps search engines better understand the context of your website’s content, improving visibility in search results and enabling rich snippets.

In the application, structured data is exposed in a JSON-LD format, following Google's recommendations. This ensures that search engines can efficiently parse and display relevant data in search results. For more details, refer to Google's official documentation.

This section covers three essential types of structured data used in the Boilerplate: Organization, Breadcrumbs, and Product.

Organization

The Organization structured data markup provides search engines with key details about our company, such as the name, logo, website, and contact information. This helps improve brand visibility and ensures accurate representation in search results.

Key attributes:

  • name – The official name of the organization.
  • url – The homepage URL.
  • logo – A URL pointing to the company’s logo.

Example:

//...
const {
  $config: {
    public: { baseUrl, shopName },
  },
} = useNuxtApp()

useJsonld(
  () =>
    ({
      '@context': 'https://schema.org',
      '@type': 'OnlineStore',
      name: shopName,
      url: baseUrl,
      logo: `${baseUrl}/logo.svg`,
    }) as WithContext<OnlineStore>,
)
//...

Will result in

<script type="application/ld+json">
    {
      "@context": "https://schema.org",
      "@type": "OnlineStore",
      "name": "Storefront Boilerplate",
      "url": "https://localhost",
      "logo": "https://localhost/logo.svg",
    }
</script>

Breadcrumbs help users and search engines understand the site’s structure and navigation hierarchy. They improve the user experience and can appear as enhanced snippets in search results.

Key attributes:

  • @type: BreadcrumbList – Defines the markup as a breadcrumb list.
  • itemListElement – An array of breadcrumb items, each containing:
    • position – The order of the breadcrumbs in the hierarchy.
    • name – The label of the breadcrumb.
    • item – The URL of the breadcrumb.

Example:



import {
  useProductListingSeoData,
} from '#storefront-product-listing'
//...
const { getBreadcrumbsFromCategory } = useBreadcrumbs()
const breadcrumbs = computed(() =>
  currentCategory.value
    ? getBreadcrumbsFromCategory(currentCategory.value, true)
    : [],
)

const { categoryBreadcrumbSchema } =
  useProductListingSeoData(breadcrumbs, route, {
    baseUrl: $config.public.baseUrl,
    fullPath: route.fullPath,
  })

useJsonld(() => categoryBreadcrumbSchema.value)
//...

Will result in

<script type="application/ld+json">
    {
      "@context": "https://schema.org",
      "@type": "BreadcrumbList",
      "itemListElement": [{
        "@type": "ListItem",
        "position": 1,
        "name": "Women",
        "item": "https://example.com/c/women-2023"
      },{
        "@type": "ListItem",
        "position": 2,
        "name": "Clothing",
        "item": "https://example.com/c/women/clothing-2027"
      },{
        "@type": "ListItem",
        "position": 3,
        "name": "Shirts",
        "item": "https://example.com/c/women/clothing/shirts-2029"
      }]
    }
</script>

ProductGroup Markup

The ProductGroup structured data markup provides key details about a product group — a collection of product variants, such as different sizes. This markup is essential for appearing in Google’s rich product results, improving visibility and search performance.

Key attributes:

  • @type: ProductGroup – Defines the schema type for the product group.
  • productGroupID - The unique ID of the product.
  • name – The product name, including the color (e.g., "Dress 'Adicolor Classics', Red").
  • image – An array of product images, including full CDN paths.
  • description – A short description of the product, taken from attributes.
  • brand – The product’s brand, provided as a nested object.
  • color - The product color, derived from attributes.
  • variesBy "https://schema.org/size" – Added if the product va1ries by size.
  • hasVariant – An array containing all product variants.
  • offers – Pricing and stock availability details for each variant.

Product Schema (Variant-Level Details)

Each product variant within a ProductGroup includes the following attributes:

  • @type: Product – Defines the schema type for the product variant.
  • size – The variant’s size, taken from attributes.
  • name – The product name, including color and size (e.g., "Dress 'Adicolor Classics', Red (XS)").
  • price – The variant’s price, formatted without a currency symbol.
  • sku – The unique stock-keeping unit (SKU) for the variant.
  • mpn – The manufacturer part number (MPN).
  • availability – The stock status of the variant ("https://schema.org/InStock" or "https://schema.org/OutOfStock").
  • url – The product URL with a variant ID parameter.

Example:

// ...
import {
  getCombineWithProductIds,
  useProductSeoData,
} from '#storefront-product-detail'
import { generateProductSchema } from '#storefront-product-detail/utils/seo'
//...
const productInfo = computed(() => ({
  name: name.value,
  brand: brand.value,
  productDescription: description.value,
  variants: variants.value.map((variant) => {
    const size = getFirstAttributeValue(variant.attributes, 'size')?.label || ''
    return generateProductSchema({
      productName: `${name.value}, ${formatColors(colors.value)}`,
      variant,
      url: `${$config.public.baseUrl}${route.fullPath}`,
      size,
    })
  }),
  images: images.value,
  productId: product.value?.id || 0,
  color: formatColors(colors.value),
  variesBy: variants.value.length > 1 ? ['https://schema.org/size'] : undefined,
}))


const { productJsonLd } = useProductSeoData(
    breadcrumbs,
    { baseUrl: $config.public.baseUrl, fullPath: route.fullPath },
    productInfo,
  )

useJsonLd(() => [productJsonLd.value])
// ...

Will result in

<script type="application/ld+json">
    {
      "@context": "http://schema.org/",
      "@type": "ProductGroup",
      "url": "https://nuxt3-demo-latest.storefront.scayle.cloud/en/p/dress-adicolor-classics-205627",
      "brand": {
        "@type": "Brand",
        "name": "ADIDAS"
      },
      "description": "",
      "name": "Dress 'Adicolor Classics'",
      "image": [
        "https://next-qa.cdn.scayle.cloud/images/08a74a6803df308bf0353441bf418f1c.jpg",
        "https://next-qa.cdn.scayle.cloud/images/08a74a6803df308bf0353441bf418f1c.jpg"
      ],
      "color": "Red",
      "productGroupID": 205627,
      "variesBy": [
        "https://schema.org/size"
      ],
      "hasVariant": [
        {
          "@context": "http://schema.org/",
          "@type": "Product",
          "size": "XS",
          "name": "Dress 'Adicolor Classics' (XS)",
          "offers": {
            "@type": "Offer",
            "url": "https://nuxt3-demo-latest.storefront.scayle.cloud/de/p/sportsweatshirt-205782?variantId=345",
            "availability": "https://schema.org/InStock",
            "itemCondition": "http://schema.org/NewCondition",
            "mpn": "ADT7081001000001",
            "price": 49.9,
            "priceCurrency": "USD",
            "sku": 337509
          }
        },
        {
          "@context": "http://schema.org/",
          "@type": "Product",
          "size": "S",
          "name": "Dress 'Adicolor Classics' (S)",
          "offers": {
            "@type": "Offer",
            "url": "https://nuxt3-demo-latest.storefront.scayle.cloud/de/p/sportsweatshirt-205782?variantId=347",
            "availability": "https://schema.org/InStock",
            "itemCondition": "http://schema.org/NewCondition",
            "mpn": "ADT7081001000001",
            "price": 49.9,
            "priceCurrency": "USD",
            "sku": 337509
          }
        }
      ]
    }
</script>

Composables

In the Boilerplate, we use composables to generate SEO-related data. For example, breadcrumbs on the PLP page are generated using the useProductListingSeoData composable, while product data on the PDP is handled by the useProductSeoData composable.

Localization

In the background, @nuxtjs/i18n is configured for locale-related aspects of SEO.

Check nuxt/i18n documentation for details.

Sitemap

The sitemap generation can be configured utilizing the Sitemap Add-on.

Footnotes

Last updated: February 20, 2025

Provide Feedback