What gets installed
A file-by-file tour of the 81 files AgentBlog writes into your repository, and the rules each piece obeys.
Everything below lands in your repository as source you own and can edit. The
paths assume a flat layout. In a src/ layout, shadcn resolves them against
your components.json aliases.
Routes
| File | What it does |
|---|---|
app/blog/layout.tsx | Imports the preflight check, adds the editorial policy link |
app/blog/page.tsx | Paginated index with Blog structured data |
app/blog/opengraph-image.tsx | The social card every list surface points at |
app/blog/[slug]/page.tsx | The post route |
app/blog/[slug]/opengraph-image.tsx | The social card for each post |
app/blog/category/[slug]/page.tsx | Category hub, always indexable |
app/blog/tag/[slug]/page.tsx | Tag listing, noindex below your configured post count |
app/authors/[slug]/page.tsx | Author page emitting Person structured data |
app/editorial-policy/page.tsx | Optional, ships as @agentblog/eeat-pages |
Five rules are baked into these files, and each one closes a failure that has no visible symptom.
generateStaticParams returns every slug. Never sliced, never paginated.
Every post becomes complete static HTML at build time, which is the safest
possible shape for a crawler that fetches once and runs no JavaScript.
No 'use client' in the article render path. Two components are allowed to
be client components, the table of contents for its scroll tracking and the
share buttons, and both render their full content on the server first. The FAQ
and the table of contents use <details> and CSS rather than mounting on
interaction, so their text is in the HTML whether or not anything hydrates.
One <h1> per page, a <time> element on every date, a stable id on
every H2 and H3, and rel="author" on the byline link.
Every route that sets openGraph or robots spreads the shared defaults
from lib/metadata.ts. See the shallow merge trap.
Pagination renders real links. A paginator that only works after hydration hides everything past page one from a crawler.
SEO routes
| File | Notes |
|---|---|
app/sitemap.ts | lastModified comes from each post's dateModified, never from the build time |
app/robots.ts | Guarded on the deployment environment, and emits your aiAccess rules |
app/feed.xml/route.ts | RSS 2.0 |
app/llms.txt/route.ts | The post list as Markdown, for retrieval agents that fetch it |
app/not-found.tsx | A 404 that links back into the content rather than being a dead end |
app/api/publish/route.ts | Revalidation and IndexNow, in that order |
sitemap.xml, robots.txt, feed.xml, llms.txt, and the social images are
all cached route handlers. That is why the publish webhook revalidates them
explicitly rather than only the post paths: a new post that never reaches them
is a failure with no symptom.
llms.txt is worth being precise about, because it is widely oversold. Google
has said no Search system reads it, and OpenAI's and Google's crawlers do not
request it in meaningful volume, so it is not an SEO lever and nothing here
treats it as one. It ships because Perplexity has confirmed it fetches the file
to decide which pages are worth retrieving, and Anthropic recommends publishing
one and honours it in Claude's retrieval. Read it as an agent-readable table of
contents built from the same content source as the sitemap, not as a ranking
signal.
Components
components/blog/ holds the reading experience: the answer capsule, the author
bio, breadcrumbs, the byline, category pills, icons, the structured data
serialiser, pagination, the post card, the post list, the prose wrapper, related
posts, share buttons, and the table of contents.
components/mdx/ holds the component map plus the callout, code block, FAQ,
figure, key takeaways, quote, stat, and table. See components you can use in a
post.
components/blog/icons.tsx re-exports every icon used anywhere in the block, so
swapping icon libraries costs one file rather than a search across twelve
components.
components/blog/type-scale.ts is the design system, and it is the file to open
first if you want the blog to look like the rest of your product. It holds the
heading, lede, section, eyebrow, and meta roles as class strings, plus the two
gaps every page composes with. Editing one constant there restyles every route
at once, which is the reason the routes import from it instead of spelling out
their own headings. The layout widths it composes against live in
styles/agentblog.css as --agentblog-measure, --agentblog-aside,
--agentblog-article, and --agentblog-rail.
One rule in that file is worth repeating here, because breaking it is how the
spacing goes wrong: no component sets its own outer margin. Pages stack
their children in a flex column with a gap, so a component that is missing
from the layout is a component that is visibly missing, rather than one that
silently renders flush against its neighbour.
Library
| File | Responsibility |
|---|---|
lib/config.ts | The resolved config and the URL helpers. The only file that reads your config |
lib/posts.ts | The content source facade. Every route reads posts through this file |
lib/schema.ts | The structured data builders, typed rather than hand-written JSON |
lib/metadata.ts | The shared metadata defaults every route spreads |
lib/og-card.tsx | The social card layout both opengraph-image routes render |
lib/render-mdx.tsx | The only place MDX is compiled |
lib/mdx-plugins/ | Table of contents extraction and answer capsule handling |
lib/toc.ts, lib/reading-time.ts | Heading extraction, word count and minutes |
lib/indexnow.ts | Submission, with the response codes surfaced rather than swallowed |
lib/ai-referrers.ts | Classifies assistant referrals. See measuring |
lib/sources/mdx.ts | The MDX content source |
Four files are generated and carry a banner saying so: lib/schemas.ts,
lib/types.ts, lib/define-config.ts, and lib/preflight-checks.ts. They are
generated rather than imported because they ship into your repository, where
there is no package of ours to import from. Do not edit them: they are the files
most likely to carry a real fix in an update, and they are always safe to
overwrite.
The shallow merge trap
Worth stating on its own, because it is invisible to the type checker and it is the single most common way a correct-looking blog loses metadata.
Next.js merges metadata across segments shallowly. If your root layout sets:
openGraph: { siteName: 'Your Brand', locale: 'en_US', type: 'website' }and a post page sets:
openGraph: {
type: 'article',
title: post.title,
description: post.description,
url: canonical,
images: [ogImage],
}then because the page defined openGraph at all, the layout's entire object is
discarded. Every post ships a social card with no site name and no locale.
Nothing errors, no validator complains, and the types are correct. The only way
to notice is to view source on a built page.
lib/metadata.ts exists so that no route file can make this mistake by
accident, and agentblog doctor asserts it against built HTML rather than
against the source.
Styles
styles/agentblog.css carries two things: the bridge from the typography plugin
to your theme tokens, and the --agentblog- prefixed variables for the reading
measure and the prose scale. See make it look like your
site.
Nothing imports this file for you. That is the one install step with no warning attached.
Content
content/blog/*.mdx, content/authors.json, and content/categories.json.
Two example posts ship with the block, licensed CC0, because they become your published content and an attribution requirement would mean every AgentBlog user owed credit on their own blog.
They are also the format specification. They are what the write-blog-post
skill patterns from, so they follow every rule in the GEO
playbook, including the copy style rules. Read one
before you delete them.