Give feedback as an early user and get the docs plus a full website free. Code FRIEND: valid on one-time purchases only.

SEO & Metadata

SEO data is centralized in src/data/client.ts, a single source of truth for all metadata, structured data, and social sharing settings across your site. A dedicated Meta component handles comprehensive SEO coverage automatically inside BaseLayout.

The Meta Component

src/components/Meta/Meta.astro provides comprehensive SEO coverage, automatically included in BaseLayout

Features

  • Open Graph tags (title, description, image, locale, site name)
  • Twitter Cards (summary_large_image)
  • JSON-LD structured data (LocalBusiness on all pages)
  • Automatic social image handling with fallback
  • Enhanced meta tags for articles (author, published/modified dates)

Social Image Strategy

  • Pages with heroImage: Uses the provided image, optimized to 1200×600 webp
  • Pages without heroImage: Falls back to /assets/social.jpg
  • Blog posts: Automatically use the post's featured image

BaseLayout Props

BaseLayout accepts simple props with sensible defaults from client.ts

1

Props Interface

interface Props {
  title?: string;        // Page title (defaults to SITE.title)
  description?: string;   // Meta description (defaults to SITE.description)
  heroImage?: HeroImage;  // Optional social sharing image
}
2

Basic Page (uses defaults)

Pass just a title and description — everything else falls back to client.ts.

<BaseLayout
  title="About Us"
  description="Learn about our company"
>
  <!-- Page content -->
</BaseLayout>
3

Page With Social Image

Optimize an image with getImage and pass it as heroImage.

---
import heroImage from "@assets/images/hero.jpg";
import { getImage } from "astro:assets";
const optimizedImage = await getImage({ src: heroImage, format: "webp" });
---

<BaseLayout
  title="Projects"
  description="Our portfolio"
  heroImage={optimizedImage}
>
  <!-- Page content -->
</BaseLayout>

💡 Sensible Defaults

Omit any prop and the Meta component pulls the value from client.ts. You only override what's different on a given page.

Extending SEO Metadata

Add custom Open Graph tags or JSON-LD structured data without touching the Meta component

Custom Open Graph Tags

Edit src/components/Meta/Meta.astro to add properties like article publish dates, Twitter-specific metadata, or additional schema.org types.

Custom JSON-LD — Approach 1: Inline Schema

The template auto-generates LocalBusiness (all pages) and BlogPosting (blog posts). To add custom JSON-LD on a specific page, use the schema slot — no component changes needed.

<BaseLayout title="FAQ" description="Frequently asked questions">
  <script slot="schema" is:inline type="application/ld+json">
    {
      "@context": "https://schema.org",
      "@type": "FAQPage",
      "mainEntity": [{
        "@type": "Question",
        "name": "What services do you offer?",
        "acceptedAnswer": {
          "@type": "Answer",
          "text": "We offer consulting and development services."
        }
      }]
    }
  </script>

  <!-- Page content -->
</BaseLayout>

Custom JSON-LD — Approach 2: Reusable Schema Functions

For schemas used across multiple pages, create a helper following the existing pattern (localBusinessSchema.js, blogPostingSchema.js).

// src/js/faqSchema.js
import { SITE } from "@data/client";

export function getFAQSchema(faqs) {
  return {
    "@context": "https://schema.org",
    "@type": "FAQPage",
    mainEntity: faqs.map((item) => ({
      "@type": "Question",
      name: item.question,
      acceptedAnswer: {
        "@type": "Answer",
        text: item.answer,
      },
    })),
  };
}

Usage in a Page

---
import { getFAQSchema } from "@js/faqSchema";
const faqSchema = getFAQSchema(faqData);
---

<BaseLayout title="FAQ" description="Frequently asked questions">
  <script
    slot="schema"
    is:inline
    type="application/ld+json"
    set:html={JSON.stringify(faqSchema)}
  />

  <!-- Page content -->
</BaseLayout>

🎯 Multiple Schemas

You can add multiple <script slot="schema"> tags — each one renders in the <head>. Validate output with Google's Rich Results Test.