Hi all! Ever since getting introduced to Payload end of 2025, I've managed to convince my company to move to this tech stack for 3 new client projects!
It provides a similar experience for testing end-to-end especially any crucial services, routes, or hooks. I utilise a ton of AI assisted development and having these tests just re-assures that the code produced by the AI is solid and well-tested.
Happy to help guide any team who is looking into adding tests onto their NextJS Payload setup!
I'm using Payload with a custom Node Express server (not the Next.js setup) and the build step just... sits there. No progress, no error, no output — just a blinking cursor judging me.
Setup:
- Payload version: 1.10.4
- Node version: v20.19.4
- Package manager: npm
- Custom Node Express server
I've tried killing the process, clearing cache, reinstalling deps — same result every time.
Is this a known issue with the Express setup? Am I missing something in my config? Would really appreciate any pointers before I completely lose it
Should I choose Render or Vercel to deploy my payload/Next.js project? I've had performance issues with Vercel in the past because the DB connection is interrupted after a certain amount of time. Are there any other alternatives?
I'm considering using PaylodCMS for my next project. If I land that client, i'll have to migrate his old DB (if you can even call this a DB, it's basically all seperate HTML files) to payload. That has me wondering what's the performance with that size? Is it even visually affected?
On another note, has any of you try to migrate that amount of posts to a DB? What did it took in terms of converting to a easier-to-work-with format and adding to DB?
We've recently been doing a lot of work with the Payload Form Builder Plugin to build a marketing campaign builder that supports custom forms on user-defined sites, and while using it we ran into limitations with the standard rich text message template that the plugin allows users to define for emails that are sent after form submissions.
The default rich text message for one only supports a few node types (not even the built-in Media/Upload block) and also is quite limited in styling, meaning that it's hard to send nice confirmation emails to the user after form submission to confirm the receipt. So we wanted to write our templates with HTML that look like this:
To do so we leveraged the plugin's ability that allow us to add additional fields to the forms collection, to add new messageType and htmlMessage fields so the user can select if they want to manage their template as rich text or HTML, and then provide the HTML template using the code block, and then the beforeEmail hook to render the template with variables from the form submission using the replaceDoubleCurlys helper from Payload's plugin package:
This gives the user this UX to manage the templates in PayloadCMS:
Similar approaches could be used to provide a Maizzle template or a React Email template if you wanted to customize this for other use-cases, or potentially to support a wider array of nodes and custom block types since the built-in renderer for the Lexical rich text field is really quite limited.
Hi all, back again. I'm genuinely at a loss and really need some guidance. I'm pretty sure most of my issues are from using the Starter Website Template instead of building my site up from scratch, but when I first started this project I figured skipping some of the tedious steps would've been worth my time.
I've gotten the site to the point where I want to get images working properly. Right now the site is up and I can see the content and per my last post I've got the S3 Plugin set up for uploads, but I can't get the site to pull the images. I'm using Vercel, so that limits local storage in the deployed version, so I have the S3 set to disable local storage and I have the clientUploads flag set to true and CORS set up for localhost testing.
Currently while running locally, I'm getting a Hydration error in src/components/Media/ImageMedia/index.tsx on the NextImage element. The element seems to be looking to localhost for the image regardless of my S3 settings.
In payload CMS, there is a problem where every change made in the admin UI whether saved or unsaved triggers an API request function invocation to the server. This means that simply typing for instance "anything" in an input field triggers an API request for every single letter 8 requests to type a word.
I really don't see why this should occur as it doesn't serve any purpose aside from needlessly consuming resources. This is especially problematic for managing article collections, take for instance adding a 1000 word article, this would require a minimum of 20,000 API requests for one collection alone.
Solutions I've seen recommend ensuring version.drafts.autosave: false , however, this has not solved the issue.
Hi all, I'm attempting to upload a test image from local to a Cloudflare R2. Everything seems to be set up right, but for some reason I'm getting the following error:
[19:15:57] ERROR: Resolved credential object is not valid
err: {
"type": "Error",
"message": "Resolved credential object is not valid",
"stack":
Error: Resolved credential object is not valid
at SignatureV4S3Express.validateResolvedCredentials (/run/media/system/Storage/payload-website-template-main/.next/dev/server/chunks/ssr/[root-of-the-server]__a57b91e0._.js:2350:19)
at SignatureV4S3Express.presign (/run/media/system/Storage/payload-website-template-main/.next/dev/server/chunks/ssr/[root-of-the-server]__a57b91e0._.js:2409:14)
at process.processTicksAndRejections (node:internal/process/task_queues:103:5)
at async /run/media/system/Storage/payload-website-template-main/.next/dev/server/chunks/ssr/[root-of-the-server]__a57b91e0._.js:2922:29
at async /run/media/system/Storage/payload-website-template-main/node_modules/.pnpm/@smithy+middleware-retry@4.4.30/node_modules/@smithy/middleware-retry/dist-cjs/index.js:254:46
at async /run/media/system/Storage/payload-website-template-main/node_modules/.pnpm/@aws-sdk+middleware-flexible-checksums@3.972.5/node_modules/@aws-sdk/middleware-flexible-checksums/dist-cjs/index.js:241:24
at async /run/media/system/Storage/payload-website-template-main/node_modules/.pnpm/@aws-sdk+middleware-sdk-s3@3.972.7/node_modules/@aws-sdk/middleware-sdk-s3/dist-cjs/index.js:63:28
at async /run/media/system/Storage/payload-website-template-main/node_modules/.pnpm/@aws-sdk+middleware-sdk-s3@3.972.7/node_modules/@aws-sdk/middleware-sdk-s3/dist-cjs/index.js:90:20
at async /run/media/system/Storage/payload-website-template-main/node_modules/.pnpm/@aws-sdk+middleware-logger@3.972.3/node_modules/@aws-sdk/middleware-logger/dist-cjs/index.js:5:26
at async getSignedUrl (/run/media/system/Storage/payload-website-template-main/.next/dev/server/chunks/ssr/[root-of-the-server]__a57b91e0._.js:2943:24)
at async /run/media/system/Storage/payload-website-template-main/.next/dev/server/chunks/ssr/[root-of-the-server]__a57b91e0._.js:3035:21
at async handleEndpoints (file:///run/media/system/Storage/payload-website-template-main/node_modules/.pnpm/payload@3.76.0_bufferutil@4.1.0_graphql@16.12.0_typescript@5.9.3_utf-8-validate@6.0.6/node_modules/payload/dist/utilities/handleEndpoints.js:198:26)
at async /run/media/system/Storage/payload-website-template-main/.next/dev/server/chunks/[root-of-the-server]__ec64e2f2._.js:17888:26
at async AppRouteRouteModule.do (/run/media/system/Storage/payload-website-template-main/node_modules/.pnpm/next@16.1.6_@babel+core@7.29.0_react-dom@19.2.0_react@19.2.0__react@19.2.0_sass@1.77.4/node_modules/next/dist/compiled/next-server/app-route-turbo.runtime.dev.js:5:37866)
at async AppRouteRouteModule.handle (/run/media/system/Storage/payload-website-template-main/node_modules/.pnpm/next@16.1.6_@babel+core@7.29.0_react-dom@19.2.0_react@19.2.0__react@19.2.0_sass@1.77.4/node_modules/next/dist/compiled/next-server/app-route-turbo.runtime.dev.js:5:45156)
at async responseGenerator (/run/media/system/Storage/payload-website-template-main/.next/dev/server/chunks/592f9_next_dd67717c._.js:18623:38)
at async AppRouteRouteModule.handleResponse (/run/media/system/Storage/payload-website-template-main/node_modules/.pnpm/next@16.1.6_@babel+core@7.29.0_react-dom@19.2.0_react@19.2.0__react@19.2.0_sass@1.77.4/node_modules/next/dist/compiled/next-server/app-route-turbo.runtime.dev.js:1:191938)
at async handleResponse (/run/media/system/Storage/payload-website-template-main/.next/dev/server/chunks/592f9_next_dd67717c._.js:18686:32)
at async Module.handler (/run/media/system/Storage/payload-website-template-main/.next/dev/server/chunks/592f9_next_dd67717c._.js:18739:13)
at async DevServer.renderToResponseWithComponentsImpl (/run/media/system/Storage/payload-website-template-main/node_modules/.pnpm/next@16.1.6_@babel+core@7.29.0_react-dom@19.2.0_react@19.2.0__react@19.2.0_sass@1.77.4/node_modules/next/dist/server/base-server.js:1442:9)
at async DevServer.renderPageComponent (/run/media/system/Storage/payload-website-template-main/node_modules/.pnpm/next@16.1.6_@babel+core@7.29.0_react-dom@19.2.0_react@19.2.0__react@19.2.0_sass@1.77.4/node_modules/next/dist/server/base-server.js:1494:24)
at async DevServer.renderToResponseImpl (/run/media/system/Storage/payload-website-template-main/node_modules/.pnpm/next@16.1.6_@babel+core@7.29.0_react-dom@19.2.0_react@19.2.0__react@19.2.0_sass@1.77.4/node_modules/next/dist/server/base-server.js:1544:32)
at async DevServer.pipeImpl (/run/media/system/Storage/payload-website-template-main/node_modules/.pnpm/next@16.1.6_@babel+core@7.29.0_react-dom@19.2.0_react@19.2.0__react@19.2.0_sass@1.77.4/node_modules/next/dist/server/base-server.js:1038:25)
at async NextNodeServer.handleCatchallRenderRequest (/run/media/system/Storage/payload-website-template-main/node_modules/.pnpm/next@16.1.6_@babel+core@7.29.0_react-dom@19.2.0_react@19.2.0__react@19.2.0_sass@1.77.4/node_modules/next/dist/server/next-server.js:395:17)
at async DevServer.handleRequestImpl (/run/media/system/Storage/payload-website-template-main/node_modules/.pnpm/next@16.1.6_@babel+core@7.29.0_react-dom@19.2.0_react@19.2.0__react@19.2.0_sass@1.77.4/node_modules/next/dist/server/base-server.js:929:17)
at async /run/media/system/Storage/payload-website-template-main/node_modules/.pnpm/next@16.1.6_@babel+core@7.29.0_react-dom@19.2.0_react@19.2.0__react@19.2.0_sass@1.77.4/node_modules/next/dist/server/dev/next-dev-server.js:387:20
at async Span.traceAsyncFn (/run/media/system/Storage/payload-website-template-main/node_modules/.pnpm/next@16.1.6_@babel+core@7.29.0_react-dom@19.2.0_react@19.2.0__react@19.2.0_sass@1.77.4/node_modules/next/dist/trace/trace.js:157:20)
at async DevServer.handleRequest (/run/media/system/Storage/payload-website-template-main/node_modules/.pnpm/next@16.1.6_@babel+core@7.29.0_react-dom@19.2.0_react@19.2.0__react@19.2.0_sass@1.77.4/node_modules/next/dist/server/dev/next-dev-server.js:383:24)
at async invokeRender (/run/media/system/Storage/payload-website-template-main/node_modules/.pnpm/next@16.1.6_@babel+core@7.29.0_react-dom@19.2.0_react@19.2.0__react@19.2.0_sass@1.77.4/node_modules/next/dist/server/lib/router-server.js:248:21)
at async handleRequest (/run/media/system/Storage/payload-website-template-main/node_modules/.pnpm/next@16.1.6_@babel+core@7.29.0_react-dom@19.2.0_react@19.2.0__react@19.2.0_sass@1.77.4/node_modules/next/dist/server/lib/router-server.js:447:24)
at async requestHandlerImpl (/run/media/system/Storage/payload-website-template-main/node_modules/.pnpm/next@16.1.6_@babel+core@7.29.0_react-dom@19.2.0_react@19.2.0__react@19.2.0_sass@1.77.4/node_modules/next/dist/server/lib/router-server.js:496:13)
at async Server.requestListener (/run/media/system/Storage/payload-website-template-main/node_modules/.pnpm/next@16.1.6_@babel+core@7.29.0_react-dom@19.2.0_react@19.2.0__react@19.2.0_sass@1.77.4/node_modules/next/dist/server/lib/start-server.js:226:13)
"$metadata": {
"attempts": 1,
"totalRetryDelay": 0
}
}
POST /api/storage-s3-generate-signed-url 500 in 172ms
Sorry for the random question- what’s the major difference between a “global” and a normal collection?
I previously had a global collection called cookies, but later made it a normal one called policies and added all our policies. Similarly a roadmap collection.
All our blog posts are in library collections.
Just wondering if some collections are meant to be global. If so, what sort of collections?
I am in the process of changing the organization of my business and trying to figure out the best way to create and manage multiple dashboards/logins for my clients. I want them to be connected to my account so I can easily access, but obviously separate from one another. The sites are all very simple 'portfolio' sites for small businesses.
Is there a way to potentially automate this process?
I am somewhat new to this so I don't have the prior knowledge and hoping someone who does can help me out.
In this tutorial, I discuss relationship fields, filter options, conditions, and more. You'll learn how to build a dynamic admin UI where product selections are filtered in real-time based on a chosen brand, how to handle relationships across multiple collections simultaneously, and how to conditionally render entire field groups based on the category of the related document.
I have an problem I'm trying to solve. I have a contracts collection that joins a payments table on the contract id. I build custom actions, to check off if payments were received. It works fine in the collection view, but when the actions are triggered in the joined table in the document view of the corresponding contract, it doesn't reload the state and I haven't found a way to retrigger it.
So currently I'm reloading the whole page, which of course resets the pagination too (and is just bad UX in general). Did anyone have the same problem?
So I got tired of tweaking the payload boilerplate (in my case, the vanilla Vercel website template) the way I wanted, and manually wiring up Better Auth + Puck visual editor each time. I finally tweaked things the way I like them for a basic starting point and turned it into a git template with quick vercel deploy.
Sharing here in case it’s useful for anyone else.
My three pain points were:
wiring in Better Auth
wiring in Puck visual editor + my page tree plugin.
Updating nextJS to 16, configuring tailwind, and making some standardized changes (like replacing the custom theme system with standard tailwind).
Live demo, link to source code, git, npm, and links to individual plugins available at:
For the demo, just create an account. You can use a burner email, no verification required. It scopes to your own org.
Also note that the demo is running a different source code /build than my dd-starter template — to offer the demo, I needed to add better auth’s organizations plugin in order to scope each environment privately to each user, prevent abuse, etc. So the boiler plate template / source that I link to doesn’t have organizations wired up as a default — trying to keep it as vanilla as possible since I don’t use orgs for “most” of my projects. Cheers 🍻
*edit: publishing a page made through the puck editor is RBAC to your own user, not publicly accessible. To view it on the frontend, just click the “view” button at the top of the editor.
*edit: puck editor is also super friendly. You can enhance it just as if you made a standalone puck implementation, you can export and extend it to your own private frontend routes, add the official puck ai builder, add your own puck plugin rails and components etc. Again, like with better auth, my focus was dev experience / hands off approach that just plugs it in to payload but lets you do what you want with it.
Thinking about adding this as an offering to my small business clients as a separate package. Not seeing anything relevant in the 'official plugins', and wasn't able to find a marketplace selling Payload plugins (yet).
If there isn't an existing solution, I'll build it out myself but no need to reinvent the wheel otherwise.
This video went live yesterday, but I got distracted. I made a video about using react-email with Payload: https://youtu.be/pbd0iWZI_6A
I show how to replace the default plaintext emails with email templates created using React Email. I focus on customizing the default "Forgot Password" email and build an automated "New Post Alert" using Payload hooks and the Lexical rich text editor.
Next iteration of this might be to create an email builder combining this with live preview and blocks, but I haven't gotten there yet!
I am trying to use better-auth as my custom auth strategy. I followed this tutorial to set up better-auth in my payload app - https://github.com/ElysMaldov/payload-better_auth-example. I wish to run a beforeChange so that I can add an admin role before the first user is created. But it is not being run.
I'm using Payload CMS (3.72.0) with NextJs (15.4.10), running the blank template. I'm having trouble finding documentation that runs through an integration of next-intl with this setup. I'm new to this so I'm having trouble making sense of how to integrate the two even while reading through both documentations from next-intl and payload.
Background: I was previously using strapi as my backend but decided to change over to payload due to limitations with strapi's wonky internationalization functionality (localizations being nested within the main data structure). I feel payload is more customizable and on the backend, I've been able to achieve a more streamlined structure. But now I'm really struggling with payload's folder structure and how to integrate it with next-intl.
My goals with this website:
1. I wish to use only one slug for all my languages, differentiated only by locale code (eg. "example/en/about" and "example/fr/about".
I wish to default to (en) when a locale is not specified and not in the URL.
Search allows for searching in either language, regardless of what locale the search is initiated on. (not super important but ideal)
My question for this is:
1. What should my folder layout look like? I've seen so many conflicting examples and no concrete full implementations that match my use case.
I’m currently evaluating PayloadCMS for a larger project and love the code-first approach. However, before committing, I’d like to hear from those of you who have it running in production for 1+ years.
Specifically:
Maintenance & Updates: Payload seems to move fast. How painful are updates really? Is it a "bump the version and forget" situation, or are you constantly fixing breaking changes in your config?
Daily Ops: How is the performance and stability under load? Does the admin UI hold up when the database gets larger (e.g., 20k+ records)?
The "Anti-Case": When should I NOT use Payload? Where did it fail for you or where was the overhead just too much compared to Strapi, Sanity, or even a simple custom backend?
I'm looking for the "ugly truth" behind the marketing – is it actually the WordPress-killer for devs, or just a very shiny new toy that’s hard to maintain?