Mentionwell

Same data flow as the App Router quickstart, with getStaticProps + ISR.

Reader helper

Same as the App Router version.

Index page

// pages/blog/index.tsx
import Link from "next/link";
import type { GetStaticProps } from "next";
import { listPosts } from "@/lib/blogoto";

export const getStaticProps: GetStaticProps = async () => {
  const { posts } = await listPosts(1, 24);
  return { props: { posts }, revalidate: 300 };
};

export default function BlogIndex({ posts }: { posts: any[] }) {
  return (
    <ul>
      {posts.map((post) => (
        <li key={post.slug}>
          <Link href={`/blog/${post.slug}`}>{post.title}</Link>
        </li>
      ))}
    </ul>
  );
}

Detail page

// pages/blog/[slug].tsx
import type { GetStaticPaths, GetStaticProps } from "next";
import { getPost, allSlugs } from "@/lib/blogoto";

export const getStaticPaths: GetStaticPaths = async () => {
  const slugs = await allSlugs();
  return { paths: slugs.map((slug) => ({ params: { slug } })), fallback: "blocking" };
};

export const getStaticProps: GetStaticProps = async ({ params }) => {
  const post = await getPost(params!.slug as string);
  if (!post) return { notFound: true };
  return { props: { post }, revalidate: 300 };
};

export default function BlogPost({ post }: { post: any }) {
  return (
    <article>
      <h1>{post.title}</h1>
      <div dangerouslySetInnerHTML={{ __html: post.html }} />
    </article>
  );
}

For revalidation, use res.revalidate('/blog/...') inside an API route that verifies the webhook signature.