In the Quantum Metropolis of 2150, system performance is a matter of survival. Every millisecond of delay can mean data loss in the quantum network or failure of critical systems. That's why Metropolis engineers must thoroughly understand the performance differences between rendering strategies in Next.js 15 and be able to choose the optimal solution for each case.
Benchmarks are performance measurements of an application under controlled conditions. They allow comparing different rendering strategies against key metrics such as:
SSG generates pages at application build time. The resulting HTML files are served directly from a CDN, ensuring the lowest possible latency.
1// app/blog/[slug]/page.tsx - SSG in Next.js 15
2import { notFound } from 'next/navigation';
3
4// Generating static paths at build time
5export async function generateStaticParams() {
6 const posts = await fetch('https://api.example.com/posts').then(r => r.json());
7 return posts.map((post: { slug: string }) => ({ slug: post.slug }));
8}
9
10// Default caching = SSG
11async function getPost(slug: string) {
12 const res = await fetch(\`https://api.example.com/posts/\${slug}\`);
13 if (!res.ok) return null;
14 return res.json();
15}
16
17export default async function BlogPost({ params }: { params: Promise<{ slug: string }> }) {
18 const post = await getPost((await params).slug);
19 if (!post) notFound();
20
21 return (
22 <article>
23 <h1>{post.title}</h1>
24 <p>{post.content}</p>
25 </article>
26 );
27}Typical SSG Metrics:
| Metric | Value | Rating | |--------|-------|--------| | TTFB | 5-50ms | Excellent | | FCP | 100-300ms | Excellent | | LCP | 200-500ms | Excellent | | TTI | 300-800ms | Very good | | CLS | 0-0.05 | Excellent |
SSR renders pages on the server for every request. It is slower than SSG but ensures always up-to-date data.
1// app/dashboard/page.tsx - SSR in Next.js 15
2import { cookies } from 'next/headers';
3
4// Forcing dynamic rendering
5export const dynamic = 'force-dynamic';
6
7async function getDashboardData(token: string) {
8 const res = await fetch('https://api.example.com/dashboard', {
9 headers: { Authorization: \`Bearer \${token}\` },
10 cache: 'no-store', // No caching = SSR
11 });
12 return res.json();
13}
14
15export default async function Dashboard() {
16 const cookieStore = await cookies();
17 const token = cookieStore.get('auth-token')?.value || '';
18 const data = await getDashboardData(token);
19
20 return (
21 <div>
22 <h1>User Dashboard</h1>
23 <p>Balance: {data.balance} QC</p>
24 <p>Last activity: {data.lastActivity}</p>
25 </div>
26 );
27}Typical SSR Metrics:
| Metric | Value | Rating | |--------|-------|--------| | TTFB | 100-500ms | Average | | FCP | 300-800ms | Good | | LCP | 500-1200ms | Average | | TTI | 500-1500ms | Average | | CLS | 0-0.1 | Good |
ISR combines the advantages of SSG (speed) with SSR (data freshness). Pages are generated statically but refreshed in the background after a specified time period.
1// app/products/page.tsx - ISR in Next.js 15
2async function getProducts() {
3 const res = await fetch('https://api.example.com/products', {
4 next: { revalidate: 60 }, // Revalidation every 60 seconds
5 });
6 return res.json();
7}
8
9export default async function ProductsPage() {
10 const products = await getProducts();
11
12 return (
13 <div>
14 <h1>Quantum Store Products</h1>
15 <div className="grid">
16 {products.map((product: any) => (
17 <div key={product.id}>
18 <h2>{product.name}</h2>
19 <p>Price: {product.price} QC</p>
20 </div>
21 ))}
22 </div>
23 </div>
24 );
25}Typical ISR Metrics:
| Metric | Value (cache hit) | Value (revalidation) | Rating | |--------|-------------------|----------------------|--------| | TTFB | 5-50ms | 100-400ms | Very good | | FCP | 100-300ms | 300-700ms | Very good | | LCP | 200-500ms | 500-1000ms | Good | | TTI | 300-800ms | 500-1200ms | Good | | CLS | 0-0.05 | 0-0.05 | Excellent |
Next.js 15 introduces improved on-demand revalidation that allows refreshing the cache without waiting for the revalidation time to expire:
1// app/api/revalidate/route.ts
2import { revalidatePath, revalidateTag } from 'next/cache';
3import { NextRequest, NextResponse } from 'next/server';
4
5export async function POST(request: NextRequest) {
6 const { path, tag, secret } = await request.json();
7
8 // Security token verification
9 if (secret !== process.env.REVALIDATION_SECRET) {
10 return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
11 }
12
13 if (tag) {
14 // Tag-based revalidation - refreshes all fetches with the given tag
15 revalidateTag(tag);
16 return NextResponse.json({ revalidated: true, tag });
17 }
18
19 if (path) {
20 // Path-based revalidation - refreshes a specific page
21 revalidatePath(path);
22 return NextResponse.json({ revalidated: true, path });
23 }
24
25 return NextResponse.json({ error: 'Missing path or tag' }, { status: 400 });
26}
27
28// Using tags in fetch
29async function getProduct(id: string) {
30 const res = await fetch(\`https://api.example.com/products/\${id}\`, {
31 next: {
32 revalidate: 3600, // Cache for 1 hour
33 tags: [\`product-\${id}\`, 'products'], // Tags for revalidation
34 },
35 });
36 return res.json();
37}In Next.js 15, you can combine different rendering strategies within a single page thanks to Partial Prerendering (PPR):
1// app/store/page.tsx - Hybrid approach
2import { Suspense } from 'react';
3
4// Static part of the page (SSG)
5function StaticHeader() {
6 return (
7 <header>
8 <h1>Quantum Store</h1>
9 <nav>
10 <a href="/products">Products</a>
11 <a href="/categories">Categories</a>
12 </nav>
13 </header>
14 );
15}
16
17// Dynamic part of the page (SSR)
18async function DynamicCart() {
19 const res = await fetch('https://api.example.com/cart', { cache: 'no-store' });
20 const cart = await res.json();
21
22 return (
23 <div>
24 <h2>Your Cart ({cart.items.length} products)</h2>
25 <p>Total: {cart.total} QC</p>
26 </div>
27 );
28}
29
30// ISR page with dynamic elements
31async function ProductList() {
32 const res = await fetch('https://api.example.com/featured', {
33 next: { revalidate: 300 },
34 });
35 const products = await res.json();
36
37 return (
38 <div>
39 {products.map((p: any) => (
40 <div key={p.id}>{p.name} - {p.price} QC</div>
41 ))}
42 </div>
43 );
44}
45
46export default function StorePage() {
47 return (
48 <div>
49 <StaticHeader />
50 <Suspense fallback={<p>Loading cart...</p>}>
51 <DynamicCart />
52 </Suspense>
53 <Suspense fallback={<p>Loading products...</p>}>
54 <ProductList />
55 </Suspense>
56 </div>
57 );
58}1// app/layout.tsx
2import { SpeedInsights } from '@vercel/speed-insights/next';
3import { Analytics } from '@vercel/analytics/react';
4
5export default function RootLayout({ children }: { children: React.ReactNode }) {
6 return (
7 <html lang="en">
8 <body>
9 {children}
10 <SpeedInsights />
11 <Analytics />
12 </body>
13 </html>
14 );
15}1# Building with profiling
2NEXT_TELEMETRY_DEBUG=1 next build
3
4# Bundle size analysis
5ANALYZE=true next build| Scenario | Strategy | Why? | |----------|----------|------| | Blog, documentation | SSG | Content doesn't change often | | E-commerce (product list) | ISR (60-300s) | Products change, but not every second | | User dashboard | SSR | Data must always be current | | Home page with promotions | ISR (300-3600s) | Promotions change every few hours | | Admin panel | SSR | Requires current data and authorization | | Landing page | SSG | Maximum speed, static content | | Social feed | SSR + Streaming | Dynamic data, progressive loading |
The choice of rendering strategy in Next.js 15 should be driven by the specific requirements of the project:
In the Quantum Metropolis of 2150, engineers base their decisions on data - they measure, analyze, and optimize. Performance benchmarks are a key tool in this process.