Skip to content

mrjasonroy/cache-components-cache-handler

Repository files navigation

Cache Components Cache Handler

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

npm version License: MIT TypeScript Next.js 16+ Tests

📦 View on npm | 🚀 Releases

What This Does

Implements Next.js 16+ caching APIs:

  • "use cache" directive
  • cacheLife() - Configure cache lifetime
  • cacheTag() - Tag cache entries
  • revalidateTag() - Invalidate by tags
  • updateTag() - Update tags
  • revalidatePath() - Invalidate by path
  • Cache Components and PPR

Supported Backends

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

Install

npm install @mrjasonroy/cache-components-cache-handler

# For Redis/Valkey/ElastiCache, also install ioredis
npm install ioredis

Quick Start

Zero-Config (Recommended)

// 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:6380 when 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 + port
  • ELASTICACHE_TLS - "true"/"false" toggle (defaults to true for ElastiCache)
  • ELASTICACHE_AUTH_TOKEN - Password/token for IAM-authenticated ElastiCache clusters

Advanced: Custom Configuration

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",
});

Usage

// 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 });
}

Cache Handler API

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.

Documentation

CI & Test Matrix

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

Development

git clone https://github.com/mrjasonroy/cache-components-cache-handler
cd cache-components-cache-handler
pnpm install
pnpm build
pnpm test

See docs/development.md for details.

Contributing

This project is AI-friendly and contributor-friendly.

For Maintainers

Publishing and release documentation:

License

MIT © Jason Roy

Acknowledgments

Forked from/Inspired by @fortedigital/nextjs-cache-handler, but focusing only on supporting Next.js 16+ and the new caching system.