BLOG_PATH, SITE_URL, PROJECT_NAME).
SDK on npm
@rankbuddy/sdk — headless client, ISR helpers, React HTML renderer.Article fields
Full reference for
RankBuddyArticle and RankBuddyCluster.What you are building
Routes
{BLOG_PATH}— article index (listing + topic strip){BLOG_PATH}/[slug]— article detail{BLOG_PATH}/topic/[clusterSlug]— topic hub (recommended){BLOG_PATH}/layout.tsx— passthrough layout for cache invalidation
Data
Server Components fetch published content via
@rankbuddy/sdk ISR helpers (not raw fetch to RankBuddy).SEO
Per-page
generateMetadata using article seo.* fields, JSON-LD BlogPosting, and robots when seo.noIndex.{BLOG_PATH} with the user’s path (e.g. /blog). Replace {SITE_URL} with their production origin (e.g. https://acme.com).
1. Install dependencies
@rankbuddy/sdk/react.
2. Environment variables
Add to.env.local and your hosting provider:
3. SDK client (server)
Createsrc/lib/rankbuddy.ts:
NEXT_PUBLIC_SITE_URL and RANKBUDDY_BLOG_PATH to match the user’s project, or hardcode values from the onboarding prompt.
4. Route map (App Router)
For{BLOG_PATH} = /blog:
src/app/(marketing)/blog/...) — URLs stay /blog/....
Blog layout
src/app/blog/layout.tsx:
5. Use SDK ISR helpers (required)
On Next.js, do not callrankBuddy.articles.getBySlug directly in Server Components. Use cached helpers so revalidateTag works:
| Page | SDK helper | Cache tag |
|---|---|---|
| Index | getArticlesList, getClustersList | blogListingCacheTag() |
| Article | getArticleBySlug | blogArticleCacheTag(slug) |
| Topic hub | getClusterBySlug, getClusterArticles | blogTopicListingCacheTag(clusterSlug) |
| Related block | getRelatedArticles | blogRelatedCacheTag(slug) + blogRelatedAllCacheTag() |
generateStaticParams | getArticlesList, getClusterSlugs | listing tag |
6. Blog index ({BLOG_PATH}/page.tsx)
Required behavior:
export const revalidate = 3600for first launch (see Caching); usefalseonly with a revalidation strategy.- Fetch
getArticlesList(rankBuddy, { limit: 24 })andgetClustersList(rankBuddy)in parallel. - Render an empty state when there are no posts.
- Each card shows:
coverImage,title,excerpt || description,publishedAt,readingTime, link to{BLOG_PATH}/{slug}. - Topic strip links to
{BLOG_PATH}/topic/{cluster.slug}. - Static
generateMetadatafor the index (title, description, canonical, Open Graph).
7. Article page ({BLOG_PATH}/[slug]/page.tsx)
Required behavior:
generateStaticParams— paginategetArticlesList(e.g.limit: 100) to prebuild slugs; wrap in try/catch so missing API key at build time does not fail CI.generateMetadata— map from article fields:
await rankBuddy.seo.generateMetadata(slug) returns a Next-compatible metadata object.
Page UI must include:
| Element | Source |
|---|---|
<h1> | post.title |
| Cover hero | post.coverImage |
| Byline | publishedAt, readingTime |
| Topic link | post.primaryCluster → topic page |
| Tags | post.tags or post.seo.keywords |
| Body | post.content.html (see below) |
| Related posts | cluster articles or getRelatedArticles(rankBuddy, slug) |
| Back link | to {BLOG_PATH} |
<h1> in HTML if the page already renders post.title (e.g. [&>h1:first-child]:hidden).
JSON-LD (recommended): BlogPosting + BreadcrumbList scripts with headline, description, image, datePublished, dateModified, url.
Call notFound() when getArticleBySlug returns null.
8. Topic hub ({BLOG_PATH}/topic/[clusterSlug]/page.tsx)
Required behavior:
generateStaticParamsfromgetClusterSlugs(rankBuddy).getClusterBySlug(rankBuddy, clusterSlug)—notFound()if null.getClusterArticles(rankBuddy, clusterSlug, { limit: 24 })for the grid.- Metadata from
cluster.name,cluster.notes,cluster.pillarKeyword,cluster.keywords. - Same card component as the index.
9. Caching
First launch (recommended)
Production (on-demand)
revalidateTag + revalidatePath after publishes. Tags from @rankbuddy/sdk:
blogArticleCacheTag(slug)blogListingCacheTag()blogRelatedAllCacheTag()blogRelatedCacheTag(slug)blogTopicListingCacheTag(clusterSlug)
10. Base styling spec
Agents should ship production-ready pages without a design system dependency:- Font: system sans or project default
- Accent: use the primary brand color from the onboarding prompt (
Primary brand color: #RRGGBB). If none was detected from the site, RankBuddy defaults to#0F172A(Tailwindslate-900) - Index: card layout with 16:9 cover thumbnails, title, 2-line excerpt
- Article: max-width ~
48remprose column, optional sidebar - Dark mode: support if the host app uses
dark:classes - Images:
altfromtitle, lazy loading,object-coveron cards - Accessibility: semantic
<article>,<time dateTime>, focus states on links
11. Agent checklist
Before finishing, verify:All three routes exist under the user’s
{BLOG_PATH}.RANKBUDDY_API_KEY is read only on the server.SDK ISR helpers used instead of raw client list/get calls.
seo.noIndex sets robots correctly.generateStaticParams tolerates missing API key at build time.Empty index state explains that posts come from RankBuddy after publish.
User can follow Publish your first article after deploy.