Next.js Redis Cache Integration Example
This example is tailored for self-hosted setups and demonstrates how to back Next.js caching with Redis, using no third-party adapter. It wires up both of Next.js's cache handler APIs against the redis client, storing everything in a single Redis instance:
cacheHandler(singular) — the ISR / incremental cache for pages, route handlers, and images. Implemented incache-handler.jswithget,set,revalidateTag, andresetRequestCache.cacheHandlers(plural) — the'use cache'family. Theremotehandler inremote-cache-handler.jsstores'use cache: remote'entries withget,set,refreshTags,getExpiration, andupdateTags.
Both are configured in next.config.js, which enables cacheComponents: true (required for 'use cache') and sets cacheMaxMemorySize: 0 so Redis is the single shared source of truth across instances.
Check out this repository that contains a comprehensive setup for Kubernetes.
How to use
Execute create-next-app with npm, Yarn, or pnpm to bootstrap the example:
npx create-next-app --example cache-handler-redis cache-handler-redis-app
yarn create next-app --example cache-handler-redis cache-handler-redis-app
pnpm create next-app --example cache-handler-redis cache-handler-redis-app
Once you have installed the dependencies, you can begin running the example Redis server by using the following command:
docker compose up -d
Then, build and start the Next.js app as usual. The custom cache handlers are only used for production builds (next start), so run:
npm run build
npm run start
To see the cache logs, set NEXT_PRIVATE_DEBUG_CACHE=1 when starting the app.
How it works
The /[timezone] page renders a mostly static shell and, inside it, a 'use cache: remote' function (getCurrentTime) that fetches the current time. This exercises both handlers at once:
-
ISR cache (
cache-handler.js): stores the prerendered page entries as JSON under anextjs:cache:prefix, and tracks which keys belong to each tag in a Redis set (nextjs:tag:<tag>).revalidateTagdeletes every key associated with a tag. -
Remote cache (
remote-cache-handler.js): stores each'use cache: remote'entry under anextjs:use-cache:prefix (the streamed value is base64-encoded). Tag revalidation is timestamp-based:updateTagsrecordsnextjs:use-cache-tag:<tag>= now, andgetExpirationreports the latest time so Next treats older entries as stale. Clicking Revalidate callsupdateTag('time-data'), which regenerates the remote entry. -
Building without Redis: both handlers skip connecting during
next build(they checkNEXT_PHASE) and degrade gracefully when Redis is unavailable, so the app still builds and runs, just without a shared cache. -
Redis server setup: ensure your Redis server is running before starting the app. Configure the connection with
REDIS_URL(defaults toredis://localhost:6379).
Note: This example fetches the current time from a public API (
timeapi.io) purely as sample data to demonstrate caching and revalidation. It is included for learning purposes only. If you reuse it, review and respect that API's terms of use and rate limits, and swap in your own data source for real applications.
Revalidation: paths, soft tags, and advisory parameters
The Revalidate button (revalidate-from.tsx) calls updateTag('time-data'), but that is only one entry point into the remote handler's tag machinery. Two related capabilities are handled without extra code, and two handler parameters are intentionally unused:
-
revalidatePathworks without extra code. Next.js derives implicit soft tags from the route path (prefixed_N_T_, e.g._N_T_/[timezone]) and routes path revalidation through the same tags asrevalidateTag. ArevalidatePath('/…')call therefore reachesupdateTags(['_N_T_/…']), and the next read callsgetExpiration(['_N_T_/…']). Because both methods operate generically over any tag string, path revalidation propagates across instances through Redis exactly like an explicit tag. -
get(cacheKey, softTags)ignoressoftTagsby design. A handler can honor soft tags one of two ways: implementgetExpirationto return the most recent revalidation timestamp (Next.js then performs the soft-tag staleness check itself), or returnInfinityfromgetExpirationand compare timestamps insideget. This handler does the former, so it never needs to read thesoftTagsargument. -
updateTags(tags, durations)ignoresdurationsby design.durations({ expire }) is only populated when a revalidation call carries acacheLifeprofile, such asrevalidateTag(tag, 'max'); it defers a tag's expiration into the future instead of expiring it immediately. This example performs only immediate revalidation, sodurationsis alwaysundefined— equivalent to recording the current timestamp, which is whatupdateTagsalready does.
See the Soft Tags section of the cacheHandlers documentation for the full tag architecture.
Documentation
For detailed information, see the official Next.js documentation:
cacheHandler(ISR / incremental cache) ↗cacheHandlers('use cache') ↗'use cache: remote'↗- Self-hosting: configuring caching ↗
Development and Production Considerations
-
The provided
compose.yamlis intended for local development. For production deployment, refer to the official Redis installation and management guidelines. -
Inspecting the cache: The
redis-stackimage bundles RedisInsight on port8001. Open it in your browser (linked from the example UI) to watch both thenextjs:cache:(ISR) andnextjs:use-cache:(remote) keys appear and expire. -
Clearing Redis Cache: To clear the Redis cache, use RedisInsight Workbench or the following CLI command:
docker exec -it cache-handler-redis redis-cli 127.0.0.1:6379> flushall OK