Skip to content
rctrl Docsrctrl Docs

Publish to Next.js and static sites

Connect a Next.js or static site to the Content API. Articles render as native pages on your domain, with no deploy per article.

RankControl publishes to Next.js sites through a pull model. Your site reads articles from the Content API and renders them at your own URLs, for example yoursite.com/blog/article-slug. RankControl never touches your repository or your host. You add a few files once, and every article after that appears on its own.

Connect the destination

  1. Go to Brand Control → Publishing and pick the Headless / Static / Next.js card.
  2. Enter your site URL and the article path where articles should live, for example /blog. If a route already exists at that path, pick a different one so your existing pages are never overwritten.
  3. Click Create destination.

The next screen shows your API key. Copy it right away: it is shown only this once. You can always mint a new read:content key later under Settings → API.

Set up your site

The same screen gives you two paths:

  • Agent prompt. One copyable prompt for Cursor, Claude Code, or any coding agent working in your repository. It contains the key, the endpoints, and instructions for every file below.
  • Manual setup. The same files as copy-paste code blocks.

Either way, your site ends up with:

File What it does
.env.local API URL, API key, and revalidate secret
lib/rankcontrol.ts Fetches articles from the Content API
app/[path]/[slug]/page.tsx Renders each article as a native page
app/api/rankcontrol/revalidate/route.ts Lets new articles appear instantly
next.config.js Allows article images from cdn.imgcloud.org

If your site already lists its own posts, one more snippet merges RankControl articles into your blog index and sitemap.

Publish your first article

The Content API serves published articles only. Planned titles and drafts stay private, so a site you just connected shows an empty list until you publish something. An empty list means the setup works and there is nothing to serve yet.

Open Content in RankControl, pick an article, and publish it. From there:

  1. The article becomes available on the Content API right away.
  2. RankControl calls your revalidate route, so the article page and your article list refresh immediately.
  3. A couple of minutes later, RankControl loads the article on your site to confirm it renders. An article that is not reachable yet is marked Not live yet on the Content screen, and the check retries on its own.

New articles need no deploy. Next.js renders an unknown slug on first request by fetching the Content API. If you skipped the revalidate route, pages refresh on a five minute timer instead.

Count AI crawler visits

AI crawlers (GPTBot, ClaudeBot, PerplexityBot and others) fetch your HTML and stop. They run no JavaScript, so a page script never sees them. A small middleware in your site reports their visits to RankControl, and the AI Crawling screen shows which crawlers read which articles.

Create middleware.ts at the project root. On Next.js 16 name the file proxy.ts and keep the same export.

import { NextResponse, type NextFetchEvent, type NextRequest } from "next/server";

const AI_BOTS =
  /GPTBot|ChatGPT-User|PerplexityBot|ClaudeBot|Claude-Web|anthropic-ai|Google-Extended|Bingbot|Bytespider|CCBot|cohere-ai|Amazonbot|Meta-ExternalAgent|Applebot-Extended|Googlebot/;

export function middleware(req: NextRequest, event: NextFetchEvent) {
  const ua = req.headers.get("user-agent") ?? "";
  if (AI_BOTS.test(ua)) {
    event.waitUntil(
      fetch(`${process.env.RANKCONTROL_API_URL}/api/site/crawl`, {
        method: "POST",
        headers: {
          "content-type": "application/json",
          "x-rankcontrol-key": process.env.RANKCONTROL_REVALIDATE_SECRET ?? "",
        },
        body: JSON.stringify({ user_agent: ua, path: req.nextUrl.pathname }),
      }).catch(() => {})
    );
  }
  return NextResponse.next();
}

export const config = { matcher: ["/((?!_next/|api/|.*\\..*).*)"] };

It uses the two variables you added in .env.local. Only requests from the crawlers in the list are reported, and the report never blocks a response. The agent prompt on your destination includes this file as step 8, with the current crawler list filled in.

Verify the install with one request. It stores nothing:

curl -X POST "$RANKCONTROL_API_URL/api/site/crawl" \
  -H "x-rankcontrol-key: $RANKCONTROL_REVALIDATE_SECRET" \
  -H "content-type: application/json" \
  -d '{"user_agent":"install-check","path":"/","test":true}'

The answer is {"ok":true,"test":true}. The destination row then reads “Crawler snippet: last report just now”, and crawler visits appear under Analytics → AI Crawling as they happen.

This works on Vercel, Netlify and Cloudflare Pages, for statically generated pages too, because middleware runs on every request. It does not work for a static export served from a storage bucket with no function layer.

Not on Next.js?

Any static site generator works. The API is plain JSON:

  • GET /api/v1/articles lists your published articles, paginated by cursor.
  • GET /api/v1/articles/{slug} returns one article with its rendered HTML.

Authenticate both with your read:content key as a Bearer header, write your pages at build time, and set a build hook on the destination. See the REST API page for the full endpoint reference.

Navigation

Type to search…

↑↓ navigate↵ selectEsc close