Payload CMS Pluginpayload-cloudinary

Cloudinary storage for Payload CMS

A production‑ready Cloudinary adapter for Payload CMS uploads. Includes customizable public IDs, optional versioning with history, PDF thumbnails, Dynamic Folder Mode compatibility, and first‑class media document metadata.

Examples
/dev/null/payload.config.ts — L1-28
import { buildConfig } from 'payload/config';
import { cloudinaryStorage } from 'payload-cloudinary';

export default buildConfig({
  // ... your existing payload config
  plugins: [
    cloudinaryStorage({
      config: {
        cloud_name: process.env.CLOUDINARY_CLOUD_NAME!,
        api_key: process.env.CLOUDINARY_API_KEY!,
        api_secret: process.env.CLOUDINARY_API_SECRET!,
      },
      collections: {
        media: true, // Enable for your media collection
      },
      folder: 'payload-media', // Optional base folder in Cloudinary
    }),
  ],
});

Installation

Install the package using your preferred package manager:

Examples
/dev/null/shell.sh — L1-12
# With npm
npm install payload-cloudinary

# With yarn
yarn add payload-cloudinary

# With pnpm
pnpm add payload-cloudinary

# With bun
bun add payload-cloudinary

Basic Configuration

Add the plugin to your Payload config and enable it for your media collection:

Examples
/dev/null/payload.config.ts — L1-28
import { buildConfig } from 'payload/config';
import { cloudinaryStorage } from 'payload-cloudinary';

export default buildConfig({
  // ... your existing payload config
  plugins: [
    cloudinaryStorage({
      config: {
        cloud_name: process.env.CLOUDINARY_CLOUD_NAME!,
        api_key: process.env.CLOUDINARY_API_KEY!,
        api_secret: process.env.CLOUDINARY_API_SECRET!,
      },
      collections: {
        media: true, // Enable for your media collection
      },
      folder: 'payload-media', // Optional base folder in Cloudinary
    }),
  ],
});

Custom Fields

Extend your media collection with additional fields. The plugin will merge them into the collection config automatically.

Examples
/dev/null/custom-fields.ts — L1-30
cloudinaryStorage({
  // ... other options
  customFields: [
    {
      name: 'alt',
      type: 'text',
      label: 'Alt Text',
      admin: { description: 'Alternative text for accessibility' },
    },
    {
      name: 'caption',
      type: 'text',
      label: 'Caption',
    },
    {
      name: 'tags',
      type: 'array',
      label: 'Tags',
      fields: [{ name: 'tag', type: 'text', required: true }],
    },
  ],
});

Public ID Strategy

Control how Cloudinary public IDs are generated for predictable URLs and folder organization.

Examples
/dev/null/public-id.ts — L1-36
cloudinaryStorage({
  // ... other options
  publicID: {
    enabled: true,       // Enable custom public ID generation
    useFilename: true,   // Include original filename
    uniqueFilename: true,// Add uniqueness to avoid collisions
    generatePublicID: (filename, prefix, folder) => {
      const sanitizedName = filename
        .toLowerCase()
        .replace(/\.[^/.]+$/, "")
        .replace(/[^a-z0-9]/g, "-")
        .replace(/-+/g, "-")
        .replace(/^-|-$/g, "");
      const timestamp = new Date().toISOString().replace(/[^0-9]/g, "").slice(0, 14);
      const prefixPath = prefix ? `${prefix}/` : "";
      return `${folder}/${prefixPath}${sanitizedName}_${timestamp}`;
    },
  },
});

Versioning

Optionally enable versioning to store historical metadata and invalidate old CDN versions when new uploads replace existing files.

Examples
/dev/null/versioning.ts — L1-16
cloudinaryStorage({
  // ... other options
  versioning: {
    enabled: true,        // Track versions in DB
    autoInvalidate: true, // Invalidate old CDN versions
    storeHistory: true,   // Keep version history metadata
  },
});

Dynamic Folder Mode

Newer Cloudinary accounts use Dynamic Folder Mode, where the Media Library’s folders can differ from the public ID path. This plugin supports both modes.

Examples
/dev/null/dynamic-folder.ts — L1-14
cloudinaryStorage({
  // ... other options
  supportDynamicFolderMode: true, // Default: true. Matches newer Cloudinary accounts
  folder: 'payload-media', // Maps to asset_folder in Dynamic Folder Mode
});
  • Assets appear where you expect in the Media Library UI
  • Moving assets in the UI doesn’t break URLs
  • Public IDs remain stable and predictable

Frontend Usage

Access Cloudinary metadata (including public_id) directly from your media documents. Build responsive image URLs with Cloudinary transformations and use f_auto/q_auto for best performance.

Troubleshooting

  • Make sure your media collection slug matches the plugin config (e.g. media).
  • If custom fields don’t appear, restart the dev server and check for field name collisions.
  • Ensure your Cloudinary credentials are set and available at build/runtime.
  • For Dynamic Folder Mode, verify assets appear in expected folders in the UI.

API Options

Core

  • config: Cloudinary credentials
  • collections: enable per collection
  • folder: base folder path
  • enabled: turn plugin on/off
  • disableLocalStorage: defaults to true

Enhancements

  • customFields: add fields to media collection
  • publicID: customize public ID generation
  • versioning: track history and invalidate CDN
  • supportDynamicFolderMode: support Cloudinary’s dynamic folders
See the README for the full, up‑to‑date option matrix and detailed types.

Found a bug or have a feature request? Open an issue or PR on GitHub.