Using `astro-seo` to Simplify SEO in Astro Projects

Learn how to install and configure the `astro-seo` component in your Astro site—streamline titles, meta tags, Open Graph and Twitter cards

Published on August 29, 2025 by Michael Andreuzza

Astro is already blazing fast and inherently SEO-friendly thanks to its static-first architecture and minimal client-side JavaScript. But managing SEO metadata—titles, descriptions, Open Graph, Twitter cards, structured data—can become repetitive and error-prone.

Enter astro-seo: a smart, flexible component that centralizes all your SEO needs in one spot. Think of it as your own SEO checklist packaged into a reusable component.

Quick i nstallation

Install via your preferred package manager:

npm install astro-seo
# or
yarn add astro-seo
# or
pnpm add astro-seo

Step 1: Define site-wide defaults

Create a file src/constants/siteData.json to store your default metadata:

{
  "default": {
    "title": "Lexington Themes",
    "description": "Fast Astro + Tailwind templates.",
    "openGraph": {
      "basic": {
        "title": "Lexington Themes",
        "type": "website",
        "image": "https://lexingtonthemes.com/og-default.jpg",
        "url": ""
      },
      "optional": {
        "siteName": "Lexington Themes"
      }
    },
    "twitter": {
      "card": "summary_large_image",
      "site": "@lexingtonthemes",
      "creator": "@lexingtonthemes"
    },
    "noindex": false,
    "nofollow": false
  },
  "pages": {
    "about": {
      "title": "About · Lexington Themes",
      "description": "About Lexington Themes and what drives us.",
      "openGraph": {
        "basic": {
          "title": "About Lexington Themes",
          "type": "website",
          "image": "https://lexingtonthemes.com/og-about.jpg",
          "url": ""
        }
      }
    }
  }
}

Step 2: Set up BaseLayout.astro with merged SEO ,ogic

In src/layouts/BaseLayout.astro, merge site defaults with per-page overrides:

---
import { SEO } from 'astro-seo';
import siteData from '../constants/siteData.json';

const { seo = {} } = Astro.props;
const merged = {
  ...siteData.default,
  ...seo,
  openGraph: {
    basic: {
      ...siteData.default.openGraph.basic,
      ...seo.openGraph?.basic,
      url: Astro.url.href
    },
    optional: siteData.default.openGraph.optional
  },
  twitter: {
    ...siteData.default.twitter,
    ...seo.twitter
  }
};
---

<html lang="en">
  <head>
    <SEO {...merged} />
  </head>
  <body>
    <slot />
  </body>
</html>

Now, any page wrapped in this layout will automatically get SEO defaults unless overridden explicitly.

Step 3: Example of a static page (about.astro)

---
import BaseLayout from '../layouts/BaseLayout.astro';
import siteData from '../constants/siteData.json';

const seo = siteData.pages.about;
---

<BaseLayout seo={seo}>
  <h1>About Lexington Themes</h1>
  <p>Learn about our mission and journey.</p>
</BaseLayout>

This makes the “About” page pick up its own title and description while applying site-wide defaults where needed.

Step 4: Dynamic blog post SEO

---
import BaseLayout from '../layouts/BaseLayout.astro';
import { getEntryBySlug } from 'astro:content';

const { slug } = Astro.params;
const post = await getEntryBySlug('blog', slug);

const seo = {
  title: post.data.title,
  description: post.data.excerpt,
  openGraph: {
    basic: {
      title: post.data.title,
      type: "article",
      image: post.data.cover,
      url: Astro.url.href
    },
    article: {
      publishedTime: post.data.published,
      tags: post.data.tags
    }
  }
};
---

<BaseLayout seo={seo}>
  <article>
    <h1>{post.data.title}</h1>
    <p><em>Published: {post.data.published}</em></p>
    {post.content}
  </article>
</BaseLayout>

Each blog post now carries its own unique SEO metadata (title, excerpt, cover image, publication date, and tags).

Step 5: Validation & testing

  • Use Lighthouse’s SEO audit in DevTools to check for missing tags.
  • Preview social sharing with tools like Facebook Sharing Debugger and Twitter Card Validator.
  • Ensure canonical URLs are correct to avoid duplicate content.

TL;DR

  • Define global SEO defaults in siteData.json.
  • Use BaseLayout.astro with astro-seo to centralize SEO logic.
  • Allow pages to override defaults selectively.
  • Easily scale SEO across static, static-generated, and dynamic page types.
  • Streamline SEO setup while keeping your pages metadata-rich, consistent, and error-free.

/Michael Andreuzza

Did you like this post? Please share it with your friends!