
Partial Prerendering (PPR) & Server Actions in Next.js: Lessons Learned in Production
10/08/2026 - 7 min read
By Augustine Cobbold
The Static vs Dynamic Dilemma
For years, web developers had to choose between two architectural extremes: static generation (SSG) for blistering CDN delivery, or dynamic server rendering (SSR) for personalized, real-time user data. Static sites lacked real-time freshness; dynamic sites suffered from slower Time-to-First-Byte (TTFB).
Partial Prerendering (PPR) solves this by combining both in a single HTTP request. The static layout shell (navbars, headers, hero marketing) is served instantly from edge cache, while dynamic components stream in parallel over the open connection wrapped in React Suspense boundaries.
Hardening Server Actions for Production
Server Actions simplify form handling and RPC mutations, but exposing backend logic directly to client components requires rigorous validation and authorization. Never trust client payloads:
'use server';
import { z } from 'zod';
import { revalidateTag } from 'next/cache';
import { auth } from '@/lib/auth';
const FeedbackSchema = z.object({
message: z.string().min(5).max(1000),
rating: z.number().int().min(1).max(5),
});
export async function submitUserFeedback(formData: FormData) {
const session = await auth();
if (!session) {
return { error: 'Unauthorized' };
}
const parse = FeedbackSchema.safeParse({
message: formData.get('message'),
rating: Number(formData.get('rating')),
});
if (!parse.success) {
return { error: 'Invalid submission data', issues: parse.error.issues };
}
await db.feedback.create({
data: { userId: session.user.id, ...parse.data }
});
revalidateTag('feedback', 'max');
return { success: true };
}Summary
Partial Prerendering and Server Actions transform Next.js into a powerhouse for high-conversion web products. By separating static layout chrome from dynamic streaming holes, teams deliver both enterprise security and instant sub-second page transitions.