Search iThub

Search tools, categories and collections. Type at least two characters.

Explore all tools
↑ ↓ to explore resultsEnter to open · Esc to close

Payload Contact

Payload CMS 3 contact-form plugin with an admin inbox, runtime settings, honeypot and rate limiting

Visit website Save

MIT-licensed TypeScript plugin for Payload 3.x that adds a contact form component, a validated submit endpoint with honeypot and per-IP rate limit, a message inbox with statuses, a runtime settings global and optional SMTP notifications.

Pricing
Open source
Deployment
Self-hosted
Platforms
Web
2stars0forks
65views
Updated

What is Payload Contact?

Payload Contact (@navanem/payload-contact) is a contact-form plugin for Payload CMS 3.x. It adds a public form component, a submit endpoint with validation and spam protection, a collection that stores each message with a status, an admin inbox view and a settings global that changes the form behavior at runtime without a redeploy. Messages are stored in the Payload database and read in the Payload admin; the README states that no email address is exposed and that email delivery is optional.

The plugin is developed by Navanem, which describes it on its project page as an internal Navanem Lab project, and it comes from the same author as the Payload Comments plugin. The source is published in the navanem/payload-contact repository under the MIT license. The package declares payload ^3.90.0 and React 18 or 19 as peer dependencies and nodemailer as its only runtime dependency.

The README documents two installation models: vendoring the src folder into a project, so the admin components resolve through the generated import map, or installing the npm package. The npm route is described as available once the package is published; as of November 10, 2026, the npm registry lists no @navanem/payload-contact package, so the vendored model and the Git repository are the available distribution.

Release history: v0.1.0 (June 21, 2026) introduced the inbox collection, the settings global, the submit endpoint with honeypot and rate limit, the admin inbox view and the form component. v0.2.0 added opt-in SMTP notifications configured from the settings global and made that global admin-read only. v0.2.1 added a SQLite-backed development app, a Vitest suite, documentation guides and pnpm tooling, and fixed TypeScript build errors. The latest release, v0.2.2 (September 6, 2026), updates Payload, React, Vitest and TypeScript dependencies, restricts pnpm build approvals and adds CI, Dependabot, contribution, security and protected-branch configuration.

The repository security notes reference the Payload advisory GHSA-jg8r-5jh2-v2xj on account unlocking, which affects Payload through 3.88.0. The plugin adds no authentication collections; the notes state that applications using an affected Payload version must define restrictive access.unlock rules on every auth-enabled collection. The rate limiter is in memory and per server process. Related listings on iThub: Payload CMS itself and Payload Comments, the companion plugin from the same author.

Key capabilities

  • Message inbox collection The plugin registers a contact-messages collection (slug configurable with messagesSlug) with the fields name, email, subject, message and status. Status is new, read or replied. Signed-in admin users can read, update and delete messages; creating messages through the collection API is disabled, so new entries arrive only through the submit endpoint.
  • Public submit endpoint The form sends JSON to POST /api/contact-api/submit with name, email, optional subject, message and the honeypot field company, and receives { ok: true }. Validation errors return { ok: false, error } with status 400 or 403. The path is under /contact-api/* so it does not collide with the REST namespace of the messages collection.
  • Honeypot field The form component renders a hidden company field. A submission with a non-empty value is dropped silently, which gives automated senders no error signal.
  • Per-IP rate limit An in-memory sliding-window limiter allows 3 submissions per 60 seconds per client by default, configurable with rateLimit. The client IP is read from x-forwarded-for or x-real-ip and hashed with SHA-256 and a salt from ipSalt, CONTACT_IP_SALT or COMMENTS_IP_SALT. The limiter state lives in each server process.
  • Content rules Message length defaults to 10 to 5000 characters through minLength and maxLength. A subject can be made mandatory with requireSubject, and messages containing any of the blockedKeywords are dropped as spam.
  • Contact Settings global A global in the admin group Contact lets an administrator open or close the form, change length limits, require a subject, edit blocked keywords and set the success message, applied immediately without a redeploy. If the global has never been saved, the endpoint and page fall back to the plugin options.
  • Contact Inbox admin view A dedicated page at /admin/contact-inbox, added with a navigation link, shows counts for total, new, read and replied messages, period filters and a table of recent messages. The view returns nothing for requests without a signed-in user, so message data is not rendered into unauthenticated server output.
  • Drop-in form component <ContactForm /> is a client component exported from @navanem/payload-contact/client. Its props are successMessage, disabled (shows a closed notice instead of the form), endpoint and className.
  • Theming with CSS variables The component uses a CSS module driven by --pc-* custom properties with defaults, for example --pc-accent and --pc-radius. Overriding them on a wrapping element changes the form styling, as shown in the frontend integration guide.
  • Helper for custom forms Projects that build their own form can call readPublicContactSettings(payload), which returns whether the form is enabled and the success message, with defaults when the global is unsaved.
  • Optional SMTP notifications Since v0.2.0, the Email notifications section of Contact Settings enables an email on each new message, with recipient, SMTP host, port, user, password, optional implicit TLS on port 465 and a from address. An afterChange hook sends the email through nodemailer. Sending is fire-and-forget: failures are logged with payload.logger and never block the visitor's submission. The feature is off by default.
  • Admin-only credential storage The settings global is admin-read only. The public endpoint and pages read settings server-side with overrideAccess, so SMTP credentials are not sent to the browser, according to the inbox and triage guide.
  • Database migration The plugin adds the contact_messages table, with an enum_contact_messages_status enum and a foreign key in payload_locked_documents_rels, and the contact_settings table. The README creates them with payload migrate:create contact_plugin followed by payload migrate.
  • Disable switch Setting disabled: true turns the plugin into a no-op while still registering the collection and global, so the database schema stays stable. The enabled option controls whether new submissions are accepted.
  • Development app and tests The dev/ folder boots a SQLite-backed Payload admin with the plugin registered, using schema auto-push instead of migrations. A Vitest suite covers defaults, rate limiting and IP hashing, submission handling and settings resolution through a fake payload object, without a database. Development uses Node.js 22.12+ (22.x), 24.x or 26+, and pnpm 11.11.0.
Advertisements

Where Payload Contact fits

Adding a contact page to a Payload site

After registering contactPlugin() in payload.config.ts and running a migration, a page imports ContactForm from @navanem/payload-contact/client and renders it with a success message. Submissions are posted to /api/contact-api/submit and stored as new entries in the contact-messages collection. The configuration guide lists all plugin options and environment variables.

Triaging enquiries in the Payload admin

Editors open the Contact Messages collection to read a message and change its status from new to read or replied. The Contact Inbox page at /admin/contact-inbox summarizes counts per status, filters by period and lists recent messages. When the plugin is vendored, the inbox view and navigation link are registered in the generated importMap.js.

Opening and closing the form at runtime

The Contact Settings global switches submissions on or off and changes length limits, subject requirement, blocked keywords and the success message without a redeploy. The disabled prop of the form component renders a closed notice, and custom pages can call readPublicContactSettings to decide what to show.

Reducing automated submissions

Each submission passes the honeypot check, the per-IP sliding-window limit and the content rules. Requests that trip the honeypot or a blocked keyword are dropped silently. The default limit of 3 requests per minute per hashed IP can be changed with the rateLimit option, and the IP hash salt is set with CONTACT_IP_SALT.

Receiving email notifications for new messages

An administrator expands Email notifications in Contact Settings, enables sending on each new message and enters the recipient and SMTP details. The plugin sends through nodemailer from an afterChange hook, and every message is still stored in the inbox when sending fails.

Developing and testing the plugin locally

With pnpm installed, pnpm install --frozen-lockfile and pnpm dev start the SQLite-backed development admin from dev/. pnpm test runs the Vitest suite once and pnpm build compiles the plugin and copies its CSS assets into dist.

Reviews of Payload Contact

No reviews yet

Be the first to share your experience with Payload Contact.

Loading review form…

Alternatives to Payload Contact

Compare Payload Contact with these tools →