@focus-reactive/payload-plugin-comments

中文文档
1.11.3发布于 2026/10/12MITTypeScriptES Module构建来源已验证近 30 天下载 4.8万

npm

原始使用文档
文档目录30 节
  1. Table of Contents
  2. AI Integration Prompt
  3. UI Screenshots
  4. Global Comments Panel
  5. Document Comments Panel
  6. Field Comment Popup
  7. Field Label Button — Two States
  8. Features
  9. Prerequisites
  10. Installation
  11. Setup
  12. 1. Add the plugin to your Payload config
  13. 2. Import the styles
  14. 3. Regenerate the import map
  15. 4. Create and run a migration
  16. Configuration
  17. CommentsPluginConfig
  18. CollectionEntry
  19. usernameFieldPath
  20. TenantPluginConfig
  21. Collection Overrides
  22. Translations
  23. Environment Variables
  24. Required for email notifications
  25. Architecture Overview
  26. How It Works
  27. Exports Reference
  28. Available Scripts
  29. Contributing
  30. License

@focus-reactive/payload-plugin-comments

npm

A collaborative commenting plugin for Payload CMS v3. Adds a full-featured comments system to the Payload admin panel — supporting document-level and field-level comments on both collections and globals, @mentions with email notifications, comment resolution, multi-tenancy, and locale-aware filtering.

Table of Contents


AI Integration Prompt

Copy and paste this prompt into your AI assistant (Cursor, Claude, etc.) to integrate the plugin into an existing Payload v3 project.

I want to add a collaborative commenting system to my Payload CMS v3 project using @focus-reactive/payload-plugin-comments.

## How it works

The plugin injects into every collection and global:
- A field-level comment badge on every field label — shows comment count, opens a popup to post
- A document-level comments drawer (sidebar) scoped to the current document
- A global comments panel (header button) listing all comments across every document and global

Comments are stored in an auto-generated `comments` collection (hidden from the sidebar). Each comment records:
- documentId / collectionSlug / globalSlug — what it belongs to
- fieldPath — dot-notation field path (null = document-level)
- locale — for field-level comments (null = shown in all locales)
- text — may contain @(userId) mention tokens
- author, mentions, isResolved, resolvedBy, resolvedAt
- tenant (optional, when multi-tenancy is enabled)

@mentions use autocomplete from the users collection. Mentioned users receive email notifications via Resend.
Comments are automatically deleted when their parent document is deleted.

## Installation

pnpm add @focus-reactive/payload-plugin-comments

## Step 1 — Register the plugin in payload.config.ts

import { buildConfig } from 'payload'
import { commentsPlugin } from '@focus-reactive/payload-plugin-comments'

export default buildConfig({
  plugins: [
    commentsPlugin({
      // Optional: specify which field to use as document title in the UI
      collections: [
        { slug: 'pages', titleField: 'title' },
        { slug: 'products', titleField: 'name' },
      ],
      // Optional: customize the display name field on users
      usernameFieldPath: 'name', // default
    }),
  ],
})

## Step 2 — Import the stylesheet

In your global CSS or admin layout:

@import "@focus-reactive/payload-plugin-comments/styles.css";

Or in a TypeScript/JS file:

import "@focus-reactive/payload-plugin-comments/styles.css";

## Step 3 — Regenerate the import map

Run this after adding the plugin so Payload registers the plugin's admin components:

npx payload generate:importmap

(Or the equivalent for your package manager: pnpm payload generate:importmap / bunx payload generate:importmap)
If you skip this step the comment badges, drawer, and header button will not appear in the admin UI.

## Step 4 — Create and run a migration (SQL adapters only)

The plugin adds a `comments` collection to your database. If you use PostgreSQL or SQLite, create and apply a migration:

npx payload migrate:create create_comments
npx payload migrate

Skip this step if you use the MongoDB adapter.

## Optional: Multi-tenancy

commentsPlugin({
  tenant: {
    enabled: true,
    collectionSlug: 'tenants',       // default
    documentTenantField: 'tenant',   // default
  },
})

## Optional: Collection overrides

commentsPlugin({
  overrides: {
    access: { /* custom access control */ },
    fields: (defaultFields) => [...defaultFields],
    hooks: {
      afterChange: [async ({ doc }) => { /* ... */ }],
    },
  },
})

## Environment variables (for email notifications)

RESEND_API_KEY=re_xxxxxxxxxxxxxxxx
[email protected]

## Important notes

- All collections and globals automatically get field-level comments — `collections` config only sets UI metadata (titleField).
- Comments on globals use globalSlug instead of collectionSlug/documentId.
- Field-level comments are locale-scoped; document-level comments show in all locales.
- The `comments` collection is hidden from the admin sidebar by default.
- Auto-cleanup: when a document is deleted, all its comments are deleted too.
- usernameFieldPath supports dot-notation (e.g. "profile.displayName").
- If RESEND_FROM_EMAIL is not set, mention emails are silently skipped.

UI Screenshots

Global Comments Panel

A header button in the Payload admin opens a drawer listing all comments across every document and collection.

Global comments panel

Document Comments Panel

When viewing a document, a side panel shows all comments scoped to that document with filter tabs (Open / Resolved / Mentioned me).

Document comments panel

Field Comment Popup

Clicking the comment badge on a field label opens a popup where you can write and post a new comment for that specific field.

Field comment popup

Field Label Button — Two States

The comment button embedded in the field label has two visual states: no comments (inactive, appears on hover) and one or more open comments (active, showing the count badge).

Inactive (no comments)Active (has comments)
Field label button — inactiveField label button — active

Features

  • Document-level comments — Leave comments on any document in any collection or global
  • Field-level comments — Comment directly on individual fields; the field label shows a badge with the comment count
  • Global support — Field-level and document-level comments work on Payload Globals, not just collections
  • @mention users — Mention other users in comments using @name autocomplete
  • Email notifications — Mentioned users receive email notifications via Resend
  • Resolve comments — Mark comments as resolved/unresolved; filter by open, resolved, or mentioned
  • Global comments panel — A header button opens a drawer showing all comments across all documents and globals
  • Optimistic UI — Comments appear instantly before the server confirms
  • Multi-tenancy — Scope comments to tenants via @payloadcms/plugin-multi-tenant
  • Locale-aware — Field-level comments are tied to a locale; document-level comments are shown in all locales
  • Auto-cleanup — Comments are automatically deleted when their parent document is deleted
  • i18n / Translations — All UI strings are translatable; ship your own locale overrides alongside the built-in English defaults

Prerequisites

  • Node.js 20 or higher
  • A working Payload CMS v3 project with Next.js
  • pnpm (recommended)
  • A Resend account (required only for mention email notifications)

Installation

pnpm add @focus-reactive/payload-plugin-comments
npm install @focus-reactive/payload-plugin-comments
# or
yarn add @focus-reactive/payload-plugin-comments

Peer dependencies: payload ^3.0.0, @payloadcms/ui ^3.0.0. next ^14 || ^15, react ^18 || ^19, react-dom, and @payloadcms/plugin-multi-tenant are optional.


Setup

1. Add the plugin to your Payload config

// payload.config.ts
import { buildConfig } from "payload";
import { commentsPlugin } from "@focus-reactive/payload-plugin-comments";

export default buildConfig({
  plugins: [
    commentsPlugin({
      // Optional: customize which field is used as the document title in the UI
      collections: [
        { slug: "pages", titleField: "title" },
        { slug: "products", titleField: "name" },
      ],
    }),
  ],
  // ... rest of your config
});

2. Import the styles

Add the plugin's stylesheet to your global CSS or admin layout:

/* global CSS */
@import "@focus-reactive/payload-plugin-comments/styles.css";

Or import it in a layout/page file:

import "@focus-reactive/payload-plugin-comments/styles.css";

3. Regenerate the import map

Payload needs its import map regenerated whenever you add or remove a plugin that registers admin components. Run:

npx payload generate:importmap

This command works regardless of your package manager (npm, pnpm, yarn, bun). Alternatively use the equivalent for your package manager:

pnpm payload generate:importmap
# yarn payload generate:importmap
# bunx payload generate:importmap

If you skip this step the plugin's admin components (comment badges, drawer, header button) will not appear in the Payload admin UI.

4. Create and run a migration

The plugin adds a comments collection to your database. If you use a SQL adapter (PostgreSQL, SQLite), create and apply a migration:

npx payload migrate:create create_comments
npx payload migrate

Skip this step if you use the MongoDB adapter — Payload creates collections automatically.


Configuration

The commentsPlugin factory accepts an optional CommentsPluginConfig object:

commentsPlugin(config?: CommentsPluginConfig)

CommentsPluginConfig

OptionTypeDefaultDescription
collectionsCollectionEntry[]—UI metadata for collections (title field). All collections get comments regardless.
enabledbooleantrueSet to false to disable the plugin entirely
tenantTenantPluginConfig—Multi-tenancy settings (see below)
overridesCollectionOverrides—Customize the generated comments collection
translationsTranslations—Override UI strings per locale (see below)
usernameFieldPathstring"name"Dot-notation path to the display name field on the users collection

CollectionEntry

Each entry in collections is an object that provides UI metadata for a specific collection:

interface CollectionEntry {
  slug: string;
  titleField?: string; // Field used as document title in the UI. Default: "id"
}

Note: You do not need to list every collection. All collections (and all globals) automatically support comments. The collections array only sets display metadata like the title field.

Examples:

commentsPlugin({
  collections: [
    { slug: "pages", titleField: "title" },
    { slug: "products", titleField: "name" },
  ],
});

usernameFieldPath

Specifies which field on the users collection is used as the display name in comment UI and @mention autocomplete. Supports dot-notation for nested fields.

commentsPlugin({
  usernameFieldPath: "name", // default
  // usernameFieldPath: "firstName",
  // usernameFieldPath: "profile.displayName",
});

TenantPluginConfig

Configure multi-tenancy when using @payloadcms/plugin-multi-tenant:

OptionTypeDefaultDescription
enabledbooleanfalseEnable tenant scoping
collectionSlugstring"tenants"Slug of the tenants collection
documentTenantFieldstring"tenant"Field on document collections that holds the tenant reference

Example:

commentsPlugin({
  tenant: {
    enabled: true,
    collectionSlug: "tenants",
    documentTenantField: "tenant",
  },
});

Collection Overrides

Use overrides to customize the generated comments collection — for example, to extend access control or add custom fields:

commentsPlugin({
  overrides: {
    access: {
      // Override the default "authenticated only" access
    },
    fields: (defaultFields) => [...defaultFields],
    hooks: {
      afterChange: [
        async ({ doc }) => {
          console.log("Comment changed:", doc.id);
        },
      ],
    },
  },
});

Translations

Use translations to override any UI string for one or more locales. Each key is a locale code; the value is a partial object of the CommentsTranslations shape — keys you omit fall back to the built-in English defaults.

commentsPlugin({
  translations: {
    fr: {
      label: "Commentaires",
      add: "Ajouter un commentaire",
      writeComment: "Écrire un commentaire",
      comment: "Commenter",
      cancel: "Annuler",
      resolve: "Résoudre",
      reopen: "Rouvrir",
      delete: "Supprimer",
      filterOpen: "Ouverts",
      filterResolved: "Résolus",
      filterMentioned: "Me mentionnent",
    },
  },
});

All translatable keys (with their English defaults):

KeyDefault (English)
label"Comments"
openComments_one"{{count}} open comment"
openComments_other"{{count}} open comments"
add"Add comment"
writeComment"Write a comment"
comment"Comment"
cancel"Cancel"
posting"Posting…"
resolve"Resolve"
reopen"Reopen"
delete"Delete"
general"General"
close"Close"
syncingComments"Syncing comments"
openCommentsAria"Open comments"
failedToPost"Failed to post comment"
failedToUpdate"Failed to update comment"
failedToDelete"Failed to delete comment"
failedToAdd"Failed to add comment"
unknownAuthor"Unknown"
deletedUser"Deleted user"
noOpenComments"No open comments"
noResolvedComments"No resolved comments"
noMentionedComments"No comments mentioning you"
filterOpen"Open"
filterResolved"Resolved"
filterMentioned"Mentioned me"
noMentionMatches"No matches"

The CommentsTranslations type is exported from the package so you can type your translation objects:

import type { CommentsTranslations } from "@focus-reactive/payload-plugin-comments";

const myTranslations: Record<string, Partial<CommentsTranslations>> = {
  fr: { label: "Commentaires" },
  de: { label: "Kommentare" },
};

commentsPlugin({ translations: myTranslations });

Environment Variables

Required for email notifications

VariableDescriptionExample
RESEND_API_KEYYour Resend API keyre_xxxxxxxxxxxxxxxx
RESEND_FROM_EMAILSender email address for mention notifications[email protected]

If RESEND_FROM_EMAIL is not set, mention email notifications are silently skipped and an error is logged to the console.

.env.local example:

RESEND_API_KEY=re_xxxxxxxxxxxxxxxx
[email protected]

Architecture Overview

How It Works

Plugin initialization (plugin.ts):

  1. The plugin receives a CommentsPluginConfig and returns a standard Payload Plugin function.
  2. It creates a comments collection (hidden from the admin sidebar by default).
  3. It patches every collection to inject FieldCommentLabel into each field's admin label and registers an afterDelete hook that cascade-deletes comments when a document is removed.
  4. It patches every global to inject FieldCommentLabel into each field's admin label.
  5. It registers an admin provider (CommentsProviderWrapper) and one admin action (CommentsHeaderButton).
  6. It registers REST endpoints under /api/comments-plugin/* (list, create, delete, resolve, mark-read, unread-mentions-count, mentionable-users, document-titles, field-labels). Each one requires an authenticated user and uses the Payload instance from the request, so it works on a freshly started serverless instance.

Data loading (src/api):

  • The admin UI fetches comments, document titles, mentionable users, field labels, and unread mention counts with TanStack Query from the plugin's endpoints.
  • Mutations (add, delete, resolve, mark as read) call the same endpoints and update the query cache optimistically.

State management (CommentsProvider):

  • Holds allComments in React state with optimistic updates via useOptimistic.
  • visibleComments is derived: filtered to the current document/collection/global/locale based on the Next.js pathname.
  • Exposes addComment, removeComment, resolveComment, and syncComments mutations.

Field-level comments (FieldCommentLabel):

  • The plugin overrides the Label component for every named field in every configured collection and global.
  • The label reads comments from context and filters by field path, showing a badge with the count.
  • Clicking the badge opens the comments drawer pre-scrolled to that field's comment group.

Comments collection schema:

FieldTypeDescription
documentIdnumberID of the document being commented on (null for globals)
collectionSlugtextSlug of the collection (null for global comments)
globalSlugtextSlug of the Payload global (null for collection document comments)
fieldPathtextDot-notation path of the field (null = document-level)
localetextLocale of the comment (null = shown in all locales)
texttextareaComment body (may contain @(userId) mention tokens)
mentionsarray → relationshipUsers mentioned in this comment
authorrelationship → usersComment author (set automatically)
isResolvedcheckboxWhether the comment is resolved
resolvedByrelationship → usersWho resolved it
resolvedAtdateWhen it was resolved
tenantrelationship (optional)Tenant scope (when multi-tenancy is enabled)

Exports Reference

Import pathExports
@focus-reactive/payload-plugin-commentscommentsPlugin, CommentsPluginConfig (type), CommentsTranslations (type)
@focus-reactive/payload-plugin-comments/styles.cssPlugin stylesheet (Tailwind-compiled CSS)

Available Scripts

Run these from the project root with pnpm:

CommandDescription
pnpm buildBuild the plugin to dist/ (tsup + Tailwind CSS minification)
pnpm devBuild in watch mode — rebuilds on file changes
pnpm lintLint + check formatting (Ultracite: oxlint + oxfmt)
pnpm lint:fixLint + fix, including formatting (Ultracite)
pnpm formatFormat files (Ultracite: oxfmt)
pnpm format:checkCheck formatting without writing

Contributing

  1. Fork the repository and create a feature branch.
  2. Install dependencies: pnpm install
  3. Start the build watcher: pnpm dev
  4. Make your changes in src/.
  5. Run pnpm lint and pnpm format:check before submitting.
  6. Open a pull request against main.

License

MIT © Focus Reactive

版本与发布记录

@focus-reactive/payload-plugin-comments 共发布 22 个版本,首次发布于 2026/03/18;过去 12 个月发布了 22 个版本。最新版本附带 npm provenance 构建来源证明,可追溯到公开的 CI 构建记录。

版本发布日期距今
1.11.3latest2026/10/121 个月前
1.11.22026/10/061 个月前
1.11.12026/07/054 个月前
1.11.02026/06/294 个月前
1.10.02026/06/194 个月前
1.9.02026/06/184 个月前

关于 @focus-reactive/payload-plugin-comments 的常见问题

如何安装 @focus-reactive/payload-plugin-comments?

在项目目录运行 npm install @focus-reactive/payload-plugin-comments,或使用 pnpm add / yarn add / bun add。它还需要宿主项目提供 peer 依赖:payload(^3.0.0)、@payloadcms/ui(^3.0.0)。npm 7 及以上与 pnpm 8 及以上默认会自动安装必需的 peer 依赖,Yarn 需要手动安装。

@focus-reactive/payload-plugin-comments 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@focus-reactive/payload-plugin-comments 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

@focus-reactive/payload-plugin-comments 可以商用吗?

@focus-reactive/payload-plugin-comments 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@focus-reactive/payload-plugin-comments 的下载量有多少?

根据 npm 官方下载统计,@focus-reactive/payload-plugin-comments 在 2026/10/15 – 2026/11/13 这 30 天内被下载 47,701 次,日均约 1,590 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@focus-reactive/payload-plugin-comments 还在维护吗?

最新版本 1.11.3 发布于 2026/10/12(1 个月前);至今共发布 22 个版本,其中过去 12 个月发布了 22 个;首次发布于 2026/03/18。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。