Skip to content

@ghostwritr/next — overview

@ghostwritr/next is the server-side data client for the Next.js App Router. It pulls your published articles from the public static feed and hands you typed Article objects to render however you like.

It’s a pure data client — no React, no rendering, no styles. You bring the markup; render the Markdown body with <ArticleContent> from @ghostwritr/react, with react-markdown, with MDX, or with your own renderer.

  • You’re on Next 14+ with the App Router (app/, server components, generateStaticParams, generateMetadata).
  • You want your articles to render as statically-generated pages — SSG, ISR, or dynamic SSR — your choice, one config flag.
  • You want zero credentials in your app: reads are keyless. Your siteId is the read capability — there’s no API key.

Install the SDK and the optional renderer:

npm install @ghostwritr/next @ghostwritr/react

Create a server-only client as a module singleton, then read articles in any server component:

lib/ghostwritr.ts
import "server-only";
import { createGhostwritr } from "@ghostwritr/next/server";
export const gw = createGhostwritr({
siteId: process.env.GHOSTWRITR_SITE_ID!, // public read key — no API key
revalidate: 3600, // ISR window in seconds (default 3600)
});
app/blog/[slug]/page.tsx
import { notFound } from "next/navigation";
import { ArticleContent } from "@ghostwritr/react";
import { gw } from "@/lib/ghostwritr";
export const generateStaticParams = gw.generateStaticParams;
export default async function Page({ params }: { params: Promise<{ slug: string }> }) {
const article = await gw.getArticle((await params).slug);
if (!article) notFound();
return (
<article>
<h1>{article.title}</h1>
<ArticleContent markdown={article.markdown} className="prose" />
</article>
);
}

That’s a complete, statically-generated article page reading from the keyless feed. The client exposes exactly three methods:

  • gw.getArticles({ order? }) — published articles, newest first by default.
  • gw.getArticle(slug) — one article by slug, or null.
  • gw.generateStaticParams()[{ slug }] for a [slug] route.

Reads hit the public static feed at feeds.ghostwritr.io/{siteId}/ — immutable build snapshots, no auth. A missing feed (wrong or unseeded siteId) throws a GhostwritrError with code NOT_FOUND; the read fails closed rather than returning empty. See Keyless reads and The content feed for the contract.

Within a single render the feed is fetched once (Next’s built-in fetch memoization). Across requests, freshness is governed entirely by your rendering mode.

  • Use @ghostwritr/next when you’re on the App Router and want statically-generated pages with typed data and your own markup.
  • On plain Astro, prefer the @ghostwritr/astro Content Layer loader.
  • On React Router v7, use @ghostwritr/react-router.
  • Building your own integration? Drop to the framework-agnostic @ghostwritr/feed core.