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
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
}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>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.
