Brent Haskins / Applied AI
generateMetadata on each blog and project route
brenthaskins.com uses generateMetadata in app/blog/[slug]/page.tsx and project pages so each URL exports its own title, description, canonical link, and Open Graph article times from frontmatter—not the root layout defaults alone.
Root layout metadata is the fallback. Every public URL should define its own.
Blog posts
app/blog/[slug]/page.tsx:
generateStaticParamsfrom all slugs incontent/bloggenerateMetadata({ params })reads frontmatter- Canonical:
https://brenthaskins.com/blog/{slug}unlesscanonicaloverride in frontmatter
Two fields serve two jobs:
| Field | Consumer |
|---|---|
description | Meta / OG / Twitter snippet length |
summary | Visible paragraph on page (see summary post) |
Projects
Case studies pull from content/projects.ts—unique title and description per slug for /projects/rally, /projects/formably, etc.
Build-time implication
Posts are static at build. New markdown requires deploy to update metadata—expected for this portfolio.
Not a ranking guarantee
Unique metadata prevents duplicate-title confusion in Search Console. It does not by itself produce rankings.
Brent Haskins — verify with curl -sI or view-source on production after adding a post.
FAQ
Questions people ask about this topic.
How is metadata set per blog post on brenthaskins.com?
generateMetadata loads the post by slug via getBlogPost, then returns title from post.title, description from post.description, alternates.canonical as SITE_URL plus /blog/slug, and openGraph with type article, publishedTime from post.date, and authors Brent Haskins. Twitter card uses summary_large_image with the same description.
Does each project case study have unique metadata?
Project routes under app/projects/[slug]/page.tsx generate metadata from content/projects.ts fields—title, summary, and slug—so case studies do not share the homepage title. Sitemap lists those URLs separately from blog posts.
Sources