Overview
The PostLayout.astro component is a specialized layout designed for blog posts. It provides similar foundational structure to the base layout but is optimized for blog content, removing the canonical URL prop while maintaining all essential SEO and styling features.
Props Interface
The post layout accepts the following props:
The blog post title that appears in the browser tab and search results
The blog post description for SEO and social media sharing
Optional Open Graph image URL for social media previews of the blog post
Source Code
src/layouts/PostLayout.astro
---
interface Props {
title: string;
description: string;
image?: string;
}
const { title, description, image } = Astro.props;
import Header from "../components/Header.astro";
import Footer from "../components/Footer.astro";
import Alert from "../components/Alert.astro";
import SEO from "../components/SEO.astro";
---
<!doctype html>
<html data-bs-theme="light" lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no" />
<title>{title}</title>
<SEO title={title} description={description} image={image} />
<link rel="stylesheet" href="/assets/bootstrap/css/bootstrap.min.css" />
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/font/bootstrap-icons.css">
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=ABeeZee&display=swap" />
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Abel&display=swap" />
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/font/bootstrap-icons.min.css">
<link rel="stylesheet" href="/assets/css/Pusab.css" />
<link rel="stylesheet" href="/assets/css/bss-overrides.css" />
<link rel="stylesheet" href="/assets/css/Overlay-Video-Player.css" />
<link rel="stylesheet" href="/assets/css/Responsive-Youtube-Embed.css" />
<meta name="description" content={description} />
<link
rel="shortcut icon"
href="https://www.robtopgames.com/favicon-32x32.png"
type="image/x-icon"
/>
</head>
<body
style="background: linear-gradient(#0281C6, #00385C);background-position: center center;background-repeat: no-repeat;background-size: cover;background-attachment: fixed;"
>
<Header />
<main>
<slot />
</main>
<Footer />
<Alert />
<script is:inline src="/assets/bootstrap/js/bootstrap.min.js"></script>
<script is:inline src="/assets/js/Overlay-Video-Player-script.js"></script>
</body>
</html>
Differences from Base Layout
The post layout differs from the base layout in the following ways:
- No Canonical URL: The
canonicalURL prop is removed since blog posts typically don’t need canonical URL management
- Explicit Meta Description: Includes an additional
<meta name="description"> tag alongside the SEO component
- Bootstrap Icons Version: Uses Bootstrap Icons v1.11.1 in addition to v1.13.1 for compatibility
Blog Post Features
When used in blog posts, this layout supports:
- SEO-optimized meta tags for search engines
- Open Graph images for social media sharing
- Responsive design with Bootstrap 5
- Consistent Header/Footer across all posts
- Video embed functionality
- Custom styling optimized for blog content
Usage Example
The post layout is primarily used in the dynamic blog post page:
Blog Post Page
Custom Blog Post
src/pages/blog/[slug].astro
---
import { getCollection } from 'astro:content';
import PostLayout from '../../layouts/PostLayout.astro';
export async function getStaticPaths() {
const posts = await getCollection('blog');
return posts.map((post) => ({
params: { slug: post.slug },
props: { post },
}));
}
const { post } = Astro.props;
const { Content } = await post.render();
// Generate navigation path
const pathSegments = post.slug.split('/');
const breadcrumbs = pathSegments.map((segment, index) => {
return {
label: segment.replace(/-/g, ' '),
url: index === pathSegments.length - 1 ? null : `/blog/${pathSegments.slice(0, index + 1).join('/')}`
};
});
---
<PostLayout title={post.data.title} description={post.data.description} image={post.data.image}>
<div class="container py-4">
<!-- Breadcrumb navigation -->
<nav class="d-flex flex-wrap align-items-center gap-2 mb-4 text-white px-2 py-3 bg-dark bg-opacity-25 rounded-4 shadow-sm">
<a href="/" class="text-white d-flex align-items-center text-decoration-none transition-all hover-bright">
<i class="bi bi-house-fill"></i>
</a>
<i class="bi bi-chevron-right opacity-50 text-white"></i>
<a href="/blog" class="text-white text-decoration-none transition-all hover-bright">Blog</a>
<!-- Dynamic breadcrumbs -->
</nav>
<div class="row gx-3">
<div class="col-12 col-lg-8">
<!-- Author info -->
<div class="d-flex flex-column flex-sm-row align-items-start align-items-sm-center gap-3 mb-4 p-3 bg-dark bg-opacity-25 rounded-4 shadow-sm">
<img
class="rounded-circle object-fit-cover shadow"
width="60"
height="60"
src={post.data.authorImage}
alt={post.data.author}
/>
<div>
<h5 class="fw-bold text-white mb-1">{post.data.author}</h5>
<p class="text-white mb-0 d-flex flex-wrap align-items-center gap-2">
<i class="bi bi-calendar3"></i>
{new Date(post.data.createdAt).toLocaleDateString('en-US', {
year: 'numeric',
month: 'long',
day: 'numeric'
})}
</p>
</div>
</div>
<!-- Post title -->
<h1 class="display-4 fw-bold text-white mb-4 mb-lg-5 ps-2 border-start border-4" style="border-color: #0281C6 !important;">
<i class="bi bi-hash text-primary"></i>
{post.data.title}
</h1>
<!-- Featured image -->
<div class="card bg-dark text-white border-0 rounded-4 shadow-lg overflow-hidden mb-4 mb-lg-5">
<div class="position-relative" style="height: 300px; height: clamp(300px, 50vw, 400px); overflow: hidden;">
<img
class="object-fit-cover w-100 h-100"
src={post.data.image}
alt={post.data.title}
/>
</div>
</div>
<!-- Post content -->
<div class="content prose prose-invert bg-dark bg-opacity-25 p-3 p-lg-4 rounded-4 shadow-sm text-white">
<Content />
</div>
</div>
<div class="col-12 col-lg-4 mt-4 mt-lg-0">
<!-- Sidebar with recent articles -->
</div>
</div>
</div>
</PostLayout>
---
import PostLayout from '../../layouts/PostLayout.astro';
---
<PostLayout
title="New Game Features Released"
description="Explore the latest features added to Geometry Dash including new levels, challenges, and community updates."
image="/assets/img/blog/new-features.jpg"
>
<div class="container py-5">
<article class="prose text-white">
<h1>New Game Features Released</h1>
<p>We're excited to announce several new features...</p>
<!-- Article content -->
</article>
</div>
</PostLayout>
Content Collections Integration
The post layout works seamlessly with Astro’s Content Collections:
{
title: string; // Passed to layout
description: string; // Passed to layout
image: string; // Passed to layout
author: string; // Used in page content
authorImage: string; // Used in page content
createdAt: Date; // Used in page content
tags: string[]; // Used in page content
publish: boolean; // Used for filtering
}