Give each route its own title and canonical, and add robots.txt plus a sitemap.

The root layout was stamping the homepage title and production root onto every page. Routes now supply a page segment through one em-dash template, canonicals follow the request path, and non-production hosts disallow crawlers.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
adilallo
2026-09-09 16:45:19 -06:00
co-authored by Cursor
parent e4e61c0c9b
commit b2b272d2cc
32 changed files with 573 additions and 72 deletions
+12 -10
View File
@@ -8,6 +8,8 @@ import {
getRelatedBlogPosts,
} from "../../../../lib/content";
import { logger } from "../../../../lib/logger";
import { routeMetadata } from "../../../../lib/siteMetadata";
import messages from "../../../../messages/en/index";
import ContentBanner from "../../../components/sections/ContentBanner";
import AskOrganizer from "../../../components/sections/AskOrganizer";
import ContentTemplateDecorativeShapes from "../../_components/ContentTemplateDecorativeShapes";
@@ -62,13 +64,13 @@ export async function generateMetadata({
const post = getBlogPostBySlug(slug);
if (!post) {
return {
title: "Post Not Found",
description: "The requested blog post could not be found.",
};
return routeMetadata(`/blog/${slug}`, {
title: messages.metadata.blog.notFoundTitle,
description: messages.metadata.blog.fallbackDescription,
});
}
return {
return routeMetadata(`/blog/${slug}`, {
title: post.frontmatter.title,
description: post.frontmatter.description,
authors: [{ name: post.frontmatter.author }],
@@ -78,8 +80,8 @@ export async function generateMetadata({
type: "article",
publishedTime: post.frontmatter.date,
authors: [post.frontmatter.author],
url: `https://communityrule.com/blog/${slug}`,
siteName: "CommunityRule",
url: `/blog/${slug}`,
siteName: messages.metadata.siteName,
},
twitter: {
card: "summary_large_image",
@@ -87,12 +89,12 @@ export async function generateMetadata({
description: post.frontmatter.description,
creator: "@communityrule",
},
};
});
} catch (error) {
logger.error("Error generating metadata:", error);
return {
title: "Blog Post",
description: "A blog post from our community.",
title: messages.metadata.blog.fallbackTitle,
description: messages.metadata.blog.fallbackDescription,
};
}
}
+16 -16
View File
@@ -1,26 +1,27 @@
import { getAllBlogPosts } from "../../../lib/content";
import ContentThumbnailTemplate from "../../components/content/ContentThumbnailTemplate";
import type { Metadata } from "next";
import messages from "../../../messages/en/index";
import { routeMetadata } from "../../../lib/siteMetadata";
export const metadata: Metadata = {
title: "Blog - CommunityRule",
description:
"Learn about community governance, decision-making, and building successful organizations.",
const blogMeta = messages.metadata.blog;
export const metadata: Metadata = routeMetadata("/blog", {
title: blogMeta.title,
description: blogMeta.description,
openGraph: {
title: "Blog - CommunityRule",
description:
"Learn about community governance, decision-making, and building successful organizations.",
url: "https://communityrule.com/blog",
siteName: "CommunityRule",
title: blogMeta.title,
description: blogMeta.description,
url: "/blog",
siteName: messages.metadata.siteName,
type: "website",
},
twitter: {
card: "summary_large_image",
title: "Blog - CommunityRule",
description:
"Learn about community governance, decision-making, and building successful organizations.",
title: blogMeta.title,
description: blogMeta.description,
},
};
});
export default function BlogPage() {
const posts = getAllBlogPosts();
@@ -34,11 +35,10 @@ export default function BlogPage() {
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-12">
<div className="text-center mb-12">
<h1 className="text-4xl md:text-5xl font-bold text-[var(--color-content-default-primary)] mb-4">
Blog
{blogMeta.title}
</h1>
<p className="text-lg text-[var(--color-content-default-secondary)] max-w-2xl mx-auto">
Learn about community governance, decision-making, and building
successful organizations.
{blogMeta.description}
</p>
</div>