Skip to content

Commit 5cbcbd5

Browse files
committed
Add comprehensive example structure and navigation for Next.js 16
Major Improvements: - Add Navigation component with links to all examples - Create informative homepage explaining all features - Improve layout with navigation and better structure - Rename fetch-example to fetch-cache with detailed UI - Add TTL/Expiration example with clear explanations - Add API routes for tag and path revalidation - Add EXAMPLES_TODO.md tracking remaining work Examples Added/Improved: - fetch-cache: Fetch caching with tags and time-based revalidation - ttl-example: Demonstrates TTL behavior (stale/expire ages) - API routes: /api/revalidate-tag and /api/revalidate-path Structure Improvements: - Navigation bar with all examples - Consistent styling and layout - Better error handling and user feedback - Educational content explaining how each feature works Remaining Work (see EXAMPLES_TODO.md): - revalidate-tag/path UI pages - use-cache-demo example - Verify existing ISR/PPR/static-params examples - Comprehensive Playwright tests - Full documentation
1 parent 9564453 commit 5cbcbd5

File tree

9 files changed

+480
-36
lines changed

9 files changed

+480
-36
lines changed
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
# Next.js 16 Cache Handler - Examples TODO
2+
3+
This document tracks the completion status of all example implementations based on [PR #109](https://github.com/fortedigital/nextjs-cache-handler/pull/109).
4+
5+
## Completed ✅
6+
7+
- [x] Create navigation structure with shared layout
8+
- [x] Improve homepage with examples overview
9+
- [x] Rename fetch-example to fetch-cache and improve
10+
- [x] Add TTL/Expiration example
11+
- [x] Add revalidate-tag API route
12+
- [x] Add revalidate-path API route
13+
14+
## In Progress 🚧
15+
16+
### Core Feature Examples
17+
18+
- [ ] **revalidate-tag page** - Interactive UI for testing tag revalidation
19+
- [ ] **revalidate-path page** - Interactive UI for testing path revalidation
20+
- [ ] **use-cache-demo** - Demonstrate Next.js 16 "use cache" directive
21+
- [ ] Verify and update **ISR example** (isr-example/blog/[id])
22+
- [ ] Verify and update **static-params-test**
23+
- [ ] Verify and update **PPR example**
24+
25+
### Additional Examples Needed
26+
27+
- [ ] **Multiple tags example** - Show revalidation with multiple tags
28+
- [ ] **Nested caching example** - Component-level caching
29+
- [ ] **Error handling example** - Cache behavior during errors
30+
- [ ] **Conditional caching example** - Dynamic cache decisions
31+
32+
## Testing 🧪
33+
34+
### Playwright E2E Tests
35+
36+
- [ ] Test fetch-cache example
37+
- [ ] Verify data is cached
38+
- [ ] Verify TTL expiration
39+
- [ ] Verify tag revalidation works
40+
- [ ] Test ISR example
41+
- [ ] Verify static generation
42+
- [ ] Verify dynamic params work
43+
- [ ] Verify revalidation
44+
- [ ] Test TTL example
45+
- [ ] Verify cache freshness
46+
- [ ] Verify staleness behavior
47+
- [ ] Verify expiration
48+
- [ ] Test tag revalidation
49+
- [ ] Test single tag revalidation
50+
- [ ] Test multiple tag revalidation
51+
- [ ] Verify cascade effects
52+
- [ ] Test path revalidation
53+
- [ ] Test single path revalidation
54+
- [ ] Test layout revalidation
55+
- [ ] Test nested path revalidation
56+
- [ ] Test "use cache" directive
57+
- [ ] Verify component caching
58+
- [ ] Verify it's separate from cache handler
59+
- [ ] Test PPR example
60+
- [ ] Verify partial prerendering
61+
- [ ] Verify dynamic/static mix
62+
- [ ] Test production build
63+
- [ ] Verify all features work in production
64+
- [ ] Verify Redis integration
65+
- [ ] Verify cache persistence
66+
67+
## Documentation 📚
68+
69+
- [ ] Add comprehensive README for examples/
70+
- [ ] Document each example's purpose and behavior
71+
- [ ] Add troubleshooting section
72+
- [ ] Add Redis setup instructions
73+
- [ ] Document differences between Next.js 15 and 16 caching
74+
75+
## Production Readiness 🚀
76+
77+
- [ ] Verify instrumentation works correctly
78+
- [ ] Test with Redis in production mode
79+
- [ ] Test with LRU fallback
80+
- [ ] Performance benchmarking
81+
- [ ] Memory usage monitoring
82+
- [ ] Error handling and logging
83+
84+
## Known Issues ⚠️
85+
86+
From PR #109:
87+
- Tags may not save properly in development mode (needs verification)
88+
- Need to test all features in both dev and production modes
89+
90+
## Next Steps
91+
92+
1. Complete all remaining example pages
93+
2. Write comprehensive Playwright tests
94+
3. Add detailed documentation
95+
4. Test production builds
96+
5. Performance testing
97+
6. Create migration guide from Next.js 15 to 16
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { revalidatePath } from "next/cache";
2+
import { NextRequest } from "next/server";
3+
4+
export async function GET(request: NextRequest) {
5+
const searchParams = request.nextUrl.searchParams;
6+
const path = searchParams.get("path");
7+
8+
if (!path) {
9+
return Response.json({ error: "Path parameter is required" }, { status: 400 });
10+
}
11+
12+
try {
13+
revalidatePath(path);
14+
return Response.json({
15+
revalidated: true,
16+
path,
17+
timestamp: new Date().toISOString()
18+
});
19+
} catch (error) {
20+
return Response.json({ error: "Failed to revalidate" }, { status: 500 });
21+
}
22+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { revalidateTag } from "next/cache";
2+
import { NextRequest } from "next/server";
3+
4+
export async function GET(request: NextRequest) {
5+
const searchParams = request.nextUrl.searchParams;
6+
const tag = searchParams.get("tag");
7+
8+
if (!tag) {
9+
return Response.json({ error: "Tag parameter is required" }, { status: 400 });
10+
}
11+
12+
try {
13+
revalidateTag(tag);
14+
return Response.json({
15+
revalidated: true,
16+
tag,
17+
timestamp: new Date().toISOString()
18+
});
19+
} catch (error) {
20+
return Response.json({ error: "Failed to revalidate" }, { status: 500 });
21+
}
22+
}
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
export const metadata = {
2+
title: "Fetch Cache Example",
3+
};
4+
5+
export default async function FetchCachePage() {
6+
const timestamp = new Date().toISOString();
7+
8+
try {
9+
const characterResponse = await fetch(
10+
"https://api.sampleapis.com/futurama/characters/1",
11+
{
12+
next: {
13+
revalidate: 60, // Revalidate every 60 seconds
14+
tags: ["futurama", "character-1"],
15+
},
16+
}
17+
);
18+
19+
const character = await characterResponse.json();
20+
const name = character.name?.first || "Unknown";
21+
22+
return (
23+
<div className="space-y-6">
24+
<div>
25+
<h1 className="text-3xl font-bold mb-4">Fetch Cache Example</h1>
26+
<p className="text-gray-600">
27+
This page demonstrates fetch caching with tags and time-based revalidation.
28+
</p>
29+
</div>
30+
31+
<div className="bg-white border border-gray-200 rounded-lg p-6">
32+
<h2 className="text-xl font-semibold mb-4">Cached Data</h2>
33+
<div className="space-y-2">
34+
<p><strong>Character Name:</strong> {name}</p>
35+
<p><strong>Page Rendered At:</strong> <code className="bg-gray-100 px-2 py-1 rounded">{timestamp}</code></p>
36+
</div>
37+
</div>
38+
39+
<div className="bg-blue-50 border-l-4 border-blue-500 p-4">
40+
<h3 className="font-semibold mb-2">Caching Configuration</h3>
41+
<ul className="list-disc list-inside space-y-1 text-sm">
42+
<li><strong>Revalidate:</strong> 60 seconds</li>
43+
<li><strong>Tags:</strong> futurama, character-1</li>
44+
<li>The fetch is cached by the custom cache handler</li>
45+
<li>Cached in Redis (primary) or LRU (fallback)</li>
46+
</ul>
47+
</div>
48+
49+
<div className="bg-yellow-50 border-l-4 border-yellow-500 p-4">
50+
<h3 className="font-semibold mb-2">Test Revalidation</h3>
51+
<p className="text-sm mb-3">
52+
Trigger on-demand revalidation by tag:
53+
</p>
54+
<a
55+
href="/api/revalidate-tag?tag=futurama"
56+
className="inline-block bg-blue-600 text-white px-4 py-2 rounded hover:bg-blue-700"
57+
>
58+
Revalidate &quot;futurama&quot; Tag
59+
</a>
60+
</div>
61+
62+
<div>
63+
<h3 className="font-semibold mb-2">How It Works</h3>
64+
<ol className="list-decimal list-inside space-y-2 text-sm text-gray-700">
65+
<li>First request fetches from API and caches the result</li>
66+
<li>Subsequent requests return cached data (fast!)</li>
67+
<li>After 60 seconds, cache becomes stale</li>
68+
<li>Next request triggers background revalidation</li>
69+
<li>Or manually revalidate using tags via the API button above</li>
70+
</ol>
71+
</div>
72+
</div>
73+
);
74+
} catch (error) {
75+
console.error("Error fetching character data:", error);
76+
return (
77+
<div className="space-y-6">
78+
<h1 className="text-3xl font-bold mb-4">Fetch Cache Example</h1>
79+
<div className="bg-red-50 border-l-4 border-red-500 p-4">
80+
<p className="font-semibold">Error fetching data</p>
81+
<p className="text-sm mt-2">
82+
Could not fetch character data. This might be due to network issues.
83+
</p>
84+
</div>
85+
</div>
86+
);
87+
}
88+
}

examples/redis-minimal/src/app/fetch-example/page.tsx

Lines changed: 0 additions & 28 deletions
This file was deleted.

examples/redis-minimal/src/app/layout.tsx

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import type { Metadata } from "next";
22
import { Geist, Geist_Mono } from "next/font/google";
33
import "./globals.css";
4+
import { Navigation } from "@/components/Navigation";
45

56
const geistSans = Geist({
67
variable: "--font-geist-sans",
@@ -13,8 +14,8 @@ const geistMono = Geist_Mono({
1314
});
1415

1516
export const metadata: Metadata = {
16-
title: "Create Next App",
17-
description: "Generated by create next app",
17+
title: "Next.js 16 Cache Handler Examples",
18+
description: "Examples demonstrating @fortedigital/nextjs-cache-handler with Next.js 16",
1819
};
1920

2021
export default function RootLayout({
@@ -27,7 +28,10 @@ export default function RootLayout({
2728
<body
2829
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
2930
>
30-
{children}
31+
<Navigation />
32+
<main className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
33+
{children}
34+
</main>
3135
</body>
3236
</html>
3337
);

0 commit comments

Comments
 (0)