Skip to content

Next.js quickstart

@ghostwritr/next is a keyless, server-only data client for the App Router. It pulls your published articles from the public feed — you render the Markdown. Requires Next 14+.

  1. Install the SDK (and the renderer used below):

    npm install @ghostwritr/next @ghostwritr/react
  2. Add your site ID to .env.local. It’s the only key you need — see Your site ID.

    .env.local
    GHOSTWRITR_SITE_ID=your-site-id
  3. Create a server-only client as a module singleton. createGhostwritr lives at the /server subpath:

    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)
    });
  4. Render the article page. gw.generateStaticParams is ready to export as-is:

    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 { slug } = await params;
    const article = await gw.getArticle(slug);
    if (!article) notFound();
    return (
    <article>
    <h1>{article.title}</h1>
    <ArticleContent markdown={article.markdown} className="prose" />
    </article>
    );
    }
  5. Render the index, newest first:

    app/blog/page.tsx
    import Link from "next/link";
    import { gw } from "@/lib/ghostwritr";
    export default async function Blog() {
    const articles = await gw.getArticles();
    return (
    <ul>
    {articles.map((a) => (
    <li key={a.id}>
    <Link href={`/blog/${a.slug}`}>{a.title}</Link>
    </li>
    ))}
    </ul>
    );
    }

That’s a complete, statically-generated blog reading from the keyless feed. Run next dev and open /blog.

  • Pick a rendering mode — ISR, pure SSG, or dynamic SSR is one config flag. See Rendering modes.
  • Publish instantly — wire the signed feed.updated webhook so new articles appear in seconds, not on the revalidate clock. See Instant revalidation.
  • SEO — drive generateMetadata and app/sitemap.ts from the article fields. See SEO metadata & sitemap.