2018-04-13 20:45:32 -06:00
{
2023-09-06 01:01:57 +02:00
"name" : "payload-monorepo" ,
2026-02-27 15:03:12 -05:00
"version" : "3.78.0" ,
2023-09-14 16:32:37 -04:00
"private" : true ,
2024-03-06 11:38:26 -05:00
"type" : "module" ,
2025-06-10 19:03:59 -04:00
"workspaces" : [
"packages/*" ,
"test/*"
] ,
2018-04-13 20:45:32 -06:00
"scripts" : {
feat!: prebundle payload, ui, richtext-lexical (#6579)
# Breaking Changes
### New file import locations
Exports from the `payload` package have been _significantly_ cleaned up.
Now, just about everything is able to be imported from `payload`
directly, rather than an assortment of subpath exports. This means that
things like `import { buildConfig } from 'payload/config'` are now just
imported via `import { buildConfig } from 'payload'`. The mental model
is significantly simpler for developers, but you might need to update
some of your imports.
Payload now exposes only three exports:
1. `payload` - all types and server-only Payload code
2. `payload/shared` - utilities that can be used in either the browser
or in Node environments
3. `payload/node` - heavy utilities that should only be imported in Node
scripts and never be imported into bundled code like Next.js
### UI library pre-bundling
With this release, we've dramatically sped up the compile time for
Payload by pre-bundling our entire UI package for use inside of the
Payload admin itself. There are new exports that should be used within
Payload custom components:
1. `@payloadcms/ui/client` - all client components
2. `@payloadcms/ui/server` - all server components
For all of your custom Payload admin UI components, you should be
importing from one of these two pre-compiled barrel files rather than
importing from the more deeply nested exports directly. That will keep
compile times nice and speedy, and will also make sure that the bundled
JS for your admin UI is kept small.
For example, whereas before, if you imported the Payload `Button`, you
would have imported it like this:
```ts
import { Button } from '@payloadcms/ui/elements/Button'
```
Now, you would import it like this:
```ts
import { Button } from '@payloadcms/ui/client'
```
This is a significant DX / performance optimization that we're pretty
pumped about.
However, if you are importing or re-using Payload UI components
_outside_ of the Payload admin UI, for example in your own frontend
apps, you can import from the individual component exports which will
make sure that the bundled JS is kept to a minimum in your frontend
apps. So in your own frontend, you can continue to import directly to
the components that you want to consume rather than importing from the
pre-compiled barrel files.
Individual component exports will now come with their corresponding CSS
and everything will work perfectly as-expected.
### Specific exports have changed
- `'@payloadcms/ui/templates/Default'` and
`'@payloadcms/ui/templates/Minimal`' are now exported from
`'@payloadcms/next/templates'`
- Old: `import { LogOut } from '@payloadcms/ui/icons/LogOut'` new:
`import { LogOutIcon } from '@payloadcms/ui/icons/LogOut'`
## Background info
In effort to make local dev as fast as possible, we need to import as
few files as possible so that the compiler has less to process. One way
we've achieved this in the Admin Panel was to _remove_ all .scss imports
from all components in the `@payloadcms/ui` module using a build
process. This stripped all `import './index.scss'` statements out of
each component before injecting them into `dist`. Instead, it bundles
all of the CSS into a single `main.css` file, and we import _that_ at
the root of the app.
While this concept is _still_ the right solution to the problem, this
particular approach is not viable when using these components outside
the Admin Panel, where not only does this root stylesheet not exist, but
where it would also bloat your app with unused styles. Instead, we need
to _keep_ these .scss imports in place so they are imported directly
alongside your components, as expected. Then, we need create a _new_
build step that _separately_ compiles the components _without_ their
stylesheets—this way your app can consume either as needed from the new
`client` and `server` barrel files within `@payloadcms/ui`, i.e. from
within `@payloadcms/next` and all other admin-specific packages and
plugins.
This way, all other applications will simply import using the direct
file paths, just as they did before. Except now they come with
stylesheets.
And we've gotten a pretty awesome initial compilation performance boost.
---------
Co-authored-by: James <james@trbl.design>
Co-authored-by: Alessio Gravili <alessio@gravili.de>
2024-06-17 14:25:36 -04:00
"bf" : "pnpm run build:force" ,
2024-03-03 13:01:59 -05:00
"build" : "pnpm run build:core" ,
2025-03-05 14:14:35 -05:00
"build:admin-bar" : "turbo build --filter \"@payloadcms/admin-bar\"" ,
2025-09-30 01:05:16 +01:00
"build:all" : "turbo build --filter \"!blank\" --filter \"!website\" --filter \"!ecommerce\"" ,
2025-12-09 14:26:11 -05:00
"build:all:force" : "pnpm clean && pnpm build:all" ,
2024-02-29 16:28:59 -05:00
"build:app" : "next build" ,
2024-03-07 13:01:43 -05:00
"build:app:analyze" : "cross-env ANALYZE=true next build" ,
2025-07-07 13:00:02 -07:00
"build:bundle-for-analysis" : "turbo run build:bundle-for-analysis" ,
feat!: prebundle payload, ui, richtext-lexical (#6579)
# Breaking Changes
### New file import locations
Exports from the `payload` package have been _significantly_ cleaned up.
Now, just about everything is able to be imported from `payload`
directly, rather than an assortment of subpath exports. This means that
things like `import { buildConfig } from 'payload/config'` are now just
imported via `import { buildConfig } from 'payload'`. The mental model
is significantly simpler for developers, but you might need to update
some of your imports.
Payload now exposes only three exports:
1. `payload` - all types and server-only Payload code
2. `payload/shared` - utilities that can be used in either the browser
or in Node environments
3. `payload/node` - heavy utilities that should only be imported in Node
scripts and never be imported into bundled code like Next.js
### UI library pre-bundling
With this release, we've dramatically sped up the compile time for
Payload by pre-bundling our entire UI package for use inside of the
Payload admin itself. There are new exports that should be used within
Payload custom components:
1. `@payloadcms/ui/client` - all client components
2. `@payloadcms/ui/server` - all server components
For all of your custom Payload admin UI components, you should be
importing from one of these two pre-compiled barrel files rather than
importing from the more deeply nested exports directly. That will keep
compile times nice and speedy, and will also make sure that the bundled
JS for your admin UI is kept small.
For example, whereas before, if you imported the Payload `Button`, you
would have imported it like this:
```ts
import { Button } from '@payloadcms/ui/elements/Button'
```
Now, you would import it like this:
```ts
import { Button } from '@payloadcms/ui/client'
```
This is a significant DX / performance optimization that we're pretty
pumped about.
However, if you are importing or re-using Payload UI components
_outside_ of the Payload admin UI, for example in your own frontend
apps, you can import from the individual component exports which will
make sure that the bundled JS is kept to a minimum in your frontend
apps. So in your own frontend, you can continue to import directly to
the components that you want to consume rather than importing from the
pre-compiled barrel files.
Individual component exports will now come with their corresponding CSS
and everything will work perfectly as-expected.
### Specific exports have changed
- `'@payloadcms/ui/templates/Default'` and
`'@payloadcms/ui/templates/Minimal`' are now exported from
`'@payloadcms/next/templates'`
- Old: `import { LogOut } from '@payloadcms/ui/icons/LogOut'` new:
`import { LogOutIcon } from '@payloadcms/ui/icons/LogOut'`
## Background info
In effort to make local dev as fast as possible, we need to import as
few files as possible so that the compiler has less to process. One way
we've achieved this in the Admin Panel was to _remove_ all .scss imports
from all components in the `@payloadcms/ui` module using a build
process. This stripped all `import './index.scss'` statements out of
each component before injecting them into `dist`. Instead, it bundles
all of the CSS into a single `main.css` file, and we import _that_ at
the root of the app.
While this concept is _still_ the right solution to the problem, this
particular approach is not viable when using these components outside
the Admin Panel, where not only does this root stylesheet not exist, but
where it would also bloat your app with unused styles. Instead, we need
to _keep_ these .scss imports in place so they are imported directly
alongside your components, as expected. Then, we need create a _new_
build step that _separately_ compiles the components _without_ their
stylesheets—this way your app can consume either as needed from the new
`client` and `server` barrel files within `@payloadcms/ui`, i.e. from
within `@payloadcms/next` and all other admin-specific packages and
plugins.
This way, all other applications will simply import using the direct
file paths, just as they did before. Except now they come with
stylesheets.
And we've gotten a pretty awesome initial compilation performance boost.
---------
Co-authored-by: James <james@trbl.design>
Co-authored-by: Alessio Gravili <alessio@gravili.de>
2024-06-17 14:25:36 -04:00
"build:clean" : "pnpm clean:build" ,
2025-09-30 01:05:16 +01:00
"build:core" : "turbo build --filter \"!@payloadcms/plugin-*\" --filter \"!@payloadcms/storage-*\" --filter \"!blank\" --filter \"!website\" --filter \"!ecommerce\"" ,
2024-08-27 22:04:05 -04:00
"build:core:force" : "pnpm clean:build && pnpm build:core --no-cache --force" ,
2024-02-29 10:38:07 -05:00
"build:create-payload-app" : "turbo build --filter create-payload-app" ,
2025-09-29 23:58:18 +03:00
"build:db-d1-sqlite" : "turbo build --filter \"@payloadcms/db-d1-sqlite\"" ,
2024-08-27 22:04:05 -04:00
"build:db-mongodb" : "turbo build --filter \"@payloadcms/db-mongodb\"" ,
"build:db-postgres" : "turbo build --filter \"@payloadcms/db-postgres\"" ,
"build:db-sqlite" : "turbo build --filter \"@payloadcms/db-sqlite\"" ,
"build:db-vercel-postgres" : "turbo build --filter \"@payloadcms/db-vercel-postgres\"" ,
2025-12-05 13:28:48 -08:00
"build:debug" : "turbo build:debug --filter \"!blank\" --filter \"!website\" --filter \"!ecommerce\"" ,
2024-08-27 22:04:05 -04:00
"build:drizzle" : "turbo build --filter \"@payloadcms/drizzle\"" ,
"build:email-nodemailer" : "turbo build --filter \"@payloadcms/email-nodemailer\"" ,
"build:email-resend" : "turbo build --filter \"@payloadcms/email-resend\"" ,
"build:eslint-config" : "turbo build --filter \"@payloadcms/eslint-config\"" ,
feat!: beta-next (#7620)
This PR makes three major changes to the codebase:
1. [Component Paths](#component-paths)
Instead of importing custom components into your config directly, they
are now defined as file paths and rendered only when needed. That way
the Payload config will be significantly more lightweight, and ensures
that the Payload config is 100% server-only and Node-safe. Related
discussion: https://github.com/payloadcms/payload/discussions/6938
2. [Client Config](#client-config)
Deprecates the component map by merging its logic into the client
config. The main goal of this change is for performance and
simplification. There was no need to deeply iterate over the Payload
config twice, once for the component map, and another for the client
config. Instead, we can do everything in the client config one time.
This has also dramatically simplified the client side prop drilling
through the UI library. Now, all components can share the same client
config which matches the exact shape of their Payload config (with the
exception of non-serializable props and mapped custom components).
3. [Custom client component are no longer
server-rendered](#custom-client-components-are-no-longer-server-rendered)
Previously, custom components would be server-rendered, no matter if
they are server or client components. Now, only server components are
rendered on the server. Client components are automatically detected,
and simply get passed through as `MappedComponent` to be rendered fully
client-side.
## Component Paths
Instead of importing custom components into your config directly, they
are now defined as file paths and rendered only when needed. That way
the Payload config will be significantly more lightweight, and ensures
that the Payload config is 100% server-only and Node-safe. Related
discussion: https://github.com/payloadcms/payload/discussions/6938
In order to reference any custom components in the Payload config, you
now have to specify a string path to the component instead of importing
it.
Old:
```ts
import { MyComponent2} from './MyComponent2.js'
admin: {
components: {
Label: MyComponent2
},
},
```
New:
```ts
admin: {
components: {
Label: '/collections/Posts/MyComponent2.js#MyComponent2', // <= has to be a relative path based on a baseDir configured in the Payload config - NOT relative based on the importing file
},
},
```
### Local API within Next.js routes
Previously, if you used the Payload Local API within Next.js pages, all
the client-side modules are being added to the bundle for that specific
page, even if you only need server-side functionality.
This `/test` route, which uses the Payload local API, was previously 460
kb. It is now down to 91 kb and does not bundle the Payload client-side
admin panel anymore.
All tests done
[here](https://github.com/payloadcms/payload-3.0-demo/tree/feat/path-test)
with beta.67/PR, db-mongodb and default richtext-lexical:
**dev /admin before:**

**dev /admin after:**

---
**dev /test before:**

**dev /test after:**

---
**build before:**

**build after::**

### Usage of the Payload Local API / config outside of Next.js
This will make it a lot easier to use the Payload config / local API in
other, server-side contexts. Previously, you might encounter errors due
to client files (like .scss files) not being allowed to be imported.
## Client Config
Deprecates the component map by merging its logic into the client
config. The main goal of this change is for performance and
simplification. There was no need to deeply iterate over the Payload
config twice, once for the component map, and another for the client
config. Instead, we can do everything in the client config one time.
This has also dramatically simplified the client side prop drilling
through the UI library. Now, all components can share the same client
config which matches the exact shape of their Payload config (with the
exception of non-serializable props and mapped custom components).
This is breaking change. The `useComponentMap` hook no longer exists,
and most component props have changed (for the better):
```ts
const { componentMap } = useComponentMap() // old
const { config } = useConfig() // new
```
The `useConfig` hook has also changed in shape, `config` is now a
property _within_ the context obj:
```ts
const config = useConfig() // old
const { config } = useConfig() // new
```
## Custom Client Components are no longer server rendered
Previously, custom components would be server-rendered, no matter if
they are server or client components. Now, only server components are
rendered on the server. Client components are automatically detected,
and simply get passed through as `MappedComponent` to be rendered fully
client-side.
The benefit of this change:
Custom client components can now receive props. Previously, the only way
for them to receive dynamic props from a parent client component was to
use hooks, e.g. `useFieldProps()`. Now, we do have the option of passing
in props to the custom components directly, if they are client
components. This will be simpler than having to look for the correct
hook.
This makes rendering them on the client a little bit more complex, as
you now have to check if that component is a server component (=>
already has been rendered) or a client component (=> not rendered yet,
has to be rendered here). However, this added complexity has been
alleviated through the easy-to-use `<RenderMappedComponent />` helper.
This helper now also handles rendering arrays of custom components (e.g.
beforeList, beforeLogin ...), which actually makes rendering custom
components easier in some cases.
## Misc improvements
This PR includes misc, breaking changes. For example, we previously
allowed unions between components and config object for the same
property. E.g. for the custom view property, you were allowed to pass in
a custom component or an object with other properties, alongside a
custom component.
Those union types are now gone. You can now either pass an object, or a
component. The previous `{ View: MyViewComponent}` is now `{ View: {
Component: MyViewComponent} }` or `{ View: { Default: { Component:
MyViewComponent} } }`.
This dramatically simplifies the way we read & process those properties,
especially in buildComponentMap. We can now simply check for the
existence of one specific property, which always has to be a component,
instead of running cursed runtime checks on a shared union property
which could contain a component, but could also contain functions or
objects.


- [x] I have read and understand the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository.
---------
Co-authored-by: PatrikKozak <patrik@payloadcms.com>
Co-authored-by: Paul <paul@payloadcms.com>
Co-authored-by: Paul Popus <paul@nouance.io>
Co-authored-by: Jacob Fletcher <jacobsfletch@gmail.com>
Co-authored-by: James <james@trbl.design>
2024-08-13 12:54:33 -04:00
"build:essentials:force" : "pnpm clean:build && turbo build --filter=\"payload...\" --filter=\"@payloadcms/ui\" --filter=\"@payloadcms/next\" --filter=\"@payloadcms/db-mongodb\" --filter=\"@payloadcms/db-postgres\" --filter=\"@payloadcms/richtext-lexical\" --filter=\"@payloadcms/translations\" --filter=\"@payloadcms/plugin-cloud\" --filter=\"@payloadcms/graphql\" --no-cache --force" ,
feat!: prebundle payload, ui, richtext-lexical (#6579)
# Breaking Changes
### New file import locations
Exports from the `payload` package have been _significantly_ cleaned up.
Now, just about everything is able to be imported from `payload`
directly, rather than an assortment of subpath exports. This means that
things like `import { buildConfig } from 'payload/config'` are now just
imported via `import { buildConfig } from 'payload'`. The mental model
is significantly simpler for developers, but you might need to update
some of your imports.
Payload now exposes only three exports:
1. `payload` - all types and server-only Payload code
2. `payload/shared` - utilities that can be used in either the browser
or in Node environments
3. `payload/node` - heavy utilities that should only be imported in Node
scripts and never be imported into bundled code like Next.js
### UI library pre-bundling
With this release, we've dramatically sped up the compile time for
Payload by pre-bundling our entire UI package for use inside of the
Payload admin itself. There are new exports that should be used within
Payload custom components:
1. `@payloadcms/ui/client` - all client components
2. `@payloadcms/ui/server` - all server components
For all of your custom Payload admin UI components, you should be
importing from one of these two pre-compiled barrel files rather than
importing from the more deeply nested exports directly. That will keep
compile times nice and speedy, and will also make sure that the bundled
JS for your admin UI is kept small.
For example, whereas before, if you imported the Payload `Button`, you
would have imported it like this:
```ts
import { Button } from '@payloadcms/ui/elements/Button'
```
Now, you would import it like this:
```ts
import { Button } from '@payloadcms/ui/client'
```
This is a significant DX / performance optimization that we're pretty
pumped about.
However, if you are importing or re-using Payload UI components
_outside_ of the Payload admin UI, for example in your own frontend
apps, you can import from the individual component exports which will
make sure that the bundled JS is kept to a minimum in your frontend
apps. So in your own frontend, you can continue to import directly to
the components that you want to consume rather than importing from the
pre-compiled barrel files.
Individual component exports will now come with their corresponding CSS
and everything will work perfectly as-expected.
### Specific exports have changed
- `'@payloadcms/ui/templates/Default'` and
`'@payloadcms/ui/templates/Minimal`' are now exported from
`'@payloadcms/next/templates'`
- Old: `import { LogOut } from '@payloadcms/ui/icons/LogOut'` new:
`import { LogOutIcon } from '@payloadcms/ui/icons/LogOut'`
## Background info
In effort to make local dev as fast as possible, we need to import as
few files as possible so that the compiler has less to process. One way
we've achieved this in the Admin Panel was to _remove_ all .scss imports
from all components in the `@payloadcms/ui` module using a build
process. This stripped all `import './index.scss'` statements out of
each component before injecting them into `dist`. Instead, it bundles
all of the CSS into a single `main.css` file, and we import _that_ at
the root of the app.
While this concept is _still_ the right solution to the problem, this
particular approach is not viable when using these components outside
the Admin Panel, where not only does this root stylesheet not exist, but
where it would also bloat your app with unused styles. Instead, we need
to _keep_ these .scss imports in place so they are imported directly
alongside your components, as expected. Then, we need create a _new_
build step that _separately_ compiles the components _without_ their
stylesheets—this way your app can consume either as needed from the new
`client` and `server` barrel files within `@payloadcms/ui`, i.e. from
within `@payloadcms/next` and all other admin-specific packages and
plugins.
This way, all other applications will simply import using the direct
file paths, just as they did before. Except now they come with
stylesheets.
And we've gotten a pretty awesome initial compilation performance boost.
---------
Co-authored-by: James <james@trbl.design>
Co-authored-by: Alessio Gravili <alessio@gravili.de>
2024-06-17 14:25:36 -04:00
"build:force" : "pnpm run build:core:force" ,
2024-08-27 22:04:05 -04:00
"build:graphql" : "turbo build --filter \"@payloadcms/graphql\"" ,
feat: add KV storage adapters (#9913)
Adds KV storage support to Payload Local API with an adapter pattern,
includes 3 adapters. We'll use it for the Realtime API.
You can access it via `payload.kv`:
```ts
interface KVAdapter {
/**
* Clears all entries in the store.
* @returns A promise that resolves once the store is cleared.
*/
clear(): Promise<void>
/**
* Deletes a value from the store by its key.
* @param key - The key to delete.
* @returns A promise that resolves once the key is deleted.
*/
delete(key: string): Promise<void>
/**
* Retrieves a value from the store by its key.
* @param key - The key to look up.
* @returns A promise that resolves to the value, or `null` if not found.
*/
get(key: string): Promise<KVStoreValue | null>
/**
* Checks if a key exists in the store.
* @param key - The key to check.
* @returns A promise that resolves to `true` if the key exists, otherwise `false`.
*/
has(key: string): Promise<boolean>
/**
* Retrieves all the keys in the store.
* @returns A promise that resolves to an array of keys.
*/
keys(): Promise<string[]>
/**
* Sets a value in the store with the given key.
* @param key - The key to associate with the value.
* @param value - The value to store.
* @returns A promise that resolves once the value is stored.
*/
set(key: string, value: KVStoreValue): Promise<void>
}
```
To configure the adapter you can use the `kv` property of the root
config.
```ts
buildConfig({
kv: adapter()
})
```
#### Database KV adapter (default)
No need to configure, as Payload uses it by default. It generates new
collection `payload-kv` and uses the current database adapter to access
it. The collection is hidden in the admin panel and access locked.
If you want to override the generated collection:
```ts
import { databaseKVAdapter } from 'payload'
buildConfig({
kv: databaseKVAdapter({
kvCollectionOverrides: {
slug: 'custom-kv',
...(process.env.DEBUG === 'true' && {
admin: { hidden: false },
access: {},
}),
},
}),
})
```
#### In Memory KV adapter
Simple and very fast storage using memory. Don't use it on Vercel /
multiple instances or if you need data persistence.
```ts
import { inMemoryKVAdapter } from 'payload'
buildConfig({
kv: inMemoryKVAdapter(),
})
```
#### Redis KV Adapter
Uses Redis. Probably the best option as it's faster than database,
persistent and works with Vercel / multiple instances, but requires
additional setup
```sh
pnpm add @payloadcms/kv-redis
```
```ts
import { redisKVAdapter } from '@payloadcms/kv-redis'
buildConfig({
kv: redisKVAdapter({
keyPrefix: "custom-prefix:", // defaults to 'payload-kv:'
redisURL: "redis://127.0.0.1:6379" // defaults to process.env.REDIS_URL (Vercel generates this variable for you if you connect a project to Redis)
}),
})
```
2025-10-30 17:05:38 +02:00
"build:kv-redis" : "turbo build --filter \"@payloadcms/kv-redis\"" ,
2024-08-27 22:04:05 -04:00
"build:live-preview" : "turbo build --filter \"@payloadcms/live-preview\"" ,
"build:live-preview-react" : "turbo build --filter \"@payloadcms/live-preview-react\"" ,
"build:live-preview-vue" : "turbo build --filter \"@payloadcms/live-preview-vue\"" ,
2024-08-23 10:54:20 -04:00
"build:next" : "turbo build --filter \"@payloadcms/next\"" ,
2025-01-20 11:34:51 -05:00
"build:packages" : "turbo build --filter=./packages/*" ,
2024-02-29 10:38:07 -05:00
"build:payload" : "turbo build --filter payload" ,
2024-10-24 21:19:15 -04:00
"build:payload-cloud" : "turbo build --filter \"@payloadcms/payload-cloud\"" ,
2024-08-27 22:04:05 -04:00
"build:plugin-cloud-storage" : "turbo build --filter \"@payloadcms/plugin-cloud-storage\"" ,
2025-09-30 01:05:16 +01:00
"build:plugin-ecommerce" : "turbo build --filter \"@payloadcms/plugin-ecommerce\"" ,
2024-08-27 22:04:05 -04:00
"build:plugin-form-builder" : "turbo build --filter \"@payloadcms/plugin-form-builder\"" ,
2025-03-04 20:06:43 -05:00
"build:plugin-import-export" : "turbo build --filter \"@payloadcms/plugin-import-export\"" ,
feat: plugin mcp (#13674)
This plugin adds support for MCP (Model Context Protocol) so you can use
Payload as an MCP server.
[Getting Started docs to
MCP](https://modelcontextprotocol.io/docs/getting-started/intro)
## Background
MCP Allows you to interact with with models via a streamlined protocol.
Developers can define allow retrieval on collections, create tools,
create resources, and create prompts that models can interact with.
This plugin uses:
[@modelcontextprotocol/sdk](https://github.com/modelcontextprotocol/typescript-sdk),
[mcp-adapter](https://github.com/vercel/mcp-adapter)
## 🚀 Basic Setup
```typescript
import { buildConfig } from 'payload'
import { mcpPlugin } from '@payloadcms/plugin-mcp'
export default buildConfig({
// ... your existing config
plugins: [
mcpPlugin({
collections: {
posts: true,
},
}),
],
})
```
### With custom server options
```typescript
import { z } from 'zod'
import { mcpPlugin } from '@payloadcms/plugin-mcp'
export default buildConfig({
// ... your existing config
plugins: [
mcpPlugin({
collections: {
posts: true,
},
// Custom server options
mcp: {
handlerOptions: {
verboseLogs: true,
maxDuration: 60,
},
serverOptions: {
serverInfo: {
name: 'My Custom MCP Server',
version: '1.0.0',
},
},
},
}),
],
})
```
## Tools
These are the Payload tools you control through the Admin or Payload
config that LLMs use to interact with collection documents.
### Resources
| Tool | Description |
| ---------------------------------------------------- |
-------------------------------------- |
| `findResources`| Find documents in a collection |
| `createResource` | Create a new document |
| `updateResource` | Update documents by ID or where clause |
| `deleteResource`| Delete documents by ID or where clause |
### Custom Tools
You can include your own custom MCP tools as well.
```ts
import { z } from 'zod'
import { mcpPlugin } from '@payloadcms/plugin-mcp'
export default buildConfig({
// ... your existing config
plugins: [
mcpPlugin({
collections: {
posts: true,
},
mcp: {
// Add your own custom tools
tools: [
{
name: 'diceRoll',
description: 'Rolls a virtual dice with a specified number of sides',
handler: (args: Record<string, unknown>) => {
const sides = (args.sides as number) || 6
const result = Math.floor(Math.random() * sides) + 1
return Promise.resolve({
content: [
{
type: 'text' as const,
text: `# Dice Roll Result\n\n**Sides:** ${sides}\n**Result:** ${result}\n\n🎲 You rolled a **${result}** on a ${sides}-sided die!`,
},
],
})
},
parameters: z.object({
sides: z
.number()
.int()
.min(2)
.max(1000)
.optional()
.default(6)
.describe('Number of sides on the dice (default: 6)'),
}).shape,
},
],
},
}),
],
})
```
## Connectors
To connect to the MCP server as a connector via a client like VS Code,
Claude or Cursor:
```json
{
"mcpServers": {
"My Payload App": {
"command": "npx",
"args": [
"-y",
"mcp-remote",
"http://localhost:300/api/mcp",
"--header",
"Authorization: Bearer API_KEY_GOES_HERE"
]
},
}
}
```
## Testing your MCP endpoint
I highly recommend using `npx @modelcontextprotocol/inspector` to test
your MCP server.
Your default MCP endpoint is: `http://localhost:3000/api/mcp`
```bash
npx @modelcontextprotocol/inspector
```
2025-10-22 22:20:54 -04:00
"build:plugin-mcp" : "turbo build --filter \"@payloadcms/plugin-mcp\"" ,
feat: adds multi-tenant plugin (#10447)
### Multi Tenant Plugin
This PR adds a `@payloadcms/plugin-multi-tenant` package. The goal is to
consolidate a source of truth for multi-tenancy. Currently we are
maintaining different implementations for clients, users in discord and
our examples repo. When updates or new paradigms arise we need to
communicate this with everyone and update code examples which is hard to
maintain.
### What does it do?
- adds a tenant selector to the sidebar, above the nav links
- adds a hidden tenant field to every collection that you specify
- adds an array field to your users collection, allowing you to assign
users to tenants
- by default combines the access control (to enabled collections) that
you define, with access control based on the tenants assigned to user on
the request
- by default adds a baseListFilter that filters the documents shown in
the list view with the selected tenant in the admin panel
### What does it not do?
- it does not implement multi-tenancy for your frontend. You will need
to query data for specific tenants to build your website/application
- it does not add a tenants collection, you **NEED** to add a tenants
collection, where you can define what types of fields you would like on
it
### The plugin config
Most of the options listed below are _optional_, but it is easier to
just lay out all of the configuration options.
**TS Type**
```ts
type MultiTenantPluginConfig<ConfigTypes = unknown> = {
/**
* After a tenant is deleted, the plugin will attempt to clean up related documents
* - removing documents with the tenant ID
* - removing the tenant from users
*
* @default true
*/
cleanupAfterTenantDelete?: boolean
/**
* Automatically
*/
collections: {
[key in CollectionSlug]?: {
/**
* Set to `true` if you want the collection to behave as a global
*
* @default false
*/
isGlobal?: boolean
/**
* Set to `false` if you want to manually apply the baseListFilter
*
* @default true
*/
useBaseListFilter?: boolean
/**
* Set to `false` if you want to handle collection access manually without the multi-tenant constraints applied
*
* @default true
*/
useTenantAccess?: boolean
}
}
/**
* Enables debug mode
* - Makes the tenant field visible in the admin UI within applicable collections
*
* @default false
*/
debug?: boolean
/**
* Enables the multi-tenant plugin
*
* @default true
*/
enabled?: boolean
/**
* Field configuration for the field added to all tenant enabled collections
*/
tenantField?: {
access?: RelationshipField['access']
/**
* The name of the field added to all tenant enabled collections
*
* @default 'tenant'
*/
name?: string
}
/**
* Field configuration for the field added to the users collection
*
* If `includeDefaultField` is `false`, you must include the field on your users collection manually
* This is useful if you want to customize the field or place the field in a specific location
*/
tenantsArrayField?:
| {
/**
* Access configuration for the array field
*/
arrayFieldAccess?: ArrayField['access']
/**
* When `includeDefaultField` is `true`, the field will be added to the users collection automatically
*/
includeDefaultField?: true
/**
* Additional fields to include on the tenants array field
*/
rowFields?: Field[]
/**
* Access configuration for the tenant field
*/
tenantFieldAccess?: RelationshipField['access']
}
| {
arrayFieldAccess?: never
/**
* When `includeDefaultField` is `false`, you must include the field on your users collection manually
*/
includeDefaultField?: false
rowFields?: never
tenantFieldAccess?: never
}
/**
* The slug for the tenant collection
*
* @default 'tenants'
*/
tenantsSlug?: string
/**
* Function that determines if a user has access to _all_ tenants
*
* Useful for super-admin type users
*/
userHasAccessToAllTenants?: (
user: ConfigTypes extends { user: User } ? ConfigTypes['user'] : User,
) => boolean
}
```
**Example usage**
```ts
import type { Config } from './payload-types'
import { buildConfig } from 'payload'
export default buildConfig({
plugins: [
multiTenantPlugin<Config>({
collections: {
pages: {},
},
userHasAccessToAllTenants: (user) => isSuperAdmin(user),
}),
],
})
```
### How to configure Collections as Globals for multi-tenant
When using multi-tenant, globals need to actually be configured as
collections so the content can be specific per tenant.
To do that, you can mark a collection with `isGlobal` and it will behave
like a global and users will not see the list view.
```ts
multiTenantPlugin({
collections: {
navigation: {
isGlobal: true,
},
},
})
```
2025-01-15 14:47:46 -05:00
"build:plugin-multi-tenant" : "turbo build --filter \"@payloadcms/plugin-multi-tenant\"" ,
2024-08-27 22:04:05 -04:00
"build:plugin-nested-docs" : "turbo build --filter \"@payloadcms/plugin-nested-docs\"" ,
"build:plugin-redirects" : "turbo build --filter \"@payloadcms/plugin-redirects\"" ,
"build:plugin-search" : "turbo build --filter \"@payloadcms/plugin-search\"" ,
"build:plugin-sentry" : "turbo build --filter \"@payloadcms/plugin-sentry\"" ,
"build:plugin-seo" : "turbo build --filter \"@payloadcms/plugin-seo\"" ,
"build:plugin-stripe" : "turbo build --filter \"@payloadcms/plugin-stripe\"" ,
2024-04-25 22:19:37 -04:00
"build:plugins" : "turbo build --filter \"@payloadcms/plugin-*\"" ,
2025-01-20 11:34:51 -05:00
"build:releaser" : "turbo build --filter \"@tools/releaser\"" ,
2024-08-27 22:04:05 -04:00
"build:richtext-lexical" : "turbo build --filter \"@payloadcms/richtext-lexical\"" ,
"build:richtext-slate" : "turbo build --filter \"@payloadcms/richtext-slate\"" ,
feat: add Payload SDK package (#9463)
Adds Payload SDK package, which can be used to query Payload REST API in
a fully type safe way. Has support for all necessary operations,
including auth, type safe `select`, `populate`, `joins` properties and
simplified file uploading.
Its interface is _very_ similar to the Local API, can't even notice the
difference:
Example:
```ts
import { PayloadSDK } from '@payloadcms/sdk'
import type { Config } from './payload-types'
// Pass your config from generated types as generic
const sdk = new PayloadSDK<Config>({
baseURL: 'https://example.com/api',
})
// Find operation
const posts = await sdk.find({
collection: 'posts',
draft: true,
limit: 10,
locale: 'en',
page: 1,
where: { _status: { equals: 'published' } },
})
// Find by ID operation
const posts = await sdk.findByID({
id,
collection: 'posts',
draft: true,
locale: 'en',
})
// Auth login operation
const result = await sdk.login({
collection: 'users',
data: {
email: 'dev@payloadcms.com',
password: '12345',
},
})
// Create operation
const result = await sdk.create({
collection: 'posts',
data: { text: 'text' },
})
// Create operation with a file
// `file` can be either a Blob | File object or a string URL
const result = await sdk.create({ collection: 'media', file, data: {} })
// Count operation
const result = await sdk.count({ collection: 'posts', where: { id: { equals: post.id } } })
// Update (by ID) operation
const result = await sdk.update({
collection: 'posts',
id: post.id,
data: {
text: 'updated-text',
},
})
// Update (bulk) operation
const result = await sdk.update({
collection: 'posts',
where: {
id: {
equals: post.id,
},
},
data: { text: 'updated-text-bulk' },
})
// Delete (by ID) operation
const result = await sdk.delete({ id: post.id, collection: 'posts' })
// Delete (bulk) operation
const result = await sdk.delete({ where: { id: { equals: post.id } }, collection: 'posts' })
// Find Global operation
const result = await sdk.findGlobal({ slug: 'global' })
// Update Global operation
const result = await sdk.updateGlobal({ slug: 'global', data: { text: 'some-updated-global' } })
// Auth Login operation
const result = await sdk.login({
collection: 'users',
data: { email: 'dev@payloadcms.com', password: '123456' },
})
// Auth Me operation
const result = await sdk.me(
{ collection: 'users' },
{
headers: {
Authorization: `JWT ${user.token}`,
},
},
)
// Auth Refresh Token operation
const result = await sdk.refreshToken(
{ collection: 'users' },
{ headers: { Authorization: `JWT ${user.token}` } },
)
// Auth Forgot Password operation
const result = await sdk.forgotPassword({
collection: 'users',
data: { email: user.email },
})
// Auth Reset Password operation
const result = await sdk.resetPassword({
collection: 'users',
data: { password: '1234567', token: resetPasswordToken },
})
// Find Versions operation
const result = await sdk.findVersions({
collection: 'posts',
where: { parent: { equals: post.id } },
})
// Find Version by ID operation
const result = await sdk.findVersionByID({ collection: 'posts', id: version.id })
// Restore Version operation
const result = await sdk.restoreVersion({
collection: 'posts',
id,
})
// Find Global Versions operation
const result = await sdk.findGlobalVersions({
slug: 'global',
})
// Find Global Version by ID operation
const result = await sdk.findGlobalVersionByID({ id: version.id, slug: 'global' })
// Restore Global Version operation
const result = await sdk.restoreGlobalVersion({
slug: 'global',
id
})
```
Every operation has optional 3rd parameter which is used to add
additional data to the RequestInit object (like headers):
```ts
await sdk.me({
collection: "users"
}, {
// RequestInit object
headers: {
Authorization: `JWT ${token}`
}
})
```
To query custom endpoints, you can use the `request` method, which is
used internally for all other methods:
```ts
await sdk.request({
method: 'POST',
path: '/send-data',
json: {
id: 1,
},
})
```
Custom `fetch` implementation and `baseInit` for shared `RequestInit`
properties:
```ts
const sdk = new PayloadSDK<Config>({
baseInit: { credentials: 'include' },
baseURL: 'https://example.com/api',
fetch: async (url, init) => {
console.log('before req')
const response = await fetch(url, init)
console.log('after req')
return response
},
})
```
2025-09-30 00:01:01 +03:00
"build:sdk" : "turbo build --filter \"@payloadcms/sdk\"" ,
2024-08-27 22:04:05 -04:00
"build:storage-azure" : "turbo build --filter \"@payloadcms/storage-azure\"" ,
"build:storage-gcs" : "turbo build --filter \"@payloadcms/storage-gcs\"" ,
2026-02-18 18:46:49 +00:00
"build:storage-r2" : "turbo build --filter \"@payloadcms/storage-r2\"" ,
2024-08-27 22:04:05 -04:00
"build:storage-s3" : "turbo build --filter \"@payloadcms/storage-s3\"" ,
"build:storage-uploadthing" : "turbo build --filter \"@payloadcms/storage-uploadthing\"" ,
"build:storage-vercel-blob" : "turbo build --filter \"@payloadcms/storage-vercel-blob\"" ,
2024-04-23 08:46:15 -04:00
"build:tests" : "pnpm --filter payload-test-suite run typecheck" ,
2025-01-20 11:34:51 -05:00
"build:tools" : "turbo build --filter=./tools/*" ,
2024-08-27 22:04:05 -04:00
"build:translations" : "turbo build --filter \"@payloadcms/translations\"" ,
"build:ui" : "turbo build --filter \"@payloadcms/ui\"" ,
2024-03-01 16:33:54 -05:00
"clean" : "turbo clean" ,
2024-07-11 14:33:45 -04:00
"clean:all" : "node ./scripts/delete-recursively.js '@node_modules' 'media/*' '**/dist/' '**/.cache/*' '**/.next/*' '**/.turbo/*' '**/tsconfig.tsbuildinfo' '**/payload*.tgz' '**/meta_*.json'" ,
"clean:build" : "node ./scripts/delete-recursively.js 'media/' '**/dist/' '**/.cache/' '**/.next/' '**/.turbo/' '**/tsconfig.tsbuildinfo' '**/payload*.tgz' '**/meta_*.json'" ,
2025-01-12 14:39:31 -07:00
"clean:build:allowtgz" : "node ./scripts/delete-recursively.js 'media/' '**/dist/' '**/.cache/' '**/.next/' '**/.turbo/' '**/tsconfig.tsbuildinfo' '**/meta_*.json'" ,
2024-07-11 14:33:45 -04:00
"clean:cache" : "node ./scripts/delete-recursively.js node_modules/.cache! packages/payload/node_modules/.cache! .next/*" ,
2025-06-16 04:58:03 -07:00
"dev" : "cross-env NODE_OPTIONS=\"--no-deprecation --max-old-space-size=16384\" tsx ./test/dev.ts" ,
feat(db-postgres, db-sqlite): drizzle schema generation (#9953)
This PR allows to have full type safety on `payload.drizzle` with a
single command
```sh
pnpm payload generate:db-schema
```
Which generates TypeScript code with Drizzle declarations based on the
current database schema.
Example of generated file with the website template:
https://gist.github.com/r1tsuu/b8687f211b51d9a3a7e78ba41e8fbf03
Video that shows the power:
https://github.com/user-attachments/assets/3ced958b-ec1d-49f5-9f51-d859d5fae236
We also now proxy drizzle package the same way we do for Lexical so you
don't have to install it (and you shouldn't because you may have version
mismatch).
Instead, you can import from Drizzle like this:
```ts
import {
pgTable,
index,
foreignKey,
integer,
text,
varchar,
jsonb,
boolean,
numeric,
serial,
timestamp,
uniqueIndex,
pgEnum,
} from '@payloadcms/db-postgres/drizzle/pg-core'
import { sql } from '@payloadcms/db-postgres/drizzle'
import { relations } from '@payloadcms/db-postgres/drizzle/relations'
```
Fixes https://github.com/payloadcms/payload/discussions/4318
In the future we can also support types generation for mongoose / raw
mongodb results.
2024-12-19 18:08:17 +02:00
"dev:generate-db-schema" : "pnpm runts ./test/generateDatabaseSchema.ts" ,
feat!: beta-next (#7620)
This PR makes three major changes to the codebase:
1. [Component Paths](#component-paths)
Instead of importing custom components into your config directly, they
are now defined as file paths and rendered only when needed. That way
the Payload config will be significantly more lightweight, and ensures
that the Payload config is 100% server-only and Node-safe. Related
discussion: https://github.com/payloadcms/payload/discussions/6938
2. [Client Config](#client-config)
Deprecates the component map by merging its logic into the client
config. The main goal of this change is for performance and
simplification. There was no need to deeply iterate over the Payload
config twice, once for the component map, and another for the client
config. Instead, we can do everything in the client config one time.
This has also dramatically simplified the client side prop drilling
through the UI library. Now, all components can share the same client
config which matches the exact shape of their Payload config (with the
exception of non-serializable props and mapped custom components).
3. [Custom client component are no longer
server-rendered](#custom-client-components-are-no-longer-server-rendered)
Previously, custom components would be server-rendered, no matter if
they are server or client components. Now, only server components are
rendered on the server. Client components are automatically detected,
and simply get passed through as `MappedComponent` to be rendered fully
client-side.
## Component Paths
Instead of importing custom components into your config directly, they
are now defined as file paths and rendered only when needed. That way
the Payload config will be significantly more lightweight, and ensures
that the Payload config is 100% server-only and Node-safe. Related
discussion: https://github.com/payloadcms/payload/discussions/6938
In order to reference any custom components in the Payload config, you
now have to specify a string path to the component instead of importing
it.
Old:
```ts
import { MyComponent2} from './MyComponent2.js'
admin: {
components: {
Label: MyComponent2
},
},
```
New:
```ts
admin: {
components: {
Label: '/collections/Posts/MyComponent2.js#MyComponent2', // <= has to be a relative path based on a baseDir configured in the Payload config - NOT relative based on the importing file
},
},
```
### Local API within Next.js routes
Previously, if you used the Payload Local API within Next.js pages, all
the client-side modules are being added to the bundle for that specific
page, even if you only need server-side functionality.
This `/test` route, which uses the Payload local API, was previously 460
kb. It is now down to 91 kb and does not bundle the Payload client-side
admin panel anymore.
All tests done
[here](https://github.com/payloadcms/payload-3.0-demo/tree/feat/path-test)
with beta.67/PR, db-mongodb and default richtext-lexical:
**dev /admin before:**

**dev /admin after:**

---
**dev /test before:**

**dev /test after:**

---
**build before:**

**build after::**

### Usage of the Payload Local API / config outside of Next.js
This will make it a lot easier to use the Payload config / local API in
other, server-side contexts. Previously, you might encounter errors due
to client files (like .scss files) not being allowed to be imported.
## Client Config
Deprecates the component map by merging its logic into the client
config. The main goal of this change is for performance and
simplification. There was no need to deeply iterate over the Payload
config twice, once for the component map, and another for the client
config. Instead, we can do everything in the client config one time.
This has also dramatically simplified the client side prop drilling
through the UI library. Now, all components can share the same client
config which matches the exact shape of their Payload config (with the
exception of non-serializable props and mapped custom components).
This is breaking change. The `useComponentMap` hook no longer exists,
and most component props have changed (for the better):
```ts
const { componentMap } = useComponentMap() // old
const { config } = useConfig() // new
```
The `useConfig` hook has also changed in shape, `config` is now a
property _within_ the context obj:
```ts
const config = useConfig() // old
const { config } = useConfig() // new
```
## Custom Client Components are no longer server rendered
Previously, custom components would be server-rendered, no matter if
they are server or client components. Now, only server components are
rendered on the server. Client components are automatically detected,
and simply get passed through as `MappedComponent` to be rendered fully
client-side.
The benefit of this change:
Custom client components can now receive props. Previously, the only way
for them to receive dynamic props from a parent client component was to
use hooks, e.g. `useFieldProps()`. Now, we do have the option of passing
in props to the custom components directly, if they are client
components. This will be simpler than having to look for the correct
hook.
This makes rendering them on the client a little bit more complex, as
you now have to check if that component is a server component (=>
already has been rendered) or a client component (=> not rendered yet,
has to be rendered here). However, this added complexity has been
alleviated through the easy-to-use `<RenderMappedComponent />` helper.
This helper now also handles rendering arrays of custom components (e.g.
beforeList, beforeLogin ...), which actually makes rendering custom
components easier in some cases.
## Misc improvements
This PR includes misc, breaking changes. For example, we previously
allowed unions between components and config object for the same
property. E.g. for the custom view property, you were allowed to pass in
a custom component or an object with other properties, alongside a
custom component.
Those union types are now gone. You can now either pass an object, or a
component. The previous `{ View: MyViewComponent}` is now `{ View: {
Component: MyViewComponent} }` or `{ View: { Default: { Component:
MyViewComponent} } }`.
This dramatically simplifies the way we read & process those properties,
especially in buildComponentMap. We can now simply check for the
existence of one specific property, which always has to be a component,
instead of running cursed runtime checks on a shared union property
which could contain a component, but could also contain functions or
objects.


- [x] I have read and understand the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository.
---------
Co-authored-by: PatrikKozak <patrik@payloadcms.com>
Co-authored-by: Paul <paul@payloadcms.com>
Co-authored-by: Paul Popus <paul@nouance.io>
Co-authored-by: Jacob Fletcher <jacobsfletch@gmail.com>
Co-authored-by: James <james@trbl.design>
2024-08-13 12:54:33 -04:00
"dev:generate-graphql-schema" : "pnpm runts ./test/generateGraphQLSchema.ts" ,
"dev:generate-importmap" : "pnpm runts ./test/generateImportMap.ts" ,
"dev:generate-types" : "pnpm runts ./test/generateTypes.ts" ,
2024-08-16 15:46:49 -04:00
"dev:postgres" : "cross-env PAYLOAD_DATABASE=postgres pnpm runts ./test/dev.ts" ,
feat!: on demand rsc (#8364)
Currently, Payload renders all custom components on initial compile of
the admin panel. This is problematic for two key reasons:
1. Custom components do not receive contextual data, i.e. fields do not
receive their field data, edit views do not receive their document data,
etc.
2. Components are unnecessarily rendered before they are used
This was initially required to support React Server Components within
the Payload Admin Panel for two key reasons:
1. Fields can be dynamically rendered within arrays, blocks, etc.
2. Documents can be recursively rendered within a "drawer" UI, i.e.
relationship fields
3. Payload supports server/client component composition
In order to achieve this, components need to be rendered on the server
and passed as "slots" to the client. Currently, the pattern for this is
to render custom server components in the "client config". Then when a
view or field is needed to be rendered, we first check the client config
for a "pre-rendered" component, otherwise render our client-side
fallback component.
But for the reasons listed above, this pattern doesn't exactly make
custom server components very useful within the Payload Admin Panel,
which is where this PR comes in. Now, instead of pre-rendering all
components on initial compile, we're able to render custom components
_on demand_, only as they are needed.
To achieve this, we've established [this
pattern](https://github.com/payloadcms/payload/pull/8481) of React
Server Functions in the Payload Admin Panel. With Server Functions, we
can iterate the Payload Config and return JSX through React's
`text/x-component` content-type. This means we're able to pass
contextual props to custom components, such as data for fields and
views.
## Breaking Changes
1. Add the following to your root layout file, typically located at
`(app)/(payload)/layout.tsx`:
```diff
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
+ import type { ServerFunctionClient } from 'payload'
import config from '@payload-config'
import { RootLayout } from '@payloadcms/next/layouts'
import { handleServerFunctions } from '@payloadcms/next/utilities'
import React from 'react'
import { importMap } from './admin/importMap.js'
import './custom.scss'
type Args = {
children: React.ReactNode
}
+ const serverFunctions: ServerFunctionClient = async function (args) {
+ 'use server'
+ return handleServerFunctions({
+ ...args,
+ config,
+ importMap,
+ })
+ }
const Layout = ({ children }: Args) => (
<RootLayout
config={config}
importMap={importMap}
+ serverFunctions={serverFunctions}
>
{children}
</RootLayout>
)
export default Layout
```
2. If you were previously posting to the `/api/form-state` endpoint, it
no longer exists. Instead, you'll need to invoke the `form-state` Server
Function, which can be done through the _new_ `getFormState` utility:
```diff
- import { getFormState } from '@payloadcms/ui'
- const { state } = await getFormState({
- apiRoute: '',
- body: {
- // ...
- },
- serverURL: ''
- })
+ const { getFormState } = useServerFunctions()
+
+ const { state } = await getFormState({
+ // ...
+ })
```
## Breaking Changes
```diff
- useFieldProps()
- useCellProps()
```
More details coming soon.
---------
Co-authored-by: Alessio Gravili <alessio@gravili.de>
Co-authored-by: Jarrod Flesch <jarrodmflesch@gmail.com>
Co-authored-by: James <james@trbl.design>
2024-11-11 13:59:05 -05:00
"dev:prod" : "cross-env NODE_OPTIONS=--no-deprecation tsx ./test/dev.ts --prod" ,
2024-08-21 22:54:47 -04:00
"dev:vercel-postgres" : "cross-env PAYLOAD_DATABASE=vercel-postgres pnpm runts ./test/dev.ts" ,
2024-06-09 01:21:11 -04:00
"devsafe" : "node ./scripts/delete-recursively.js '**/.next' && pnpm dev" ,
test: improve database test setup (#14982)
This PR brings over most of the test suite improvements I made
[here](https://github.com/payloadcms/enterprise-plugins/pull/249) to our
payload monorepo.
- Replaces mongodb-memory-server with an actual mongo db using the
mongodb-community-server docker image. This unblocks the vitest
migration PR (https://github.com/payloadcms/payload/pull/14337).
Currently, debugging does not work in that PR - this is due to the
global setup script that has to start the mongo memory db.
- Just like postgres, all mongodb databases now support vector search.
mongodb-atlas-local supports it natively, and for
mongodb-community-server, our docker compose script installs `mongot`,
which unlocks support for vector search. This means we could add [vector
storage/search tests similar to the ones we have for
postgres](https://github.com/payloadcms/payload/blob/main/test/database/postgres-vector.int.spec.ts)
- Int tests now run against both mongodb adapters: mongodb
(mongodb-community-server) and mongodb-atlas (mongodb-atlas-local)
- Adds docker scripts for mongodb, mongodb-atlas, and postgres,
documented in README.md. Updates default db adapter URLs to
automatically pick up databases started by those scripts. This makes it
easier for people cloning the repo to get started with consistent
databases matching CI - no complicated manual installation steps
- Simplified db setup handling locally in CI. In CI, everything is
scoped to `.github/actions/start-database/action.yml`. Locally,
everything is scoped to `test/helpers/db`. Each database adapter now
shares the same username, password and db name
- Use consistent db connection string env variables, all ending with
_URL
- Updates the CONTRIBUTING.md with up-to-date information and adds a new
database section. We now recommend everyone to use those docker scripts
2025-12-19 07:07:16 -08:00
"docker:mongodb-atlas:restart" : "pnpm docker:mongodb-atlas:stop && pnpm docker:mongodb-atlas:start" ,
2026-02-03 09:22:39 -08:00
"docker:mongodb-atlas:restart:clean" : "docker rm -f mongodb-atlas-payload-test 2>/dev/null; docker compose -f test/__helpers/shared/db/mongodb-atlas/docker-compose.yml down -v && pnpm docker:mongodb-atlas:start" ,
"docker:mongodb-atlas:start" : "docker compose -f test/__helpers/shared/db/mongodb-atlas/docker-compose.yml up -d --wait" ,
"docker:mongodb-atlas:stop" : "docker compose -f test/__helpers/shared/db/mongodb-atlas/docker-compose.yml down" ,
"docker:mongodb-atlas:test" : "pnpm runts test/__helpers/shared/db/mongodb-atlas/run-test-connection.ts" ,
test: improve database test setup (#14982)
This PR brings over most of the test suite improvements I made
[here](https://github.com/payloadcms/enterprise-plugins/pull/249) to our
payload monorepo.
- Replaces mongodb-memory-server with an actual mongo db using the
mongodb-community-server docker image. This unblocks the vitest
migration PR (https://github.com/payloadcms/payload/pull/14337).
Currently, debugging does not work in that PR - this is due to the
global setup script that has to start the mongo memory db.
- Just like postgres, all mongodb databases now support vector search.
mongodb-atlas-local supports it natively, and for
mongodb-community-server, our docker compose script installs `mongot`,
which unlocks support for vector search. This means we could add [vector
storage/search tests similar to the ones we have for
postgres](https://github.com/payloadcms/payload/blob/main/test/database/postgres-vector.int.spec.ts)
- Int tests now run against both mongodb adapters: mongodb
(mongodb-community-server) and mongodb-atlas (mongodb-atlas-local)
- Adds docker scripts for mongodb, mongodb-atlas, and postgres,
documented in README.md. Updates default db adapter URLs to
automatically pick up databases started by those scripts. This makes it
easier for people cloning the repo to get started with consistent
databases matching CI - no complicated manual installation steps
- Simplified db setup handling locally in CI. In CI, everything is
scoped to `.github/actions/start-database/action.yml`. Locally,
everything is scoped to `test/helpers/db`. Each database adapter now
shares the same username, password and db name
- Use consistent db connection string env variables, all ending with
_URL
- Updates the CONTRIBUTING.md with up-to-date information and adds a new
database section. We now recommend everyone to use those docker scripts
2025-12-19 07:07:16 -08:00
"docker:mongodb:restart" : "pnpm docker:mongodb:stop && pnpm docker:mongodb:start" ,
2026-02-03 09:22:39 -08:00
"docker:mongodb:restart:clean" : "docker rm -f mongodb-payload-test mongot-payload-test 2>/dev/null; docker compose -f test/__helpers/shared/db/mongodb/docker-compose.yml down -v && pnpm docker:mongodb:start" ,
"docker:mongodb:start" : "docker compose -f test/__helpers/shared/db/mongodb/docker-compose.yml up -d --wait" ,
"docker:mongodb:stop" : "docker rm -f mongodb-payload-test mongot-payload-test 2>/dev/null; docker compose -f test/__helpers/shared/db/mongodb/docker-compose.yml down" ,
"docker:mongodb:test" : "pnpm runts test/__helpers/shared/db/mongodb/run-test-connection.ts" ,
test: improve database test setup (#14982)
This PR brings over most of the test suite improvements I made
[here](https://github.com/payloadcms/enterprise-plugins/pull/249) to our
payload monorepo.
- Replaces mongodb-memory-server with an actual mongo db using the
mongodb-community-server docker image. This unblocks the vitest
migration PR (https://github.com/payloadcms/payload/pull/14337).
Currently, debugging does not work in that PR - this is due to the
global setup script that has to start the mongo memory db.
- Just like postgres, all mongodb databases now support vector search.
mongodb-atlas-local supports it natively, and for
mongodb-community-server, our docker compose script installs `mongot`,
which unlocks support for vector search. This means we could add [vector
storage/search tests similar to the ones we have for
postgres](https://github.com/payloadcms/payload/blob/main/test/database/postgres-vector.int.spec.ts)
- Int tests now run against both mongodb adapters: mongodb
(mongodb-community-server) and mongodb-atlas (mongodb-atlas-local)
- Adds docker scripts for mongodb, mongodb-atlas, and postgres,
documented in README.md. Updates default db adapter URLs to
automatically pick up databases started by those scripts. This makes it
easier for people cloning the repo to get started with consistent
databases matching CI - no complicated manual installation steps
- Simplified db setup handling locally in CI. In CI, everything is
scoped to `.github/actions/start-database/action.yml`. Locally,
everything is scoped to `test/helpers/db`. Each database adapter now
shares the same username, password and db name
- Use consistent db connection string env variables, all ending with
_URL
- Updates the CONTRIBUTING.md with up-to-date information and adds a new
database section. We now recommend everyone to use those docker scripts
2025-12-19 07:07:16 -08:00
"docker:postgres:restart" : "pnpm docker:postgres:stop && pnpm docker:postgres:start" ,
2026-02-03 09:22:39 -08:00
"docker:postgres:restart:clean" : "docker rm -f postgres-payload-test 2>/dev/null; docker compose -f test/__helpers/shared/db/postgres/docker-compose.yml down -v && pnpm docker:postgres:start" ,
"docker:postgres:start" : "docker compose -f test/__helpers/shared/db/postgres/docker-compose.yml up -d --wait" ,
"docker:postgres:start:clean" : "docker pull ghcr.io/payloadcms/postgis-vector:latest && docker compose -f test/__helpers/shared/db/postgres/docker-compose.yml down -v && docker compose -f test/__helpers/shared/db/postgres/docker-compose.yml up -d --wait" ,
"docker:postgres:stop" : "docker rm -f postgres-payload-test 2>/dev/null; docker compose -f test/__helpers/shared/db/postgres/docker-compose.yml down" ,
Chore/next poc merge main (#5204)
* wip moves payload, user and data into partial req
* chore: adjust req type
* chore(next): installs sass and resolves type errors
* feat: working login route/view
* fix: me route
* chore(next): scaffolds access routes (#4562)
* chore(next): scaffolds admin layout and dashboard view (#4566)
* chore(next): builds initPage utility (#4589)
* feat(3.0): next route handlers (#4590)
* chore: removes old files
* chore(next): ssr list view (#4594)
* chore: removes old files
* chore: adjusts graphql file imports to align with new operation exports
* chore: allows for custom endpoints
* chore: cleanup
* chore(next): ssr edit view (#4614)
* chore(ui): ssr main nav (#4619)
* chore(next): ssr account view (#4620)
* chore(next): ssr auth views and document create (#4631)
* chore(next): ssr globals view (#4640)
* chore(next): scaffolds document layout (#4644)
* chore(next): ssr versions view (#4645)
* chore(next): ssr field conditions (#4675)
* chore(next): ssr field validations (#4700)
* chore(next): moves dashboard view into next dir
* chore(next): moves account view into next dir
* chore(next): moves global edit view into next dir
* chore(next): returns isolated configs and locale from initPage
* chore(next): ssr api view (#4721)
* feat: adds i18n functionality within Rest API, Local and Client contexts (#4749)
* chore: separate client translation groups with empty line
* chore: add missing translation used in db adapters
* chore: simplify next/routes export and import paths
* chore: renames PayloadT to Payload
* chore(next): custom views (#4748)
* chore: fix translation tsconfig
* chore: adjust other package ts-configs that rely on translations
* chore(next): installs @payloadcms/ui as direct dependency
* chore(next): progress to build
* chore(next): migrates types (#4792)
* fixes acccept-language detection
* chore(next): moves remaining components out from payload core (#4794)
* chore(deps): removes all unused dependencies from payload core (#4797)
* chore(next): achieves buildable state (#4803)
* adds Translation component and removes more react-i18next
* fixes up remaining translation strings
* fixes a few i18n TODO's
* chore: remaining translation strings without colons
* chore: adds missing ja translations
* chore(next): ssr group field (#4830)
* chore: removes placeholder t function
* chore: removes old file
* chore(bundler-webpack): removes webpack bundler
* chore(bundler-vite): removes vite bundler
* chore(next): ssr tabs field (#4863)
* chore(next): ssr row field
* chore(next): ssr textarea field
* chore(next): wires server action into document edit view (#4873)
* chore(next): conditional logic (#4880)
* chore(next): ssr radio, point, code, json, ui, and hidden fields (#4891)
* chore(next): ssr collapsible field (#4894)
* chore: remove findByID from req
* chore: adjusts file property on request type
* comment clarification
* chore: wires up busboy with Requst readstream
* chore: ports over express-fileupload into a NextJS compatible format
* chore: adjust upload file structure
* chore: adds try/catch around routes, corrects a few route responses
* chore: renames file/function
* chore: improve req type safety in local operations, misc req.files replacements
* chore: misc type and fn export changes
* chore: ensures root routes take pass unmodified request to root routes
* chore: improve types
* chore: consolidates locale api req initialization (#4922)
* chore(next): overhauls field rendering strategy (#4924)
* chore(next): ssr array field (#4937)
* chore(next): ssr blocks field (#4942)
* chore(next): ssr upload field and document drawer (#4957)
* chore(next): wires form submissions (#4982)
* chore: api handler adjustments
* feat: adds graphql playground handler
* adds credentials include setting to playground
* remove old playground init, stub graphql handler location
* fix: allow for null fallbackLocale
* fix: correctly prioritize locales passed as null
* chore: move all graphql code into next package
* graphql changes
* chore: semi working version of graphql http layer
* gql fix attempts
* rm console log
* chore: partial gql changes
* chore: adds gql and gql-http back into payload
* chore: removes collection from req
* chore: separates graphql package out for schema generation
* chore: dep cleanup
* chore: move graphql handlers
* chore: removes unused deps
* chore(next): ssr list view (#5032)
* chore: refactor response handler order for custom endpoints
* chore: add back in condition for collection GET path with 2 slugs
* chore: rm optional chain
* chore: import sort route file
* chore: allows custom endpoints to attempt before erroring
* feat: adds memoization to translation functions (#5036)
* chore: fix APIError import
* chore: return attemptCustomEndpointBeforeError responses
* chore(next): properly instantiates table columns
* fix(next): attaches params to req and properly assigns prefs key (#5042)
* chore: reorganize next route order
* chore(next): adds RouteError handler to next routes
* chore: builds payload successfully
* chore: misc file omissions
* fix(ui): maintains proper column order
* fix(ui): ensures first cell is a link
* fix(next): properly copies url object in createPayloadRequest (#5064)
* fix(ui): bumps react-toastify to v10.0.4 to fix hydration warnings
* feat: add route for static file GET requests (#5065)
* chore(next): allows resolved config promise to be thread through initPage (#5071)
* chore(ui): conditionally renders field label from props
* feat(next): next install script
* chore: pass config to route handlers
* feat: initial test suite framework (#4929)
* chore(next): renderable account, api, and create first user views (#5084)
* fix(next): properly parses search params in find, update, and delete handlers (#5088)
* chore(next): ssr versions view (#5085)
* chore: adds homepage for scss testing
* chore: moves dev folder to top, establishes new test pattern
* chore: working turbopack
* chore: sets up working dynamic payload-config imports
* remove unused code
* chore: rm console log
* misc
* feat: correctly subs out ability to boot REST API within same process
* chore: WIP dev suites
* chore: removes need for REST_API folder in test dir
* removes duplicate bootAdminPanel fn
* misc
* specify default export
* chore: sets up jest to work with next/jest
* chore: progress to mongodb and sharp builds
* chore: passing community tests
* chore: sorta workin
* chore: adjust payload-config import
* chore: adds rest client for Next handlers
* chore: removes test garb
* chore: restores payload-config tsconfig path temporarily
* chore: establishes pattern for memory db during tests
* chore: bumps mongoose to 7
* chore(next): 404s on nested create urls
* chore: functional _community e2e
* chore: increases e2e expect timeout
* fix(next): sanitizes locale toString from client config
* chore: type fixes
* chore: pulls mongodb from main
* chore: uses graphql to log user in
* feat: passing auth test suite
* chore(ui): threads params through context and conditionally renders document tabs (#5094)
* feat(ui): adds params context (#5095)
* chore: removes unecessary memory allocation for urlPropertiesObject object
* chore: passing graphql test suite
* chore: removes references to bson
* chore: re-enables mongodb memory server for auth test suite
* chore: replace bson with bson-objectid
* feat: passing collections-rest int suite
* chore: fixes bad imports
* chore: more passing int suites
* feat: passing globals int tests
* feat: passing hooks int test suite
* chore: remove last express file
* chore: start live-preview int test migration
* chore: passing localization int tests
* passing relationships int tests
* chore: partial passing upload int tests
* chore: fixes scss imports
* chore(ui): renders document info provider at root (#5106)
* chore: adds schema path to useFieldPath provider, more passing tests
* chore: begins work to optimize translation imports
* chore: add translations to ui ts-config references
* chore: add exports folder to package json exports
* chore: adds readme how-to-use instructions
* chore: attempts refactor of translation imports
* chore: adds authentication:account translation key to server keys
* chore: finishes translation optimization
* chore: ignores warnings from mongodb
* chore(ui): renders live document title (#5115)
* chore(ui): ssr document tabs (#5116)
* chore: handles redirecting from login
* chore: handle redirect with no searchParams
* chore: handle missing segments
* chore(next): migrates server action into standalone api endpoint (#5122)
* chore: adjust dashboard colection segments
* test: update e2e suites
* fix(ui): prevents unnecessary calls to form state
* chore: fix finding global config fields from schema path
* fix(next): executes root POST endpoints
* chore(ui): ignores values returned by form state polling
* chore: scaffolds ssr rte
* chore: renders client leaves
* chore: server-side rendered rich text elements
* chore: defines ClientFunction pattern
* chore(ui): migrates relationship field
* chore: adds translations, cleans up slate
* chore: functional slate link
* chore: slate upload ssr
* chore: relationship slate ssr
* chore: remaining slate ssr
* chore: fixes circular workspace dep
* chore: correct broken int test import paths
* chore: remove media files from root
* chore: server renders custom edit view
* fix(ui): resolves infinite loading in versions view
* fix(next): resolves global edit view lookup
* chore: payload builds
* chore: delete unused files
* chore: removes local property from payload
* chore: adds mongodb as dev dep in db-mongodb package
* chore: hide deprecation warnings for tempfile and jest-environment-jsdom
* chore: remove all translations from translations dist
* chore: clean ts-config files
* chore: simple type fixes
* chore(ui): server renders custom list view
* chore: fix next config payload-config alias
* chore: adds turbo alias paths
* chore: adjusts translation generation
* chore: improve auth function
* chore: eslint config for packages/ui
* chore(ui): exports FormState
* chore(next): migrates account view to latest patterns
* chore: disable barbie mode
* chore(ui): lints
* chore(next): lints
* chore: for alexical
* chore: custom handler type signature adjustment
* fix: non-boolean condition result causes infinite looping (#4579)
* chore(richtext-lexical): upgrade lexical from v0.12.5 to v0.12.6 (#4732)
* chore(richtext-lexical): upgrade all lexical packages from 0.12.5 to 0.12.6
* fix(richtext-lexical): fix TypeScript errors
* fix indenting
* feat(richtext-lexical): Blocks: generate type definitions for blocks fields (#4529)
* feat(richtext-lexical)!: Update lexical from 0.12.6 to 0.13.1, port over all useful changes from playground (#5066)
* feat(richtext-lexical): Update lexical from 0.12.6 to 0.13.1, port over all useful changes from playground
* chore: upgrade lexical version used in monorepo
* chore: remove the 3
* chore: upgrade nodemon versions (#5059)
* feat: add more options to addFieldStatePromise so that it can be used for field flattening (#4799)
* feat(plugin-seo)!: remove support for payload <2.7.0 (#4765)
* chore(plugin-seo): remove test script from package.json (#4762)
* chore: upgrade @types/nodemailer from v6.4.8 to v6.4.14 (#4733)
* chore: revert auth and initPage changes
* chore(next): moves edit and list views (#5170)
* fix: "The punycode module is deprecated" warning by updating nodemailer
* chore: adjust translations tsconfig paths in root
* chore: fix merge build
---------
Co-authored-by: Jarrod Flesch <jarrodmflesch@gmail.com>
Co-authored-by: Jacob Fletcher <jacobsfletch@gmail.com>
Co-authored-by: Jarrod Flesch <30633324+JarrodMFlesch@users.noreply.github.com>
Co-authored-by: Elliot DeNolf <denolfe@gmail.com>
Co-authored-by: James <james@trbl.design>
Co-authored-by: Alessio Gravili <alessio@gravili.de>
Co-authored-by: Alessio Gravili <70709113+AlessioGr@users.noreply.github.com>
2024-02-28 13:44:17 -05:00
"docker:restart" : "pnpm docker:stop --remove-orphans && pnpm docker:start" ,
2024-12-31 09:14:56 -05:00
"docker:start" : "docker compose -f test/docker-compose.yml up -d" ,
"docker:stop" : "docker compose -f test/docker-compose.yml down" ,
feat!: prebundle payload, ui, richtext-lexical (#6579)
# Breaking Changes
### New file import locations
Exports from the `payload` package have been _significantly_ cleaned up.
Now, just about everything is able to be imported from `payload`
directly, rather than an assortment of subpath exports. This means that
things like `import { buildConfig } from 'payload/config'` are now just
imported via `import { buildConfig } from 'payload'`. The mental model
is significantly simpler for developers, but you might need to update
some of your imports.
Payload now exposes only three exports:
1. `payload` - all types and server-only Payload code
2. `payload/shared` - utilities that can be used in either the browser
or in Node environments
3. `payload/node` - heavy utilities that should only be imported in Node
scripts and never be imported into bundled code like Next.js
### UI library pre-bundling
With this release, we've dramatically sped up the compile time for
Payload by pre-bundling our entire UI package for use inside of the
Payload admin itself. There are new exports that should be used within
Payload custom components:
1. `@payloadcms/ui/client` - all client components
2. `@payloadcms/ui/server` - all server components
For all of your custom Payload admin UI components, you should be
importing from one of these two pre-compiled barrel files rather than
importing from the more deeply nested exports directly. That will keep
compile times nice and speedy, and will also make sure that the bundled
JS for your admin UI is kept small.
For example, whereas before, if you imported the Payload `Button`, you
would have imported it like this:
```ts
import { Button } from '@payloadcms/ui/elements/Button'
```
Now, you would import it like this:
```ts
import { Button } from '@payloadcms/ui/client'
```
This is a significant DX / performance optimization that we're pretty
pumped about.
However, if you are importing or re-using Payload UI components
_outside_ of the Payload admin UI, for example in your own frontend
apps, you can import from the individual component exports which will
make sure that the bundled JS is kept to a minimum in your frontend
apps. So in your own frontend, you can continue to import directly to
the components that you want to consume rather than importing from the
pre-compiled barrel files.
Individual component exports will now come with their corresponding CSS
and everything will work perfectly as-expected.
### Specific exports have changed
- `'@payloadcms/ui/templates/Default'` and
`'@payloadcms/ui/templates/Minimal`' are now exported from
`'@payloadcms/next/templates'`
- Old: `import { LogOut } from '@payloadcms/ui/icons/LogOut'` new:
`import { LogOutIcon } from '@payloadcms/ui/icons/LogOut'`
## Background info
In effort to make local dev as fast as possible, we need to import as
few files as possible so that the compiler has less to process. One way
we've achieved this in the Admin Panel was to _remove_ all .scss imports
from all components in the `@payloadcms/ui` module using a build
process. This stripped all `import './index.scss'` statements out of
each component before injecting them into `dist`. Instead, it bundles
all of the CSS into a single `main.css` file, and we import _that_ at
the root of the app.
While this concept is _still_ the right solution to the problem, this
particular approach is not viable when using these components outside
the Admin Panel, where not only does this root stylesheet not exist, but
where it would also bloat your app with unused styles. Instead, we need
to _keep_ these .scss imports in place so they are imported directly
alongside your components, as expected. Then, we need create a _new_
build step that _separately_ compiles the components _without_ their
stylesheets—this way your app can consume either as needed from the new
`client` and `server` barrel files within `@payloadcms/ui`, i.e. from
within `@payloadcms/next` and all other admin-specific packages and
plugins.
This way, all other applications will simply import using the direct
file paths, just as they did before. Except now they come with
stylesheets.
And we've gotten a pretty awesome initial compilation performance boost.
---------
Co-authored-by: James <james@trbl.design>
Co-authored-by: Alessio Gravili <alessio@gravili.de>
2024-06-17 14:25:36 -04:00
"force:build" : "pnpm run build:core:force" ,
2025-09-30 01:05:16 +01:00
"lint" : "turbo run lint --log-order=grouped --continue --filter \"!blank\" --filter \"!website\" --filter \"!ecommerce\"" ,
2024-11-12 20:50:52 -05:00
"lint-staged" : "lint-staged" ,
2025-09-30 01:05:16 +01:00
"lint:fix" : "turbo run lint:fix --log-order=grouped --continue --filter \"!blank\" --filter \"!website\" --filter \"!ecommerce\"" ,
2026-01-06 05:23:32 +11:00
"lint:scss" : "stylelint \"packages/ui/src/**/*.scss\"" ,
2024-06-09 01:21:11 -04:00
"obliterate-playwright-cache-macos" : "rm -rf ~/Library/Caches/ms-playwright && find /System/Volumes/Data/private/var/folders -type d -name 'playwright*' -exec rm -rf {} +" ,
2026-02-27 11:15:12 -08:00
"prepare" : "husky && pnpm turbo build --filter @payloadcms/typescript-plugin" ,
feat!: on demand rsc (#8364)
Currently, Payload renders all custom components on initial compile of
the admin panel. This is problematic for two key reasons:
1. Custom components do not receive contextual data, i.e. fields do not
receive their field data, edit views do not receive their document data,
etc.
2. Components are unnecessarily rendered before they are used
This was initially required to support React Server Components within
the Payload Admin Panel for two key reasons:
1. Fields can be dynamically rendered within arrays, blocks, etc.
2. Documents can be recursively rendered within a "drawer" UI, i.e.
relationship fields
3. Payload supports server/client component composition
In order to achieve this, components need to be rendered on the server
and passed as "slots" to the client. Currently, the pattern for this is
to render custom server components in the "client config". Then when a
view or field is needed to be rendered, we first check the client config
for a "pre-rendered" component, otherwise render our client-side
fallback component.
But for the reasons listed above, this pattern doesn't exactly make
custom server components very useful within the Payload Admin Panel,
which is where this PR comes in. Now, instead of pre-rendering all
components on initial compile, we're able to render custom components
_on demand_, only as they are needed.
To achieve this, we've established [this
pattern](https://github.com/payloadcms/payload/pull/8481) of React
Server Functions in the Payload Admin Panel. With Server Functions, we
can iterate the Payload Config and return JSX through React's
`text/x-component` content-type. This means we're able to pass
contextual props to custom components, such as data for fields and
views.
## Breaking Changes
1. Add the following to your root layout file, typically located at
`(app)/(payload)/layout.tsx`:
```diff
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
+ import type { ServerFunctionClient } from 'payload'
import config from '@payload-config'
import { RootLayout } from '@payloadcms/next/layouts'
import { handleServerFunctions } from '@payloadcms/next/utilities'
import React from 'react'
import { importMap } from './admin/importMap.js'
import './custom.scss'
type Args = {
children: React.ReactNode
}
+ const serverFunctions: ServerFunctionClient = async function (args) {
+ 'use server'
+ return handleServerFunctions({
+ ...args,
+ config,
+ importMap,
+ })
+ }
const Layout = ({ children }: Args) => (
<RootLayout
config={config}
importMap={importMap}
+ serverFunctions={serverFunctions}
>
{children}
</RootLayout>
)
export default Layout
```
2. If you were previously posting to the `/api/form-state` endpoint, it
no longer exists. Instead, you'll need to invoke the `form-state` Server
Function, which can be done through the _new_ `getFormState` utility:
```diff
- import { getFormState } from '@payloadcms/ui'
- const { state } = await getFormState({
- apiRoute: '',
- body: {
- // ...
- },
- serverURL: ''
- })
+ const { getFormState } = useServerFunctions()
+
+ const { state } = await getFormState({
+ // ...
+ })
```
## Breaking Changes
```diff
- useFieldProps()
- useCellProps()
```
More details coming soon.
---------
Co-authored-by: Alessio Gravili <alessio@gravili.de>
Co-authored-by: Jarrod Flesch <jarrodmflesch@gmail.com>
Co-authored-by: James <james@trbl.design>
2024-11-11 13:59:05 -05:00
"prepare-run-test-against-prod" : "pnpm bf && rm -rf test/packed && rm -rf test/node_modules && rm -rf app && rm -f test/pnpm-lock.yaml && pnpm run script:pack --all --no-build --dest test/packed && pnpm runts test/setupProd.ts && cd test && pnpm i --ignore-workspace && cd .." ,
fix: basePath not working properly with admin routes (#14967)
This PR adds a base-path test suite, updates the formatAdminURL helper
function.
The helper function allows you to create routes inside the admin panel
that take `basePath` into account when set.
There are a lot of touched files here, most have the same treatment.
## API URL construction changes
Previously we were appending the basePath in the default api
configuration. That is no longer the case and is more predictable to use
the helper function that will generate a correct path/url.
## Admin URL construction changes
The default admin route differed from the default api route, the default
admin route did not prepend the basePath. The reason it did not/should
not, is because `usePathname` from NextJS excludes basePath.
So if we were to prepend it to `routes.admin` and then for example use
`pathname.startsWith(config.routes.admin)` it would always return false
when using a basePath, since `/route` will never start with
`/basePath/route`.
Also, when you do something like `router.push(/adminRoute)` NextJS will
prepend the basePath and push you to `/basePath/adminRoute`. If we
prepended it in the config it would push you to
`/basePath/basePath/adminRoute`.
## Helper function usage
The helper is especially useful for plugin work, this way your plugin
will generate correct urls for users using basePath.
Before we were doing:
```ts
// worked because we prepended basePath to apiRoute
const apiURL = `${serverURL}${apiRoute}/collections/${slug}/${id}`
// never worked since basePath was never prepended to adminRoute
const adminURL = `${serverURL}${adminRoute}/collections/${slug}/${id}`
```
Now we can do:
```ts
import { formatAdminURL } from 'payload/shared'
// Admin url example
const adminURL = formatAdminURL({
adminRoute: config.routes.admin,
path: `/collections/${slug}/${id}`,
})
// API url example
const apiURL = formatAdminURL({
apiRoute: config.routes.api,
path: `/collections/${slug}/${id}`,
// serverURL, when supplied returns a full url
})
```
If you pass `serverURL` the result will be a full URL. If excluded the
result will be a relative path.
---------
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-12-19 11:13:21 -05:00
"prepare-run-test-against-prod:ci" : "rm -rf test/packed && rm -rf test/node_modules && rm -rf app && rm -f test/pnpm-lock.yaml && pnpm run script:pack --all --no-build --dest test/packed && pnpm runts test/setupProd.ts && cd test && pnpm i --ignore-workspace && cd .." ,
2025-03-05 23:19:01 -05:00
"publish-prerelease" : "pnpm --filter releaser publish-prerelease" ,
2024-02-29 10:38:07 -05:00
"reinstall" : "pnpm clean:all && pnpm install" ,
2025-01-20 11:34:51 -05:00
"release" : "pnpm --filter releaser release --tag latest" ,
2025-05-28 15:41:05 -07:00
"runts" : "cross-env NODE_OPTIONS=\"--no-deprecation --no-experimental-strip-types\" node --no-deprecation --no-experimental-strip-types --import @swc-node/register/esm-register" ,
2025-07-08 11:42:41 -04:00
"script:audit" : "pnpm audit -P" ,
"script:audit:critical" : "pnpm audit -P --audit-level critical" ,
"script:audit:high" : "pnpm audit -P --audit-level high" ,
"script:audit:moderate" : "pnpm audit -P --audit-level moderate" ,
2025-01-20 11:34:51 -05:00
"script:build-template-with-local-pkgs" : "pnpm --filter scripts build-template-with-local-pkgs" ,
"script:gen-templates" : "pnpm --filter scripts gen-templates" ,
2025-03-26 14:52:53 -06:00
"script:gen-templates:build" : "pnpm --filter scripts gen-templates --build" ,
2025-03-25 11:54:00 -04:00
"script:license-check" : "pnpm --filter scripts license-check" ,
2025-01-20 11:34:51 -05:00
"script:list-published" : "pnpm --filter releaser list-published" ,
"script:pack" : "pnpm --filter scripts pack-all-to-dest" ,
2026-01-16 20:19:25 +00:00
"script:setup-figma" : "./scripts/setup-figma-dev.sh" ,
2024-04-25 22:19:37 -04:00
"pretest" : "pnpm build" ,
2023-09-06 01:01:57 +02:00
"test" : "pnpm test:int && pnpm test:components && pnpm test:e2e" ,
feat!: beta-next (#7620)
This PR makes three major changes to the codebase:
1. [Component Paths](#component-paths)
Instead of importing custom components into your config directly, they
are now defined as file paths and rendered only when needed. That way
the Payload config will be significantly more lightweight, and ensures
that the Payload config is 100% server-only and Node-safe. Related
discussion: https://github.com/payloadcms/payload/discussions/6938
2. [Client Config](#client-config)
Deprecates the component map by merging its logic into the client
config. The main goal of this change is for performance and
simplification. There was no need to deeply iterate over the Payload
config twice, once for the component map, and another for the client
config. Instead, we can do everything in the client config one time.
This has also dramatically simplified the client side prop drilling
through the UI library. Now, all components can share the same client
config which matches the exact shape of their Payload config (with the
exception of non-serializable props and mapped custom components).
3. [Custom client component are no longer
server-rendered](#custom-client-components-are-no-longer-server-rendered)
Previously, custom components would be server-rendered, no matter if
they are server or client components. Now, only server components are
rendered on the server. Client components are automatically detected,
and simply get passed through as `MappedComponent` to be rendered fully
client-side.
## Component Paths
Instead of importing custom components into your config directly, they
are now defined as file paths and rendered only when needed. That way
the Payload config will be significantly more lightweight, and ensures
that the Payload config is 100% server-only and Node-safe. Related
discussion: https://github.com/payloadcms/payload/discussions/6938
In order to reference any custom components in the Payload config, you
now have to specify a string path to the component instead of importing
it.
Old:
```ts
import { MyComponent2} from './MyComponent2.js'
admin: {
components: {
Label: MyComponent2
},
},
```
New:
```ts
admin: {
components: {
Label: '/collections/Posts/MyComponent2.js#MyComponent2', // <= has to be a relative path based on a baseDir configured in the Payload config - NOT relative based on the importing file
},
},
```
### Local API within Next.js routes
Previously, if you used the Payload Local API within Next.js pages, all
the client-side modules are being added to the bundle for that specific
page, even if you only need server-side functionality.
This `/test` route, which uses the Payload local API, was previously 460
kb. It is now down to 91 kb and does not bundle the Payload client-side
admin panel anymore.
All tests done
[here](https://github.com/payloadcms/payload-3.0-demo/tree/feat/path-test)
with beta.67/PR, db-mongodb and default richtext-lexical:
**dev /admin before:**

**dev /admin after:**

---
**dev /test before:**

**dev /test after:**

---
**build before:**

**build after::**

### Usage of the Payload Local API / config outside of Next.js
This will make it a lot easier to use the Payload config / local API in
other, server-side contexts. Previously, you might encounter errors due
to client files (like .scss files) not being allowed to be imported.
## Client Config
Deprecates the component map by merging its logic into the client
config. The main goal of this change is for performance and
simplification. There was no need to deeply iterate over the Payload
config twice, once for the component map, and another for the client
config. Instead, we can do everything in the client config one time.
This has also dramatically simplified the client side prop drilling
through the UI library. Now, all components can share the same client
config which matches the exact shape of their Payload config (with the
exception of non-serializable props and mapped custom components).
This is breaking change. The `useComponentMap` hook no longer exists,
and most component props have changed (for the better):
```ts
const { componentMap } = useComponentMap() // old
const { config } = useConfig() // new
```
The `useConfig` hook has also changed in shape, `config` is now a
property _within_ the context obj:
```ts
const config = useConfig() // old
const { config } = useConfig() // new
```
## Custom Client Components are no longer server rendered
Previously, custom components would be server-rendered, no matter if
they are server or client components. Now, only server components are
rendered on the server. Client components are automatically detected,
and simply get passed through as `MappedComponent` to be rendered fully
client-side.
The benefit of this change:
Custom client components can now receive props. Previously, the only way
for them to receive dynamic props from a parent client component was to
use hooks, e.g. `useFieldProps()`. Now, we do have the option of passing
in props to the custom components directly, if they are client
components. This will be simpler than having to look for the correct
hook.
This makes rendering them on the client a little bit more complex, as
you now have to check if that component is a server component (=>
already has been rendered) or a client component (=> not rendered yet,
has to be rendered here). However, this added complexity has been
alleviated through the easy-to-use `<RenderMappedComponent />` helper.
This helper now also handles rendering arrays of custom components (e.g.
beforeList, beforeLogin ...), which actually makes rendering custom
components easier in some cases.
## Misc improvements
This PR includes misc, breaking changes. For example, we previously
allowed unions between components and config object for the same
property. E.g. for the custom view property, you were allowed to pass in
a custom component or an object with other properties, alongside a
custom component.
Those union types are now gone. You can now either pass an object, or a
component. The previous `{ View: MyViewComponent}` is now `{ View: {
Component: MyViewComponent} }` or `{ View: { Default: { Component:
MyViewComponent} } }`.
This dramatically simplifies the way we read & process those properties,
especially in buildComponentMap. We can now simply check for the
existence of one specific property, which always has to be a component,
instead of running cursed runtime checks on a shared union property
which could contain a component, but could also contain functions or
objects.


- [x] I have read and understand the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository.
---------
Co-authored-by: PatrikKozak <patrik@payloadcms.com>
Co-authored-by: Paul <paul@payloadcms.com>
Co-authored-by: Paul Popus <paul@nouance.io>
Co-authored-by: Jacob Fletcher <jacobsfletch@gmail.com>
Co-authored-by: James <james@trbl.design>
2024-08-13 12:54:33 -04:00
"test:e2e" : "pnpm runts ./test/runE2E.ts" ,
2025-05-28 15:41:05 -07:00
"test:e2e:debug" : "cross-env NODE_OPTIONS=\"--no-deprecation --no-experimental-strip-types\" NODE_NO_WARNINGS=1 PWDEBUG=1 DISABLE_LOGGING=true playwright test" ,
"test:e2e:headed" : "cross-env NODE_OPTIONS=\"--no-deprecation --no-experimental-strip-types\" NODE_NO_WARNINGS=1 DISABLE_LOGGING=true playwright test --headed" ,
2025-06-05 18:01:55 -07:00
"test:e2e:noturbo" : "pnpm runts ./test/runE2E.ts --no-turbo" ,
ci: cache e2e prod preparation to run once instead of per-shard, various improvements and flake fixes (#15368)
Moves `prepare-run-test-against-prod:ci` into a dedicated `e2e-prep` job
that runs once and caches the result. Previously this 2-minute step ran
for every E2E shard.
Saves ~2 minutes per shard (~140+ minutes total across all E2E jobs).
Additionally, this entails a few other test suite improvements and
flakes fixes.
All together, **this reduces the amount of time our entire CI takes to
run from > 1 hour if you factor in manual retries, to about 15
minutes.**
- about 2x faster `waitForCompilation` checking, by checking more
frequently and without waiting for the load event. This previously took
about 2 seconds in CI, often running before every single test. After
`waitForCompilation` we usually navigate to a different page, so waiting
for a full page load was a waste of time.
- Reduce test timeouts. Quicker failures => quicker retries => quicker
test suite passes. The previous config was excessive
- Introduces a shorter navigation timeout. By default, it used to be
equal to `TEST_TIMEOUT`. `TEST_TIMEOUT` is high to accommodate long e2e
tests with multiple steps, not to allow page.goto to try for 3 minutes
before it fails.
- Reduces console spam due to `waitForCompilation`
- Reduce retries of `waitForCompilation`. If the dev server fails for
some reason, we shouldn't have to wait 50 (!!) minutes until the e2e
test finally fails and is ready to be retried
- Lots of flake fixes for individual tests and test suites
- Fixes flaky behavior of checkbox component. This **fixes flakiness
EVERYWHERE any checkbox is clicked**.
- Fixes upload directory clearing and seed restoration not working
correctly if there were multiple upload dirs
2026-01-28 11:44:52 -08:00
"test:e2e:prod" : "pnpm prepare-run-test-against-prod && pnpm test:e2e:prod:run" ,
"test:e2e:prod:ci" : "pnpm prepare-run-test-against-prod:ci && pnpm test:e2e:prod:run" ,
"test:e2e:prod:ci:noturbo" : "pnpm prepare-run-test-against-prod:ci && pnpm test:e2e:prod:run:noturbo" ,
"test:e2e:prod:run" : "pnpm runts ./test/runE2E.ts --prod" ,
"test:e2e:prod:run:noturbo" : "pnpm runts ./test/runE2E.ts --prod --no-turbo" ,
2025-12-19 23:07:14 +02:00
"test:int" : "cross-env NODE_OPTIONS=\"--no-deprecation --no-experimental-strip-types\" NODE_NO_WARNINGS=1 DISABLE_LOGGING=true vitest --project int" ,
"test:int:firestore" : "cross-env NODE_OPTIONS=\"--no-deprecation --no-experimental-strip-types\" NODE_NO_WARNINGS=1 PAYLOAD_DATABASE=firestore DISABLE_LOGGING=true vitest --project int" ,
"test:int:postgres" : "cross-env NODE_OPTIONS=\"--no-deprecation --no-experimental-strip-types\" NODE_NO_WARNINGS=1 PAYLOAD_DATABASE=postgres DISABLE_LOGGING=true vitest --project int" ,
"test:int:sqlite" : "cross-env NODE_OPTIONS=\"--no-deprecation --no-experimental-strip-types\" NODE_NO_WARNINGS=1 PAYLOAD_DATABASE=sqlite DISABLE_LOGGING=true vitest --project int" ,
2026-01-16 20:19:25 +00:00
"test:int:summary" : "pnpm runts ./test/runTestsWithSummary.ts" ,
2024-12-07 16:38:25 +02:00
"test:types" : "tstyche" ,
2025-12-19 23:07:14 +02:00
"test:unit" : "vitest run --project unit" ,
2025-06-16 04:58:03 -07:00
"translateNewKeys" : "pnpm --filter @tools/scripts run generateTranslations:core"
2023-09-01 17:39:44 +02:00
} ,
2024-04-25 22:19:37 -04:00
"lint-staged" : {
2024-04-25 22:23:58 -04:00
"**/package.json" : "sort-package-json" ,
2024-04-25 22:19:37 -04:00
"*.{md,mdx,yml,json}" : "prettier --write" ,
"*.{js,jsx,ts,tsx}" : [
2026-02-04 15:39:50 -08:00
"eslint --flag v10_config_lookup_from_file --cache --fix" ,
2026-02-02 09:44:31 -08:00
"prettier --write"
2024-04-29 23:19:06 -04:00
] ,
2024-11-25 10:04:41 -05:00
"templates/**/pnpm-lock.yaml" : "pnpm runts scripts/remove-template-lock-files.ts" ,
2025-10-23 10:01:48 -04:00
"tsconfig.base.json" : "node scripts/reset-tsconfig.js" ,
"README.md" : "sh -c 'cp ./README.md ./packages/payload/README.md'"
2024-04-25 22:19:37 -04:00
} ,
2023-09-06 01:01:57 +02:00
"devDependencies" : {
2025-12-01 10:37:31 -06:00
"@axe-core/playwright" : "4.11.0" ,
2024-10-15 16:20:50 -04:00
"@libsql/client" : "0.14.0" ,
2026-02-20 13:12:29 -08:00
"@next/bundle-analyzer" : "16.2.0-canary.54" ,
2024-08-16 15:46:49 -04:00
"@payloadcms/db-postgres" : "workspace:*" ,
2023-09-06 01:01:57 +02:00
"@payloadcms/eslint-config" : "workspace:*" ,
2024-07-09 09:50:37 -04:00
"@payloadcms/eslint-plugin" : "workspace:*" ,
2024-03-22 12:38:55 -04:00
"@payloadcms/live-preview-react" : "workspace:*" ,
2026-02-27 11:15:12 -08:00
"@payloadcms/typescript-plugin" : "workspace:*" ,
2026-02-13 10:10:54 -08:00
"@playwright/test" : "1.58.2" ,
feat(plugin-sentry): update plugin to 3.0 (#8613)
Updates the plugin to 3.0
Test:
```sh
NEXT_PUBLIC_SENTRY_DSN=<DSN here> pnpm dev plugin-sentry
```
Example:
```ts
sentryPlugin({
options: {
captureErrors: [400, 403],
context: ({ defaultContext, req }) => {
return {
...defaultContext,
tags: {
locale: req.locale,
},
}
},
debug: true,
},
Sentry,
})
```
2024-10-09 21:26:58 +03:00
"@sentry/nextjs" : "^8.33.1" ,
"@sentry/node" : "^8.33.1" ,
2025-12-05 17:50:58 -08:00
"@swc-node/register" : "1.11.1" ,
"@swc/cli" : "0.7.9" ,
"@swc/core" : "1.15.3" ,
2023-10-08 13:17:50 -04:00
"@types/fs-extra" : "^11.0.2" ,
2024-04-06 15:35:09 -04:00
"@types/minimist" : "1.2.5" ,
2025-06-11 13:59:19 -07:00
"@types/node" : "22.15.30" ,
2026-01-27 09:53:18 -08:00
"@types/react" : "19.2.9" ,
2026-01-20 13:58:30 -08:00
"@types/react-dom" : "19.2.3" ,
2024-04-06 15:35:09 -04:00
"@types/shelljs" : "0.8.15" ,
2025-12-01 10:37:31 -06:00
"axe-core" : "4.11.0" ,
2024-03-03 13:01:59 -05:00
"chalk" : "^4.1.2" ,
2024-03-04 16:55:43 -05:00
"comment-json" : "^4.2.3" ,
2023-09-06 01:01:57 +02:00
"copyfiles" : "2.4.1" ,
2024-06-05 15:39:28 -04:00
"create-payload-app" : "workspace:*" ,
2023-09-06 01:01:57 +02:00
"cross-env" : "7.0.3" ,
2025-01-18 04:08:12 -07:00
"dotenv" : "16.4.7" ,
2025-11-20 15:37:35 -08:00
"drizzle-kit" : "0.31.7" ,
"drizzle-orm" : "0.44.7" ,
2025-06-16 23:03:18 +03:00
"escape-html" : "^1.0.3" ,
2026-02-04 15:39:50 -08:00
"eslint" : "9.39.2" ,
2024-02-29 16:01:51 -05:00
"execa" : "5.1.1" ,
2026-01-30 11:03:02 -05:00
"form-data" : "3.0.4" ,
2023-10-08 13:17:50 -04:00
"fs-extra" : "10.1.0" ,
2024-03-16 06:52:55 -04:00
"globby" : "11.1.0" ,
2024-07-09 09:50:37 -04:00
"husky" : "9.0.11" ,
"lint-staged" : "15.2.7" ,
2023-10-08 13:17:50 -04:00
"minimist" : "1.2.8" ,
2025-11-07 11:42:21 -08:00
"mongoose" : "8.15.1" ,
2026-02-20 13:12:29 -08:00
"next" : "16.2.0-canary.54" ,
2026-01-28 14:05:26 -05:00
"node-gyp" : "12.2.0" ,
2024-03-21 11:39:27 -04:00
"open" : "^10.1.0" ,
2024-04-08 10:29:26 -04:00
"p-limit" : "^5.0.0" ,
2025-07-02 09:24:53 -07:00
"pg" : "8.16.3" ,
2026-01-06 05:23:32 +11:00
"postcss" : "^8.4.49" ,
"postcss-scss" : "^4.0.9" ,
2025-03-14 11:13:08 -06:00
"prettier" : "3.5.3" ,
2026-01-27 09:53:18 -08:00
"react" : "19.2.4" ,
"react-dom" : "19.2.4" ,
2024-12-27 09:16:17 -05:00
"rimraf" : "6.0.1" ,
2024-03-05 16:12:07 -05:00
"sharp" : "0.32.6" ,
2023-09-07 23:06:04 +02:00
"shelljs" : "0.8.5" ,
2023-10-06 17:38:33 -04:00
"slash" : "3.0.0" ,
2024-04-25 22:19:37 -04:00
"sort-package-json" : "^2.10.0" ,
2026-01-06 05:23:32 +11:00
"stylelint" : "^16.12.0" ,
2025-12-05 17:50:58 -08:00
"swc-plugin-transform-remove-imports" : "8.3.0" ,
2024-07-11 14:33:45 -04:00
"tempy" : "1.0.1" ,
feat(richtext-lexical): utility render lexical field on-demand (#13657)
## Why this exists
Lexical in Payload is a React Server Component (RSC). Historically that
created three headaches:
1. You couldn’t render the editor directly from the client.
2. Features like blocks, tables, upload and link drawers require the
server to know the shape of nested sub‑fields at render time. If you
tried to render on demand, the server didn’t know those schemas.
3. The rich text field is designed to live inside a Form. For simple use
cases, setting up a full form just to manage editor state was
cumbersome.
## What’s new
We now ship a client component, `<RenderLexical />`, that renders a
Lexical editor **on demand** while still covering the full feature set.
On mount, it calls a server action to render the editor on the server
using the new `render-field` server action. That server render gives
Lexical everything it needs (including nested field schemas) and returns
a ready‑to‑hydrate editor.
## Example - Rendering in custom component within existing Form
```tsx
'use client'
import type { JSONFieldClientComponent } from 'payload'
import { buildEditorState, RenderLexical } from '@payloadcms/richtext-lexical/client'
import { lexicalFullyFeaturedSlug } from '../../slugs.js'
export const Component: JSONFieldClientComponent = (args) => {
return (
<div>
Fully-Featured Component:
<RenderLexical
field={{ name: 'json' }}
initialValue={buildEditorState({ text: 'defaultValue' })}
schemaPath={`collection.${lexicalFullyFeaturedSlug}.richText`}
/>
</div>
)
}
```
## Example - Rendering outside of Form, manually managing richText
values
```ts
'use client'
import type { DefaultTypedEditorState } from '@payloadcms/richtext-lexical'
import type { JSONFieldClientComponent } from 'payload'
import { buildEditorState, RenderLexical } from '@payloadcms/richtext-lexical/client'
import React, { useState } from 'react'
import { lexicalFullyFeaturedSlug } from '../../slugs.js'
export const Component: JSONFieldClientComponent = (args) => {
const [value, setValue] = useState<DefaultTypedEditorState | undefined>(() =>
buildEditorState({ text: 'state default' }),
)
const handleReset = React.useCallback(() => {
setValue(buildEditorState({ text: 'state default' }))
}, [])
return (
<div>
Default Component:
<RenderLexical
field={{ name: 'json' }}
initialValue={buildEditorState({ text: 'defaultValue' })}
schemaPath={`collection.${lexicalFullyFeaturedSlug}.richText`}
setValue={setValue as any}
value={value}
/>
<button onClick={handleReset} style={{ marginTop: 8 }} type="button">
Reset Editor State
</button>
</div>
)
}
```
## How it works (under the hood)
- On first render, `<RenderLexical />` calls the server function
`render-field` (wired into @payloadcms/next), passing a schemaPath.
- The server loads the exact field config and its client schema map for
that path, renders the Lexical editor server‑side (so nested features
like blocks/tables/relationships are fully known), and returns the
component tree.
- While waiting, the client shows a small shimmer skeleton.
- Inside Forms, RenderLexical plugs into the parent form via useField;
outside Forms, you can fully control the value by passing
value/setValue.
## Type Improvements
While implementing the `buildEditorState` helper function for our test
suite, I noticed some issues with our `TypedEditorState` type:
- nodes were no longer narrowed by their node.type types
- upon fixing this issue, the type was no longer compatible with the
generated types. To address this, I had to weaken the generated type a
bit.
In order to ensure the type will keep functioning as intended from now
on, this PR also adds some type tests
---
- To see the specific tasks where the Asana app for GitHub is being
used, see below:
- https://app.asana.com/0/0/1211110462564644
2025-09-18 15:01:12 -07:00
"tstyche" : "3.5.0" ,
2026-01-26 14:35:14 -08:00
"tsx" : "4.21.0" ,
2025-06-26 10:55:28 -07:00
"turbo" : "^2.5.4" ,
2025-10-09 21:24:44 +03:00
"typescript" : "5.7.3" ,
2025-12-19 23:07:14 +02:00
"vitest" : "4.0.15" ,
2026-01-30 11:03:02 -05:00
"wrangler" : "~4.61.1"
2023-09-06 01:01:57 +02:00
} ,
2026-01-09 13:15:16 -08:00
"packageManager" : "pnpm@10.27.0" ,
2023-09-14 16:32:37 -04:00
"engines" : {
2024-06-06 13:15:21 -04:00
"node" : "^18.20.2 || >=20.9.0" ,
2026-01-09 13:15:16 -08:00
"pnpm" : "^10.27.0"
2023-10-02 14:02:34 -04:00
} ,
2024-02-06 13:42:52 -05:00
"pnpm" : {
2026-01-09 13:15:16 -08:00
"onlyBuiltDependencies" : [
2026-01-20 13:58:30 -08:00
"@parcel/watcher" ,
2026-01-09 13:15:16 -08:00
"@sentry/cli" ,
"@swc/core" ,
"@vercel/git-hooks" ,
"better-sqlite3" ,
"bufferutil" ,
"esbuild" ,
"mongodb-memory-server" ,
"sharp" ,
"unrs-resolver" ,
"utf-8-validate" ,
"workerd"
] ,
2024-02-06 13:42:52 -05:00
"overrides" : {
2024-02-28 14:51:24 -05:00
"copyfiles" : "$copyfiles" ,
"cross-env" : "$cross-env" ,
"dotenv" : "$dotenv" ,
2025-12-19 23:07:14 +02:00
"graphql" : "16.8.1" ,
2024-02-28 14:51:24 -05:00
"react" : "$react" ,
"react-dom" : "$react-dom" ,
2024-03-09 14:41:00 -05:00
"typescript" : "$typescript"
2024-02-06 13:42:52 -05:00
}
2025-06-10 19:03:59 -04:00
}
2023-09-01 21:57:59 +02:00
}