npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

payload-smart-cache

v1.3.0

Published

Payload Plugin for Cached Data

Readme

payload-smart-cache

Intelligent, dependency-aware cache invalidation for Next.js + Payload CMS applications.

npm version License: MIT

Overview

payload-smart-cache hooks into Payload's save and publish flow to provide automatic, dependency-aware cache invalidation. It builds a dependency graph from your collection and global relationships and walks it on every change, revalidating all affected Next.js cache tags — including indirectly related collections and globals.

Features

  • Dependency graph — automatically discovers relationships between collections, so changing a referenced document revalidates its dependents.
  • Tag-based revalidation — precise, granular cache invalidation via Next.js revalidateTag().
  • Versions-aware — for versioned collections, cache invalidation only fires on publish, not on draft saves.
  • Request caching utilitycreateRequestHandler wraps data-fetching functions with collection/global-level cache tags for automatic revalidation.

Installation

pnpm add payload-smart-cache

Usage

Important: smartCachePlugin scans collection and global fields at config time to auto-discover referenced collections. It must be listed after any plugin that registers collections or injects relationship fields, so those are visible during the scan.

// payload.config.ts
import { buildConfig } from "payload";
import { discussionsPlugin } from "payload-discussions";
import { smartCachePlugin } from "payload-smart-cache";

export default buildConfig({
  // ...
  plugins: [
    discussionsPlugin({ collections: ["posts"] }), // registers collections & injects fields
    smartCachePlugin({
      collections: ["pages", "posts"],
      globals: ["site-settings"],
    }), // must come after
  ],
});

Wrap your data-fetching functions with createRequestHandler so they are cached by collection/global tags and automatically revalidated when those collections or globals change:

import { createRequestHandler } from "payload-smart-cache";

const getPosts = createRequestHandler(
  async () => {
    const payload = await getPayload({ config });
    return payload.find({ collection: "posts" });
  },
  ["posts"], // collection/global slugs — revalidated when posts change
);

You can pass additional cache options as a third argument:

const getPosts = createRequestHandler(
  async () => {
    const payload = await getPayload({ config });
    return payload.find({ collection: "posts" });
  },
  ["posts"],
  { revalidate: 60 }, // also revalidate every 60 seconds
);

| Cache Option | Type | Default | Description | | ------------- | ------------------ | ------- | -------------------------------------------------------------------- | | tags | string[] | [] | Additional cache tags beyond the collection/global slugs. | | revalidate | number \| false | false | Time-based revalidation in seconds, or false for tag-based only. |

Options

| Option | Type | Default | Description | | --------------------- | ----------------------------------------------------------------------------- | ----------- | -------------------------------------------------------------------------------------------------------------------- | | collections | CollectionSlug[] | [] | Collections to track changes for. Referenced collections are auto-tracked. | | globals | GlobalSlug[] | [] | Globals to track changes for. Referenced collections are auto-tracked. | | disableAutoTracking | boolean | false | Disable automatic tracking of collections referenced via relationship/upload fields. | | onInvalidate | (change) => void \| Promise<void> | — | Called when cache invalidation fires for a registered collection ({ type: 'collection', slug, docID }) or global ({ type: 'global', slug }). | | tenantField | string | undefined | Name of the tenant relationship field. When set, cache invalidation is scoped per-tenant. Collections without this field use global invalidation. |

Multi-Tenant Support

For multi-tenant Payload applications using @payloadcms/plugin-multi-tenant (or a custom tenant field), set tenantField to scope cache invalidation per tenant. When a tenant's document changes, only that tenant's cached data is revalidated — other tenants' caches remain warm.

smartCachePlugin({
  collections: ["posts", "media", "members", "events"],
  tenantField: "tenant", // matches your multi-tenant plugin field name
})

Collections without the tenant field (e.g., shared content) are automatically detected and use global invalidation as before.

Tenant-scoped data fetching

Next.js 15+ (unstable_cache)

import { createTenantRequestHandler } from "payload-smart-cache";

const getPosts = createTenantRequestHandler(
  async (tenantId: string, slug: string) => {
    const payload = await getPayload({ config });
    return payload.find({
      collection: "posts",
      where: { slug: { equals: slug }, tenant: { equals: tenantId } },
    });
  },
  (tenantId) => ["posts", `posts:${tenantId}`],
);

// Usage
const posts = await getPosts(tenantId, "my-post");

Next.js 16+ ("use cache" directive)

import { tenantCacheTag } from "payload-smart-cache/cache";

async function getPosts(tenantId: string) {
  "use cache";
  tenantCacheTag("posts", tenantId);

  const payload = await getPayload({ config });
  return payload.find({
    collection: "posts",
    where: { tenant: { equals: tenantId } },
  });
}

tenantCacheTag requires cacheComponents: true in your next.config.

Contributing

This plugin lives in the payload-plugins monorepo.

Development

pnpm install

# watch this plugin for changes
pnpm --filter payload-smart-cache dev

# run the Payload dev app (in a second terminal)
pnpm --filter sandbox dev

The sandbox/ directory is a Next.js + Payload app that imports plugins via workspace:* — use it to test changes locally.

Code quality

  • Formatting & linting — handled by Biome, enforced on commit via husky + lint-staged.
  • Commits — must follow Conventional Commits with a valid scope (e.g. fix(payload-smart-cache): ...).
  • Changesets — please include a changeset in your PR by running pnpm release.

Issues & PRs

Bug reports and feature requests are welcome — open an issue.

License

MIT