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

qwertyuiopkjdnjwennjkd

v0.1.5

Published

[![npm](https://img.shields.io/npm/v/@focus-reactive/payload-plugin-comments)](https://www.npmjs.com/package/@focus-reactive/payload-plugin-comments)

Readme

@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, @mentions with email notifications, comment resolution, multi-tenancy, and locale-aware filtering.

Table of Contents


Features

  • Document-level comments — Leave comments on any document in any collection
  • Field-level comments — Comment directly on individual fields; the field label shows a badge with the comment count
  • @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
  • 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

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({
      // All collections will get comments. You can specify title field for UI.
      collections: [
        {
          slug: "pages",
          titleField: "title",
        },
      ],
    }),
  ],
  // ... 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";

Configuration

The commentsPlugin factory accepts an optional CommentsPluginConfig object:

commentsPlugin(config?: CommentsPluginConfig)

CommentsPluginConfig

| Option | Type | Default | Description | | -------------- | --------------------- | --------------- | ---------------------------------------------------- | | collections | CollectionEntry[] | all collections | Collections whose documents support comments | | enabled | boolean | true | Set to false to disable the plugin entirely | | tenant | TenantPluginConfig | — | Multi-tenancy settings (see below) | | overrides | CollectionOverrides | — | Customize the generated comments collection | | translations | Translations | — | Override UI strings per locale (see below) |

CollectionEntry

Each entry in collections can be a plain slug string or an object:

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

Examples:

commentsPlugin({
  collections: [
    { slug: "pages", titleField: "title" }, // Uses "title" field as display name
    { slug: "products", titleField: "name" },
  ],
});

TenantPluginConfig

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

| Option | Type | Default | Description | | --------------------- | --------- | ----------- | ------------------------------------------------------------- | | enabled | boolean | false | Enable tenant scoping | | collectionSlug | string | "tenants" | Slug of the tenants collection | | documentTenantField | string | "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):

| Key | Default (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" | | noOpenComments | "No open comments" | | noResolvedComments | "No resolved comments" | | noMentionedComments | "No comments mentioning you" | | filterOpen | "Open" | | filterResolved | "Resolved" | | filterMentioned | "Mentioned me" |

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

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

const myTranslations: Translations = {
  fr: { label: "Commentaires" },
  de: { label: "Kommentare" },
};

commentsPlugin({ translations: myTranslations });

Environment Variables

Required for email notifications

| Variable | Description | Example | | ------------------- | ---------------------------------------------- | ------------------------- | | RESEND_API_KEY | Your Resend API key | re_xxxxxxxxxxxxxxxx | | RESEND_FROM_EMAIL | Sender 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 configured collection to inject FieldCommentLabel into each field's admin label — this shows a comment count badge next to each field.
  4. It registers two admin providers (CommentsProviderWrapper, GlobalCommentsLoader) and one admin action (CommentsHeaderButton).

Data loading (GlobalCommentsLoader):

  • This server component runs on every admin page load.
  • It fetches all comments, document titles, mentionable users, field labels, and collection labels in parallel.
  • Results are passed to GlobalCommentsHydrator (a client component) which hydrates the CommentsContext.

State management (CommentsProvider):

  • Holds allComments in React state with optimistic updates via useOptimistic.
  • visibleComments is derived: filtered to the current document/collection/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.
  • 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:

| Field | Type | Description | | ---------------- | ----------------------- | ------------------------------------------------------ | | documentId | number | ID of the document being commented on | | collectionSlug | text | Slug of the collection | | fieldPath | text | Dot-notation path of the field (null = document-level) | | locale | text | Locale of the comment (null = shown in all locales) | | text | textarea | Comment body (may contain @(userId) mention tokens) | | mentions | array → relationship | Users mentioned in this comment | | author | relationship → users | Comment author (set automatically) | | isResolved | checkbox | Whether the comment is resolved | | resolvedBy | relationship → users | Who resolved it | | resolvedAt | date | When it was resolved | | tenant | relationship (optional) | Tenant scope (when multi-tenancy is enabled) |


Available Scripts

Run these from the project root with pnpm:

| Command | Description | | ------------------- | -------------------------------------------------------------- | | pnpm build | Build the plugin to dist/ (tsup + Tailwind CSS minification) | | pnpm dev | Build in watch mode — rebuilds on file changes | | pnpm lint | Run ESLint on src/ | | pnpm lint:fix | Run ESLint with auto-fix | | pnpm format | Format src/ with Prettier | | pnpm format:check | Check 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