blog.hangerthem.com/app/sitemap.ts
HangerThem 64ae30b545 feat(seo): sitemap, robots, canonical URLs, OG images and JSON-LD
- Add sitemap.ts and robots.ts
- pageMetadata() helper: consistent title, canonical URL, Open Graph,
  Twitter card and feed alternate on every page
- Generated OG images: a site card, and per post either the cover image
  or a branded title card (pre-rendered at build)
- BlogPosting JSON-LD on post pages
- Fail fast with a clear error when SITE_URL is missing

Content loading:
- Validate post frontmatter at build time and normalize dates to ISO
  (fixes invalid <time datetime> values)
- Add draft: true support (drafts only render in development)
- Cache markdown rendering so metadata and page share one render
- Replace the three copy-pasted about/projects/setup pages with a shared
  MarkdownPage component
2026-10-19 14:22:40 +02:00

41 lines
1.4 KiB
TypeScript

import type { MetadataRoute } from 'next'
import { SITE_URL } from '@/app/metadata'
import { getAllCategories } from '@/lib/categories'
import { getAllPostsMeta } from '@/lib/posts'
import { getAllTags } from '@/lib/tags'
import { slugify } from '@/utils/slug'
export default function sitemap(): MetadataRoute.Sitemap {
const posts = getAllPostsMeta()
const lastPostUpdate = posts
.map((post) => post.updated ?? post.date)
.toSorted()
.at(-1)
return [
{ url: SITE_URL, lastModified: lastPostUpdate, changeFrequency: 'weekly', priority: 1 },
...['about', 'projects', 'setup'].map((page) => ({
url: `${SITE_URL}/${page}`,
changeFrequency: 'monthly' as const,
priority: 0.8,
})),
...posts.map((post) => ({
url: `${SITE_URL}/posts/${post.slug}`,
lastModified: post.updated ?? post.date,
changeFrequency: 'monthly' as const,
priority: 0.9,
})),
{ url: `${SITE_URL}/categories`, changeFrequency: 'weekly', priority: 0.4 },
...Object.keys(getAllCategories()).map((category) => ({
url: `${SITE_URL}/categories/${slugify(category)}`,
changeFrequency: 'weekly' as const,
priority: 0.3,
})),
{ url: `${SITE_URL}/tags`, changeFrequency: 'weekly', priority: 0.4 },
...Object.keys(getAllTags()).map((tag) => ({
url: `${SITE_URL}/tags/${slugify(tag)}`,
changeFrequency: 'weekly' as const,
priority: 0.3,
})),
]
}