← all conversations

Infinite Scroll Blog

2025-06-212 turns4,549 charsgpt-4o
nextjsinfinite-scrollblog

Summary

Convert a Next.js blog post into an infinite scroll blog page with dynamic post loading.

Messages

make this into a blog page that has infinite scroll: import { notFound } from "next/navigation"; import { getPostData } from "@/lib/posts"; import MarkdownRenderer from "@/components/markdown-renderer"; export { generateStaticParams } from "./generate-static-params"; // eslint-disable-next-line @typescript-eslint/no-explicit-any export default async function Page(props: any) { const { params } = props; const post = await getPostData(params.slug); if (!post) { notFound(); } return ( <main className="container mx-auto px-4 py-8"> <article className="prose lg:prose-xl dark:prose-invert mx-auto"> <h1>{post.title}</h1> <p className="text-gray-500 dark:text-gray-400"> {new Date(post.date).toLocaleDateString("en-US", { year: "numeric", month: "long", day: "numeric", })} </p> {post.serializedContent && ( <MarkdownRenderer source={post.serializedContent} /> )} </article> </main> ); }
To turn this `Page` component into a **blog with infinite scroll**, you'll need to **load multiple posts progressively** instead of rendering just one post from `params.slug`. That means this is no longer a **dynamic route for a single post**—instead, you're building a `/blog` or `/posts` **feed** page. Below is a modified version that: 1. Fetches an initial set of blog posts. 2. Renders them in a list. 3. Adds infinite scrolling using `IntersectionObserver`. --- ### ✅ Create a new file: `/app/blog/page.tsx` ```tsx "use client"; import { useEffect, useRef, useState } from "react"; import { getPaginatedPosts } from "@/lib/posts"; import MarkdownRenderer from "@/components/markdown-renderer"; type Post = { slug: string; title: string; date: string; serializedContent: string; }; const POSTS_PER_PAGE = 5; export default function BlogPage() { const [posts, setPosts] = useState<Post[]>([]); const [page, setPage] = useState(1); const [hasMore, setHasMore] = useState(true); const loaderRef = useRef<HTMLDivElement>(null); useEffect(() => { const loadPosts = async () => { const { posts: newPosts, hasMore } = await getPaginatedPosts(page, POSTS_PER_PAGE); setPosts((prev) => [...prev, ...newPosts]); setHasMore(hasMore); }; loadPosts(); }, [page]); useEffect(() => { const observer = new IntersectionObserver( ([entry]) => { if (entry.isIntersecting && hasMore) { setPage((prev) => prev + 1); } }, { threshold: 1.0 } ); const loader = loaderRef.current; if (loader) observer.observe(loader); return () => { if (loader) observer.unobserve(loader); }; }, [hasMore]); return ( <main className="container mx-auto px-4 py-8"> <h1 className="text-4xl font-bold mb-8 text-center">Blog</h1> {posts.map((post) => ( <article key={post.slug} className="prose lg:prose-xl dark:prose-invert mx-auto mb-12"> <h2>{post.title}</h2> <p className="text-gray-500 dark:text-gray-400"> {new Date(post.date).toLocaleDateString("en-US", { year: "numeric", month: "long", day: "numeric", })} </p> {post.serializedContent && ( <MarkdownRenderer source={post.serializedContent} /> )} </article> ))} {hasMore && ( <div ref={loaderRef} className="text-center py-8"> <span className="text-gray-500 dark:text-gray-400">Loading more...</span> </div> )} </main> ); } ``` --- ### 🧠 Backend Function (`getPaginatedPosts`) Make sure `@/lib/posts.ts` includes a function like this: ```ts export async function getPaginatedPosts(page: number, limit: number) { const allPosts = await getAllPosts(); // Assume this returns an array sorted by date descending const start = (page - 1) * limit; const end = start + limit; const paginated = allPosts.slice(start, end); return { posts: paginated, hasMore: end < allPosts.length, }; } ``` --- ### 📝 Notes - This should live at `/blog`. - You'll want `getAllPosts()` to return posts with serialized Markdown. - If you’re using `getPostData(slug)` to serialize Markdown, you'll need to modify it to accept multiple slugs for batching. - Optionally, add skeleton loaders or animated transitions with `framer-motion`. Want me to help you wire this into your existing project structure or switch this to a REST API with SSR?
gpt-4o