Partial Prerendering in Next.js

6 min read

When I was working on my latest side project where I was building a platform for users who want to start writing their own blogs, I had a problem with the article page, because I wanted to statically generate them (SSG), but also I wanted it to fetch the comments dynamically. You can't simply put a comments section inside the page that uses generateStaticParams because in this scenario the application couldn't be built. I did a research on how to handle this and the newest version of Next.js (while I'm writing this post, it's still an experimental feature) introduces something called Partial Prerendering which allows you to put dynamic content on statically generated pages!

How it works?

Before we start

As I mentioned before, while I'm writing this post, Partial Prerendering is marked as experimental feature, so we need to enable it in next.config.js

/** @type {import('next').NextConfig} */
const nextConfig = {
	experimental: {
		ppr: true,
	},
};

export default nextConfig;

Let's code it!

For the purpose of this tutorial, we will be using the code from my latest project. The code below showcases the static generation of article pages using generateStaticParams and hole for dynamic elements wrapped with Suspense.

 ...
export const generateStaticParams = async () => {
	const posts = await getAllPosts();
	return posts.map((post) => ({
		params: { slug: post.slug },
	}));
};

export async function generateMetadata({ params }: Props): Promise<Metadata> {
	const post = await getPostBySlug(params.slug);

	return {
		title: post.title,
		description: post.description,
	};
}

export default async function PostPage({ params: { slug } }: Props) {
	const post = await getPostBySlug(slug);

	if (!post) {
		return notFound();
	}

	const { title, profiles, created_at, categories, content } = post;

	return;
	<section className="mx-auto mb-20 max-w-4xl px-6">
                ...
		<article className="prose prose-invert max-w-none">
			<MDXRemote source={content} />
		</article>
		<Suspense fallback={<LoadingIndicator />}>
			<CommentsContainer id={post.id} />
		</Suspense>
	</section>;
}

Comments component

Now we can simply fetch our dynamic data any way we want, I used react-query

...
export const CommentsContainer = async ({ id }: { id: number }) => {
	const queryClient = new QueryClient();
	const session = await getServerSession(authOptions);

	await queryClient.prefetchQuery({
		queryKey: ["comments", id],
		queryFn: () => getCommentsByPostID(id),
	});

	return (
		<div className="mt-20 border-t pt-5">
			<h2 className="mb-5 text-2xl font-bold">Comments</h2>
			<CommentsList id={id} />
			{session && <AddCommentForm post_id={id} />}
		</div>
	);
};

After our application build is successfully done, we can see that the article pages are marked as Partial Prerender

Screenshot 2024-03-06 at 22.02.27.png

Conclusion

Partial Prerendering is a powerful feature, and I am sure that it won't be experimental soon, it allows you to dynamically fetch data in static pages. I encourage you to check it out! Happy coding! 🚀