Cache handler for Next.js 16+ with support for Cache Components and "use cache" directive.
Why another cache handler? I was originally going to contribute to fortedigital/nextjs-cache-handler but that project is focused on backwards compatibility and carries a lot of legacy baggage. This repo is a ground-up rewrite that:
- Targets Next.js 16+ only (Cache Components,
"use cache",cacheLife, etc.)- Ships with true E2E coverage (Playwright + Next 16) for every backend
- Keeps the surface area small: zero-config memory handler for dev, Redis/Valkey/ElastiCache factory for prod
- Provides an AI-first workflow so contributors (human or agent) can ship confidently
📦 View on npm | 🚀 Releases
Implements Next.js 16+ caching APIs:
"use cache"directivecacheLife()- Configure cache lifetimecacheTag()- Tag cache entriesrevalidateTag()- Invalidate by tagsupdateTag()- Update tagsrevalidatePath()- Invalidate by path- Cache Components and PPR
All backends are integration tested with Next.js 16+ in CI so you can trust a release tag.
| Backend | Use Case | Key Features |
|---|---|---|
| Memory | Development, Single-instance | Zero config, fast, no external dependencies |
| Redis | Production, Distributed | Industry standard, high performance, clustering |
| Valkey | Production, Open Source | Redis-compatible, LGPL licensed, drop-in replacement |
| AWS ElastiCache | Production, Managed | Fully managed, IAM auth, auto-scaling, high availability |
npm install @mrjasonroy/cache-components-cache-handler
# For Redis/Valkey/ElastiCache, also install ioredis
npm install ioredis// data-cache-handler.mjs
import { createCacheHandler } from "@mrjasonroy/cache-components-cache-handler";
// Memory (dev)
export default createCacheHandler({ type: "memory" });
// Redis (production)
export default createCacheHandler({ type: "redis" });
// Valkey (production)
export default createCacheHandler({ type: "valkey" });
// ElastiCache (AWS)
export default createCacheHandler({ type: "elasticache" });// next.config.js
export default {
cacheComponents: true,
cacheHandler: "./cache-handler.mjs", // Optional: ISR handler (Memory by default)
cacheHandlers: {
default: "./data-cache-handler.mjs",
remote: "./data-cache-handler.mjs",
},
cacheMaxMemorySize: 0, // Disable Next's built-in in-memory handler
};Environment Variables:
REDIS_URL/VALKEY_URL- Primary connection string for Redis-compatible stores (Valkey works with the same URI format, e.g.redis://localhost:6380when using the provided docker-compose service)REDIS_PASSWORD- Password/token used when your URL lacks credentials (works for every backend)ELASTICACHE_ENDPOINT/ELASTICACHE_PORT- AWS ElastiCache hostname + portELASTICACHE_TLS-"true"/"false"toggle (defaults totruefor ElastiCache)ELASTICACHE_AUTH_TOKEN- Password/token for IAM-authenticated ElastiCache clusters
Need more control? You can override any option:
// data-cache-handler.mjs
import { createCacheHandler } from "@mrjasonroy/cache-components-cache-handler";
export default createCacheHandler({
type: "elasticache",
endpoint: "cache.prod-cluster.cache.amazonaws.com",
port: 6380,
tls: true,
password: process.env.CACHE_AUTH_TOKEN,
keyPrefix: "myapp:cache:",
tagPrefix: "myapp:tags:",
debug: process.env.NODE_ENV === "development",
});// Basic caching
async function ProductList() {
"use cache";
const products = await db.products.findMany();
return <ProductGrid products={products} />;
}
// With cache lifetime
import { cacheLife } from "next/cache";
async function BlogPost({ id }: { id: string }) {
"use cache";
cacheLife("hours"); // 1 hour
const post = await db.posts.findUnique({ where: { id } });
return <Article post={post} />;
}
// With tags for selective invalidation
import { cacheTag } from "next/cache";
async function UserProfile({ userId }: { userId: string }) {
"use cache";
cacheTag("user-profile", `user:${userId}`);
const user = await db.user.findUnique({ where: { id: userId } });
return <Profile user={user} />;
}
// Invalidate from API route
import { revalidateTag } from "next/cache";
export async function POST(request: Request) {
const { userId } = await request.json();
revalidateTag(`user:${userId}`);
return Response.json({ revalidated: true });
}This library implements the Next.js 16+ DataCacheHandler interface:
interface DataCacheHandler {
// Retrieve cached entry for a cache key
get(cacheKey: string, softTags: string[]): Promise<DataCacheEntry | undefined>;
// Store a cache entry (handles streaming responses)
set(cacheKey: string, pendingEntry: Promise<DataCacheEntry>): Promise<void>;
// Called periodically to refresh local tag manifest
refreshTags(): Promise<void>;
// Get maximum revalidation timestamp for tags
getExpiration(tags: string[]): Promise<Timestamp>;
// Called when revalidateTag() invalidates tags
updateTags(tags: string[], durations?: { expire?: number }): Promise<void>;
}When you call revalidateTag('my-tag') in your application, Next.js internally calls our updateTags(['my-tag']) implementation, which marks all cache entries with that tag as stale.
- Installation & Setup
- Usage Examples
- API Reference
- Redis Configuration
- Valkey & ElastiCache Setup (coming from same guide)
- Contributing
| Workflow | What it Verifies |
|---|---|
.github/workflows/ci.yml |
Biome lint + format check, typecheck, Vitest, and Playwright e2e suites against Memory, Redis, Valkey, and ElastiCache-mode backends |
.github/workflows/nextjs-canary-test.yml |
Daily compatibility runs against Next.js canary/rc/latest to catch upstream breakage |
.github/workflows/nextjs-version-check.yml |
Nightly scan for new Next.js releases, opens PRs to bump peer deps |
.github/workflows/publish.yml |
Trusted publishing with provenance – release tags must pass the full matrix before npm sees them |
git clone https://github.com/mrjasonroy/cache-components-cache-handler
cd cache-components-cache-handler
pnpm install
pnpm build
pnpm testSee docs/development.md for details.
This project is AI-friendly and contributor-friendly.
- For AI Agents: See CLAUDE.md for instructions
- For Humans: See CONTRIBUTING.md
Publishing and release documentation:
- Automated Releases - Automated release workflow
- Prerelease Versions - Alpha, beta, and RC releases
- Release Checklist - Complete release process
MIT © Jason Roy
Forked from/Inspired by @fortedigital/nextjs-cache-handler, but focusing only on supporting Next.js 16+ and the new caching system.