Files
joelhooks__joelclaw/apps/web/lib/jsonld.ts
panda b794889a76 Initial joelclaw: MDX blog with full SEO
- Next.js 16.1.5 + Turborepo + pnpm
- MDX blog with file-based content (apps/web/content/)
- Posts at root URLs (joelclaw.com/post-slug)
- Dark-first design: Geist font + Tailwind CSS
- Full SEO: JSON-LD (BlogPosting, Blog, Person, Breadcrumb),
  OpenGraph, Twitter cards, canonical URLs
- sitemap.xml, robots.txt, RSS feed (/feed.xml)
- next-mdx-remote/rsc for server component MDX rendering
- oxlint + oxfmt (replaced ESLint + Prettier, -240 packages)
- 42 agent skills installed (Next.js, React, Apple/Swift, SEO, marketing)
- 2 seed posts from Vault architecture decisions
2026-02-14 14:46:37 -08:00

73 lines
1.7 KiB
TypeScript

import type { PostMeta } from "./posts";
import { SITE_URL, SITE_NAME, AUTHOR } from "./constants";
export function blogPostingJsonLd(meta: PostMeta) {
return {
"@context": "https://schema.org",
"@type": "BlogPosting",
headline: meta.title,
description: meta.description,
datePublished: meta.date,
dateModified: meta.date,
url: `${SITE_URL}/${meta.slug}`,
author: {
"@type": "Person",
name: AUTHOR.name,
url: AUTHOR.url,
},
publisher: {
"@type": "Person",
name: AUTHOR.name,
url: AUTHOR.url,
},
mainEntityOfPage: {
"@type": "WebPage",
"@id": `${SITE_URL}/${meta.slug}`,
},
isPartOf: {
"@type": "Blog",
name: SITE_NAME,
url: SITE_URL,
},
};
}
export function blogJsonLd() {
return {
"@context": "https://schema.org",
"@type": "Blog",
name: SITE_NAME,
description:
"Building a personal AI operating system from scratch. Architecture decisions, working code, and the journey from zero to a composable agent system.",
url: SITE_URL,
author: {
"@type": "Person",
name: AUTHOR.name,
url: AUTHOR.url,
},
};
}
export function personJsonLd() {
return {
"@context": "https://schema.org",
"@type": "Person",
name: AUTHOR.name,
url: AUTHOR.url,
sameAs: ["https://github.com/joelhooks", "https://twitter.com/jhooks"],
};
}
export function breadcrumbJsonLd(items: { name: string; url: string }[]) {
return {
"@context": "https://schema.org",
"@type": "BreadcrumbList",
itemListElement: items.map((item, i) => ({
"@type": "ListItem",
position: i + 1,
name: item.name,
item: item.url,
})),
};
}