Compare commits

..

7 Commits

Author SHA1 Message Date
Germán Jabloñski
b7fea7a7ab change folder names 2024-12-10 12:18:03 -03:00
Germán Jabloñski
e5564a18e6 change folder names 2024-12-10 12:14:52 -03:00
Germán Jabloñski
d0f1cda6e4 Merge remote-tracking branch 'origin/main' into v2-docs-excluded 2024-12-10 12:13:34 -03:00
Germán Jabloñski
033e528ee6 improve contributing.md 2024-12-10 10:53:51 -03:00
Germán Jabloñski
2a038a4664 change yarn with pnpm in contributing to docs 2024-12-10 08:56:33 -03:00
Germán Jabloñski
b32a070c70 Merge remote-tracking branch 'origin/main' into v2_docs 2024-12-10 08:51:53 -03:00
Germán Jabloñski
54a56f8bf0 add v2 docs to main branch 2024-12-10 08:50:37 -03:00
4645 changed files with 159352 additions and 357841 deletions

41
.github/CODEOWNERS vendored
View File

@@ -1,34 +1,29 @@
# Order matters. The last matching pattern takes precedence
# Order matters. The last matching pattern takes precedence.
# Approvals are not required currently but may be enabled in the future.
## Package Exports
### Package Exports ###
/**/exports/ @denolfe @jmikrut @DanRibbens
**/exports/ @denolfe @DanRibbens
## Packages
/packages/create-payload-app/src/ @denolfe
### Packages ###
/packages/plugin-cloud*/src/ @denolfe
/packages/email-*/src/ @denolfe
/packages/eslint-*/ @denolfe @AlessioGr
/packages/plugin-cloud-storage/src/ @denolfe
/packages/plugin-multi-tenant/src/ @JarrodMFlesch
/packages/richtext-*/src/ @AlessioGr
/packages/storage-*/src/ @denolfe
/packages/ui/src/ @jacobsfletch @AlessioGr @JarrodMFlesch
## Templates
/packages/create-payload-app/src/ @denolfe
/packages/eslint-*/ @denolfe @AlessioGr
### Templates ###
/templates/_data/ @denolfe
/templates/_template/ @denolfe
## Build Files
### Build Files ###
/tsconfig.json @denolfe
/**/tsconfig*.json @denolfe
/jest.config.js @denolfe
/**/jest.config.js @denolfe
**/jest.config.js @denolfe @AlessioGr
**/tsconfig*.json @denolfe @AlessioGr
## Root
/.github/ @denolfe
### Root ###
/package.json @denolfe
/scripts/ @denolfe
/.husky/ @denolfe
/.vscode/ @denolfe @AlessioGr
/package.json @denolfe
/tools/ @denolfe
/.github/ @denolfe

View File

@@ -43,7 +43,6 @@ body:
- 'plugin: cloud'
- 'plugin: cloud-storage'
- 'plugin: form-builder'
- 'plugin: multi-tenant'
- 'plugin: nested-docs'
- 'plugin: richtext-lexical'
- 'plugin: richtext-slate'
@@ -58,9 +57,12 @@ body:
- type: textarea
attributes:
label: Environment Info
description: Paste output from `pnpm payload info` _or_ Payload, Node.js, and Next.js versions. Please avoid using "latest"—specific version numbers help us accurately diagnose and resolve issues.
description: Paste output from `pnpm payload info` _or_ Payload, Node.js, and Next.js versions.
render: text
placeholder: Run `pnpm payload info` in your terminal and paste the output here.
placeholder: |
Payload:
Node.js:
Next.js:
validations:
required: true

View File

@@ -20,7 +20,7 @@ body:
- type: textarea
attributes:
label: Environment Info
description: Paste output from `pnpm payload info` _or_ Payload, Node.js, and Next.js versions. Please avoid using "latest"—specific version numbers help us accurately diagnose and resolve issues.
description: Paste output from `pnpm payload info` _or_ Payload, Node.js, and Next.js versions.
render: text
placeholder: |
Payload:

View File

@@ -1,27 +0,0 @@
MIT License
Copyright (c) 2020-2025 Cameron Little <cameron@camlittle.com>
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
'Software'), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
---
Modifications made by Payload CMS, Inc. <info@payloadcms.com>, 2025
Details in README.md

View File

@@ -4,7 +4,6 @@
"private": true,
"description": "GitHub Action to automatically comment on PRs and Issues when a fix is released.",
"license": "MIT",
"author": "Payload <dev@payloadcms.com> (https://payloadcms.com)",
"main": "dist/index.js",
"scripts": {
"build": "pnpm build:typecheck && pnpm build:ncc",
@@ -29,6 +28,7 @@
"eslint": "^7.32.0",
"jest": "^29.7.0",
"prettier": "^3.3.3",
"ts-jest": "^26.5.6",
"typescript": "^4.9.5"
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{
"compilerOptions": {
"target": "es5",
"target": "ES2022",
"lib": ["es2020.string"],
"noEmit": true,
"strict": true,

View File

@@ -4,17 +4,24 @@ description: |
inputs:
node-version:
description: Node.js version override
description: Node.js version
required: true
default: 22.6.0
pnpm-version:
description: Pnpm version override
description: Pnpm version
required: true
default: 9.7.1
pnpm-run-install:
description: Whether to run pnpm install
required: false
default: true
pnpm-restore-cache:
description: Whether to restore cache
required: false
default: true
pnpm-install-cache-key:
description: The cache key override for the pnpm install cache
description: The cache key for the pnpm install cache
default: pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
outputs:
pnpm-store-path:
@@ -30,44 +37,15 @@ runs:
shell: bash
run: sudo ethtool -K eth0 tx off rx off
- name: Get versions from .tool-versions or use overrides
shell: bash
run: |
# if node-version input is provided, use it; otherwise, read from .tool-versions
if [ "${{ inputs.node-version }}" ]; then
echo "Node version override provided: ${{ inputs.node-version }}"
echo "NODE_VERSION=${{ inputs.node-version }}" >> $GITHUB_ENV
elif [ -f .tool-versions ]; then
NODE_VERSION=$(grep '^nodejs ' .tool-versions | awk '{print $2}')
echo "NODE_VERSION=$NODE_VERSION" >> $GITHUB_ENV
echo "Node version resolved to: $NODE_VERSION"
else
echo "No .tool-versions file found and no node-version input provided. Invalid configuration."
exit 1
fi
# if pnpm-version input is provided, use it; otherwise, read from .tool-versions
if [ "${{ inputs.pnpm-version }}" ]; then
echo "Pnpm version override provided: ${{ inputs.pnpm-version }}"
echo "PNPM_VERSION=${{ inputs.pnpm-version }}" >> $GITHUB_ENV
elif [ -f .tool-versions ]; then
PNPM_VERSION=$(grep '^pnpm ' .tool-versions | awk '{print $2}')
echo "PNPM_VERSION=$PNPM_VERSION" >> $GITHUB_ENV
echo "Pnpm version resolved to: $PNPM_VERSION"
else
echo "No .tool-versions file found and no pnpm-version input provided. Invalid configuration."
exit 1
fi
- name: Setup Node@${{ inputs.node-version }}
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
node-version: ${{ inputs.node-version }}
- name: Install pnpm
uses: pnpm/action-setup@v4
with:
version: ${{ env.PNPM_VERSION }}
version: ${{ inputs.pnpm-version }}
run_install: false
- name: Get pnpm store path
@@ -77,25 +55,14 @@ runs:
echo "STORE_PATH=$STORE_PATH" >> $GITHUB_ENV
echo "Pnpm store path resolved to: $STORE_PATH"
- name: Compute Cache Key
shell: bash
run: |
if [ -n "${{ inputs.pnpm-install-cache-key }}" ]; then
PNPM_INSTALL_CACHE_KEY="${{ inputs.pnpm-install-cache-key }}"
else
PNPM_INSTALL_CACHE_KEY="pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}"
fi
echo "Computed PNPM_INSTALL_CACHE_KEY: $PNPM_INSTALL_CACHE_KEY"
echo "PNPM_INSTALL_CACHE_KEY=$PNPM_INSTALL_CACHE_KEY" >> $GITHUB_ENV
- name: Restore pnpm install cache
if: ${{ inputs.pnpm-restore-cache == 'true' }}
uses: actions/cache@v4
with:
path: ${{ env.STORE_PATH }}
key: ${{ env.PNPM_INSTALL_CACHE_KEY }}
key: ${{ inputs.pnpm-install-cache-key }}
restore-keys: |
pnpm-store-${{ env.PNPM_VERSION }}-
pnpm-store-${{ inputs.pnpm-version }}-
pnpm-store-
- name: Run pnpm install
@@ -105,5 +72,5 @@ runs:
# Set the cache key output
- run: |
echo "pnpm-install-cache-key=${{ env.PNPM_INSTALL_CACHE_KEY }}" >> $GITHUB_OUTPUT
echo "pnpm-install-cache-key=${{ inputs.pnpm-install-cache-key }}" >> $GITHUB_ENV
shell: bash

View File

@@ -18,7 +18,7 @@
},
"devDependencies": {
"@octokit/webhooks-types": "^7.5.1",
"@swc/jest": "^0.2.37",
"@swc/jest": "^0.2.36",
"@types/jest": "^27.5.2",
"@types/node": "^20.16.5",
"@typescript-eslint/eslint-plugin": "^4.33.0",
@@ -28,6 +28,7 @@
"eslint": "^7.32.0",
"jest": "^29.7.0",
"prettier": "^3.3.3",
"ts-jest": "^26.5.6",
"typescript": "^4.9.5"
}
}

3648
.github/actions/triage/pnpm-lock.yaml generated vendored

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{
"compilerOptions": {
"target": "es5",
"target": "ES2022",
"lib": ["es2020.string"],
"noEmit": true,
"strict": true,

View File

@@ -4,7 +4,7 @@ Depending on the quality of reproduction steps, this issue may be closed if no r
### Why was this issue marked with the `invalid-reproduction` label?
To be able to investigate, we need access to a reproduction to identify what triggered the issue. We prefer a link to a public GitHub repository created with `create-payload-app@latest -t blank` or a forked/branched version of this repository with tests added (more info in the [reproduction-guide](https://github.com/payloadcms/payload/blob/main/.github/reproduction-guide.md)).
To be able to investigate, we need access to a reproduction to identify what triggered the issue. We prefer a link to a public GitHub repository created with `create-payload-app@beta -t blank` or a forked/branched version of this repository with tests added (more info in the [reproduction-guide](https://github.com/payloadcms/payload/blob/main/.github/reproduction-guide.md)).
To make sure the issue is resolved as quickly as possible, please make sure that the reproduction is as **minimal** as possible. This means that you should **remove unnecessary code, files, and dependencies** that do not contribute to the issue. Ensure your reproduction does not depend on secrets, 3rd party registries, private dependencies, or any other data that cannot be made public. Avoid a reproduction including a whole monorepo (unless relevant to the issue). The easier it is to reproduce the issue, the quicker we can help.

114
.github/pnpm-lock.yaml generated vendored
View File

@@ -19,8 +19,8 @@ importers:
specifier: ^7.5.1
version: 7.5.1
'@swc/jest':
specifier: ^0.2.37
version: 0.2.37(@swc/core@1.7.26)
specifier: ^0.2.36
version: 0.2.36(@swc/core@1.7.26)
'@types/jest':
specifier: ^27.5.2
version: 27.5.2
@@ -48,6 +48,9 @@ importers:
prettier:
specifier: ^3.3.3
version: 3.3.3
ts-jest:
specifier: ^26.5.6
version: 26.5.6(jest@29.7.0(@types/node@20.16.5))(typescript@4.9.5)
typescript:
specifier: ^4.9.5
version: 4.9.5
@@ -65,8 +68,8 @@ importers:
specifier: ^7.5.1
version: 7.5.1
'@swc/jest':
specifier: ^0.2.37
version: 0.2.37(@swc/core@1.7.26)
specifier: ^0.2.36
version: 0.2.36(@swc/core@1.7.26)
'@types/jest':
specifier: ^27.5.2
version: 27.5.2
@@ -94,6 +97,9 @@ importers:
prettier:
specifier: ^3.3.3
version: 3.3.3
ts-jest:
specifier: ^26.5.6
version: 26.5.6(jest@29.7.0(@types/node@20.16.5))(typescript@4.9.5)
typescript:
specifier: ^4.9.5
version: 4.9.5
@@ -380,6 +386,10 @@ packages:
resolution: {integrity: sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
'@jest/types@26.6.2':
resolution: {integrity: sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==}
engines: {node: '>= 10.14.2'}
'@jest/types@29.6.3':
resolution: {integrity: sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
@@ -532,8 +542,8 @@ packages:
'@swc/counter@0.1.3':
resolution: {integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==}
'@swc/jest@0.2.37':
resolution: {integrity: sha512-CR2BHhmXKGxTiFr21DYPRHQunLkX3mNIFGFkxBGji6r9uyIR5zftTOVYj1e0sFNMV2H7mf/+vpaglqaryBtqfQ==}
'@swc/jest@0.2.36':
resolution: {integrity: sha512-8X80dp81ugxs4a11z1ka43FPhP+/e+mJNXJSxiNYk8gIX/jPBtY4gQTrKu/KIoco8bzKuPI5lUxjfLiGsfvnlw==}
engines: {npm: '>= 7.0.0'}
peerDependencies:
'@swc/core': '*'
@@ -580,6 +590,9 @@ packages:
'@types/yargs-parser@21.0.3':
resolution: {integrity: sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==}
'@types/yargs@15.0.19':
resolution: {integrity: sha512-2XUaGVmyQjgyAZldf0D0c14vvo/yv0MhQBSTJcejMMaitsn3nxCB6TmH4G0ZQf+uxROOa9mpanoSm8h6SG/1ZA==}
'@types/yargs@17.0.33':
resolution: {integrity: sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==}
@@ -733,6 +746,10 @@ packages:
engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
hasBin: true
bs-logger@0.2.6:
resolution: {integrity: sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==}
engines: {node: '>= 6'}
bser@2.1.1:
resolution: {integrity: sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==}
@@ -766,6 +783,9 @@ packages:
resolution: {integrity: sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==}
engines: {node: '>=10'}
ci-info@2.0.0:
resolution: {integrity: sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==}
ci-info@3.9.0:
resolution: {integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==}
engines: {node: '>=8'}
@@ -1113,6 +1133,10 @@ packages:
is-arrayish@0.2.1:
resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==}
is-ci@2.0.0:
resolution: {integrity: sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==}
hasBin: true
is-core-module@2.15.1:
resolution: {integrity: sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==}
engines: {node: '>= 0.4'}
@@ -1287,6 +1311,10 @@ packages:
resolution: {integrity: sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
jest-util@26.6.2:
resolution: {integrity: sha512-MDW0fKfsn0OI7MS7Euz6h8HNDXVQ0gaM9uW6RjfDmd1DAFcaxX9OqIakHIqhbnmF08Cf2DLDG+ulq8YQQ0Lp0Q==}
engines: {node: '>= 10.14.2'}
jest-util@29.7.0:
resolution: {integrity: sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
@@ -1386,6 +1414,9 @@ packages:
resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==}
engines: {node: '>=10'}
make-error@1.3.6:
resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==}
makeerror@1.0.12:
resolution: {integrity: sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==}
@@ -1407,6 +1438,11 @@ packages:
minimatch@3.1.2:
resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==}
mkdirp@1.0.4:
resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==}
engines: {node: '>=10'}
hasBin: true
ms@2.1.3:
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
@@ -1716,6 +1752,14 @@ packages:
resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==}
hasBin: true
ts-jest@26.5.6:
resolution: {integrity: sha512-rua+rCP8DxpA8b4DQD/6X2HQS8Zy/xzViVYfEs2OQu68tkCuKLV0Md8pmX55+W24uRIyAsf/BajRfxOs+R2MKA==}
engines: {node: '>= 10'}
hasBin: true
peerDependencies:
jest: '>=26 <27'
typescript: '>=3.8 <5.0'
tslib@1.14.1:
resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==}
@@ -1819,6 +1863,10 @@ packages:
yallist@3.1.1:
resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==}
yargs-parser@20.2.9:
resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==}
engines: {node: '>=10'}
yargs-parser@21.1.1:
resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==}
engines: {node: '>=12'}
@@ -2259,6 +2307,14 @@ snapshots:
transitivePeerDependencies:
- supports-color
'@jest/types@26.6.2':
dependencies:
'@types/istanbul-lib-coverage': 2.0.6
'@types/istanbul-reports': 3.0.4
'@types/node': 20.16.5
'@types/yargs': 15.0.19
chalk: 4.1.2
'@jest/types@29.6.3':
dependencies:
'@jest/schemas': 29.6.3
@@ -2421,7 +2477,7 @@ snapshots:
'@swc/counter@0.1.3': {}
'@swc/jest@0.2.37(@swc/core@1.7.26)':
'@swc/jest@0.2.36(@swc/core@1.7.26)':
dependencies:
'@jest/create-cache-key-function': 29.7.0
'@swc/core': 1.7.26
@@ -2482,6 +2538,10 @@ snapshots:
'@types/yargs-parser@21.0.3': {}
'@types/yargs@15.0.19':
dependencies:
'@types/yargs-parser': 21.0.3
'@types/yargs@17.0.33':
dependencies:
'@types/yargs-parser': 21.0.3
@@ -2682,6 +2742,10 @@ snapshots:
node-releases: 2.0.18
update-browserslist-db: 1.1.0(browserslist@4.23.3)
bs-logger@0.2.6:
dependencies:
fast-json-stable-stringify: 2.1.0
bser@2.1.1:
dependencies:
node-int64: 0.4.0
@@ -2709,6 +2773,8 @@ snapshots:
char-regex@1.0.2: {}
ci-info@2.0.0: {}
ci-info@3.9.0: {}
cjs-module-lexer@1.4.1: {}
@@ -3061,6 +3127,10 @@ snapshots:
is-arrayish@0.2.1: {}
is-ci@2.0.0:
dependencies:
ci-info: 2.0.0
is-core-module@2.15.1:
dependencies:
hasown: 2.0.2
@@ -3400,6 +3470,15 @@ snapshots:
transitivePeerDependencies:
- supports-color
jest-util@26.6.2:
dependencies:
'@jest/types': 26.6.2
'@types/node': 20.16.5
chalk: 4.1.2
graceful-fs: 4.2.11
is-ci: 2.0.0
micromatch: 4.0.8
jest-util@29.7.0:
dependencies:
'@jest/types': 29.6.3
@@ -3504,6 +3583,8 @@ snapshots:
dependencies:
semver: 7.6.3
make-error@1.3.6: {}
makeerror@1.0.12:
dependencies:
tmpl: 1.0.5
@@ -3523,6 +3604,8 @@ snapshots:
dependencies:
brace-expansion: 1.1.11
mkdirp@1.0.4: {}
ms@2.1.3: {}
natural-compare@1.4.0: {}
@@ -3776,6 +3859,21 @@ snapshots:
tree-kill@1.2.2: {}
ts-jest@26.5.6(jest@29.7.0(@types/node@20.16.5))(typescript@4.9.5):
dependencies:
bs-logger: 0.2.6
buffer-from: 1.1.2
fast-json-stable-stringify: 2.1.0
jest: 29.7.0(@types/node@20.16.5)
jest-util: 26.6.2
json5: 2.2.3
lodash: 4.17.21
make-error: 1.3.6
mkdirp: 1.0.4
semver: 7.6.3
typescript: 4.9.5
yargs-parser: 20.2.9
tslib@1.14.1: {}
tslib@2.7.0: {}
@@ -3861,6 +3959,8 @@ snapshots:
yallist@3.1.1: {}
yargs-parser@20.2.9: {}
yargs-parser@21.1.1: {}
yargs@17.7.2:

View File

@@ -40,7 +40,7 @@ There are a couple ways run integration tests:
- **Granularly** - you can run individual tests in vscode by installing the Jest Runner plugin and using that to run individual tests. Clicking the `debug` button will run the test in debug mode allowing you to set break points.
<img src="https://raw.githubusercontent.com/payloadcms/payload/main/.github/assets/int-debug.png" />
<img src="https://raw.githubusercontent.com/payloadcms/payload/main/packages/payload/src/assets/images/github/int-debug.png" />
- **Manually** - you can run all int tests in the `/test/_community/int.spec.ts` file by running the following command:
@@ -57,7 +57,7 @@ The easiest way to run E2E tests is to install
Once they are installed you can open the `testing` tab in vscode sidebar and drill down to the test you want to run, i.e. `/test/_community/e2e.spec.ts`
<img src="https://raw.githubusercontent.com/payloadcms/payload/main/.github/assets/e2e-debug.png" />
<img src="https://raw.githubusercontent.com/payloadcms/payload/main/packages/payload/src/assets/images/github/e2e-debug.png" />
#### Notes

View File

@@ -1,30 +0,0 @@
#!/bin/bash
severity=${1:-"critical"}
audit_json=$(pnpm audit --prod --json)
output_file="audit_output.json"
echo "Auditing for ${severity} vulnerabilities..."
echo "${audit_json}" | jq --arg severity "${severity}" '
.advisories | to_entries |
map(select(.value.patched_versions != "<0.0.0" and .value.severity == $severity) |
{
package: .value.module_name,
vulnerable: .value.vulnerable_versions,
fixed_in: .value.patched_versions
}
)
' >$output_file
audit_length=$(jq 'length' $output_file)
if [[ "${audit_length}" -gt "0" ]]; then
echo "Actionable vulnerabilities found in the following packages:"
jq -r '.[] | "\u001b[1m\(.package)\u001b[0m vulnerable in \u001b[31m\(.vulnerable)\u001b[0m fixed in \u001b[32m\(.fixed_in)\u001b[0m"' $output_file | while read -r line; do echo -e "$line"; done
echo "Output written to ${output_file}"
exit 1
else
echo "No actionable vulnerabilities"
exit 0
fi

View File

@@ -1,53 +0,0 @@
name: audit-dependencies
on:
# Sundays at 2am EST
schedule:
- cron: '0 7 * * 0'
workflow_dispatch:
inputs:
audit-level:
description: The level of audit to run (low, moderate, high, critical)
required: false
default: critical
debug:
description: Enable debug logging
required: false
default: false
env:
DO_NOT_TRACK: 1 # Disable Turbopack telemetry
NEXT_TELEMETRY_DISABLED: 1 # Disable Next telemetry
jobs:
audit:
runs-on: ubuntu-24.04
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup
uses: ./.github/actions/setup
- name: Run audit dependencies script
id: audit_dependencies
run: ./.github/workflows/audit-dependencies.sh ${{ inputs.audit-level }}
- name: Slack notification on failure
if: failure()
uses: slackapi/slack-github-action@v2.1.0
with:
webhook: ${{ inputs.debug == 'true' && secrets.SLACK_TEST_WEBHOOK_URL || secrets.SLACK_WEBHOOK_URL }}
webhook-type: incoming-webhook
payload: |
{
"username": "GitHub Actions Bot",
"blocks": [
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "🚨 Actionable vulnerabilities found: <https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}|View Details>"
}
},
]
}

View File

@@ -1,24 +0,0 @@
name: dispatch-event
on:
workflow_dispatch:
env:
PAYLOAD_PUSH_MAIN_EVENT: payload-push-main-event
jobs:
repository-dispatch:
name: Repository dispatch
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Dispatch event
if: ${{ github.event_name == 'workflow_dispatch' }}
uses: peter-evans/repository-dispatch@v3
with:
token: ${{ secrets.PAYLOAD_REPOSITORY_DISPATCH }}
repository: ${{ secrets.REMOTE_REPOSITORY }}
event-type: ${{ env.PAYLOAD_PUSH_MAIN_EVENT }}
client-payload: '{"event": {"head_commit": {"id": "${{ env.GITHUB_SHA }}"}}}' # mocked for testing
# client-payload: '{"event": ${{ toJson(github.event) }}}'

View File

@@ -2,8 +2,8 @@ name: lock-issues
on:
schedule:
# Run nightly at 12am EST, staggered with stale workflow
- cron: '0 5 * * *'
# Run nightly at 12am EST
- cron: '0 4 * * *'
workflow_dispatch:
permissions:
@@ -17,7 +17,7 @@ jobs:
uses: dessant/lock-threads@v5
with:
process-only: 'issues'
issue-inactive-days: '7'
issue-inactive-days: '1'
exclude-any-issue-labels: 'status: awaiting-reply'
log-output: true
issue-comment: >

View File

@@ -1,4 +1,4 @@
name: ci
name: build
on:
pull_request:
@@ -6,7 +6,6 @@ on:
- opened
- reopened
- synchronize
- labeled
push:
branches:
- main
@@ -17,6 +16,8 @@ concurrency:
cancel-in-progress: true
env:
NODE_VERSION: 22.6.0
PNPM_VERSION: 9.7.1
DO_NOT_TRACK: 1 # Disable Turbopack telemetry
NEXT_TELEMETRY_DISABLED: 1 # Disable Next telemetry
@@ -40,14 +41,14 @@ jobs:
with:
filters: |
needs_build:
- '.github/workflows/main.yml'
- '.github/workflows/**'
- 'packages/**'
- 'test/**'
- 'pnpm-lock.yaml'
- 'package.json'
- 'templates/**'
needs_tests:
- '.github/workflows/main.yml'
- '.github/workflows/**'
- 'packages/**'
- 'test/**'
- 'pnpm-lock.yaml'
@@ -61,6 +62,8 @@ jobs:
echo "templates: ${{ steps.filter.outputs.templates }}"
lint:
if: >
github.event_name == 'pull_request' && !contains(github.event.pull_request.title, 'no-lint') && !contains(github.event.pull_request.title, 'skip-lint')
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
@@ -69,9 +72,15 @@ jobs:
- name: Node setup
uses: ./.github/actions/setup
with:
node-version: ${{ env.NODE_VERSION }}
pnpm-version: ${{ env.PNPM_VERSION }}
pnpm-install-cache-key: pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
- name: Lint
run: pnpm lint -- --quiet
- name: Lint staged
run: |
git diff --name-only --diff-filter=d origin/${GITHUB_BASE_REF}...${GITHUB_SHA}
npx lint-staged --diff="origin/${GITHUB_BASE_REF}...${GITHUB_SHA}"
build:
needs: changes
@@ -83,6 +92,10 @@ jobs:
- name: Node setup
uses: ./.github/actions/setup
with:
node-version: ${{ env.NODE_VERSION }}
pnpm-version: ${{ env.PNPM_VERSION }}
pnpm-install-cache-key: pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
- run: pnpm run build:all
env:
@@ -104,8 +117,11 @@ jobs:
- name: Node setup
uses: ./.github/actions/setup
with:
node-version: ${{ env.NODE_VERSION }}
pnpm-version: ${{ env.PNPM_VERSION }}
pnpm-run-install: false
pnpm-restore-cache: false # Full build is restored below
pnpm-install-cache-key: pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
- name: Restore build
uses: actions/cache@v4
@@ -128,8 +144,11 @@ jobs:
- name: Node setup
uses: ./.github/actions/setup
with:
node-version: ${{ env.NODE_VERSION }}
pnpm-version: ${{ env.PNPM_VERSION }}
pnpm-run-install: false
pnpm-restore-cache: false # Full build is restored below
pnpm-install-cache-key: pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
- name: Restore build
uses: actions/cache@v4
@@ -147,7 +166,6 @@ jobs:
needs: [changes, build]
if: ${{ needs.changes.outputs.needs_tests == 'true' }}
name: int-${{ matrix.database }}
timeout-minutes: 45
strategy:
fail-fast: false
matrix:
@@ -158,8 +176,6 @@ jobs:
- postgres-uuid
- supabase
- sqlite
- sqlite-uuid
env:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
@@ -171,8 +187,7 @@ jobs:
services:
postgres:
# Custom postgres 17 docker image that supports both pg-vector and postgis: https://github.com/payloadcms/postgis-vector
image: ${{ (startsWith(matrix.database, 'postgres') ) && 'ghcr.io/payloadcms/postgis-vector:latest' || '' }}
image: ${{ (startsWith(matrix.database, 'postgres') ) && 'postgis/postgis:16-3.4' || '' }}
env:
# must specify password for PG Docker container image, see: https://registry.hub.docker.com/_/postgres?tab=description&page=1&name=10
POSTGRES_USER: ${{ env.POSTGRES_USER }}
@@ -189,8 +204,11 @@ jobs:
- name: Node setup
uses: ./.github/actions/setup
with:
node-version: ${{ env.NODE_VERSION }}
pnpm-version: ${{ env.PNPM_VERSION }}
pnpm-run-install: false
pnpm-restore-cache: false # Full build is restored below
pnpm-install-cache-key: pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
- name: Restore build
uses: actions/cache@v4
@@ -231,18 +249,23 @@ jobs:
if: matrix.database == 'supabase'
- name: Integration Tests
run: pnpm test:int
uses: nick-fields/retry@v3
env:
NODE_OPTIONS: --max-old-space-size=8096
PAYLOAD_DATABASE: ${{ matrix.database }}
POSTGRES_URL: ${{ env.POSTGRES_URL }}
with:
retry_on: any
max_attempts: 5
timeout_minutes: 15
command: pnpm test:int
on_retry_command: pnpm clean:build && pnpm install --no-frozen-lockfile
tests-e2e:
runs-on: ubuntu-24.04
needs: [changes, build]
if: ${{ needs.changes.outputs.needs_tests == 'true' }}
name: e2e-${{ matrix.suite }}
timeout-minutes: 45
strategy:
fail-fast: false
matrix:
@@ -250,57 +273,35 @@ jobs:
suite:
- _community
- access-control
- admin__e2e__general
- admin__e2e__list-view
- admin__e2e__document-view
- admin-bar
- admin__e2e__1
- admin__e2e__2
- admin__e2e__3
- admin-root
- auth
- auth-basic
- bulk-edit
- joins
- field-error-states
- fields-relationship
- fields__collections__Array
- fields
- fields__collections__Blocks
- fields__collections__Blocks#config.blockreferences.ts
- fields__collections__Checkbox
- fields__collections__Collapsible
- fields__collections__ConditionalLogic
- fields__collections__CustomID
- fields__collections__Array
- fields__collections__Relationship
- fields__collections__RichText
- fields__collections__Lexical__e2e__main
- fields__collections__Lexical__e2e__blocks
- fields__collections__Date
- fields__collections__Email
- fields__collections__Indexed
- fields__collections__JSON
- fields__collections__Number
- fields__collections__Point
- fields__collections__Radio
- fields__collections__Relationship
- fields__collections__Row
- fields__collections__Select
- fields__collections__Tabs
- fields__collections__Tabs2
- fields__collections__Text
- fields__collections__UI
- fields__collections__Upload
- hooks
- lexical__collections__Lexical__e2e__main
- lexical__collections__Lexical__e2e__blocks
- lexical__collections__Lexical__e2e__blocks#config.blockreferences.ts
- lexical__collections__RichText
- query-presets
- form-state
- live-preview
- localization
- locked-documents
- i18n
- plugin-cloud-storage
- plugin-form-builder
- plugin-import-export
- plugin-multi-tenant
- plugin-nested-docs
- plugin-seo
- sort
- versions
- uploads
env:
@@ -311,8 +312,11 @@ jobs:
- name: Node setup
uses: ./.github/actions/setup
with:
node-version: ${{ env.NODE_VERSION }}
pnpm-version: ${{ env.PNPM_VERSION }}
pnpm-run-install: false
pnpm-restore-cache: false # Full build is restored below
pnpm-install-cache-key: pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
- name: Restore build
uses: actions/cache@v4
@@ -347,7 +351,13 @@ jobs:
run: pnpm exec playwright install-deps chromium
- name: E2E Tests
run: PLAYWRIGHT_JSON_OUTPUT_NAME=results_${{ matrix.suite }}.json pnpm test:e2e:prod:ci:noturbo ${{ matrix.suite }}
uses: nick-fields/retry@v3
with:
retry_on: any
max_attempts: 5
timeout_minutes: 20
command: PLAYWRIGHT_JSON_OUTPUT_NAME=results_${{ matrix.suite }}.json pnpm test:e2e:prod:ci ${{ matrix.suite }}
on_retry_command: pnpm clean:build && pnpm install --no-frozen-lockfile && pnpm build:all
env:
PLAYWRIGHT_JSON_OUTPUT_NAME: results_${{ matrix.suite }}.json
NEXT_TELEMETRY_DISABLED: 1
@@ -367,180 +377,37 @@ jobs:
# report-tag: ${{ matrix.suite }}
# job-summary: true
tests-e2e-turbo:
# Build listed templates with packed local packages
build-templates:
runs-on: ubuntu-24.04
needs: [changes, build]
if: >-
needs.changes.outputs.needs_tests == 'true' &&
(
contains(github.event.pull_request.labels.*.name, 'run-e2e-turbo') ||
github.event.label.name == 'run-e2e-turbo'
)
name: e2e-turbo-${{ matrix.suite }}
needs: build
strategy:
fail-fast: false
matrix:
# find test -type f -name 'e2e.spec.ts' | sort | xargs dirname | xargs -I {} basename {}
suite:
- _community
- access-control
- admin__e2e__general
- admin__e2e__list-view
- admin__e2e__document-view
- admin-bar
- admin-root
- auth
- auth-basic
- bulk-edit
- joins
- field-error-states
- fields-relationship
- fields__collections__Array
- fields__collections__Blocks
- fields__collections__Blocks#config.blockreferences.ts
- fields__collections__Checkbox
- fields__collections__Collapsible
- fields__collections__ConditionalLogic
- fields__collections__CustomID
- fields__collections__Date
- fields__collections__Email
- fields__collections__Indexed
- fields__collections__JSON
- fields__collections__Number
- fields__collections__Point
- fields__collections__Radio
- fields__collections__Relationship
- fields__collections__Row
- fields__collections__Select
- fields__collections__Tabs
- fields__collections__Tabs2
- fields__collections__Text
- fields__collections__UI
- fields__collections__Upload
- hooks
- lexical__collections__Lexical__e2e__main
- lexical__collections__Lexical__e2e__blocks
- lexical__collections__Lexical__e2e__blocks#config.blockreferences.ts
- lexical__collections__RichText
- query-presets
- form-state
- live-preview
- localization
- locked-documents
- i18n
- plugin-cloud-storage
- plugin-form-builder
- plugin-import-export
- plugin-multi-tenant
- plugin-nested-docs
- plugin-seo
- sort
- versions
- uploads
env:
SUITE_NAME: ${{ matrix.suite }}
steps:
- uses: actions/checkout@v4
- name: Node setup
uses: ./.github/actions/setup
with:
pnpm-run-install: false
pnpm-restore-cache: false # Full build is restored below
- name: Restore build
uses: actions/cache@v4
with:
path: ./*
key: ${{ github.sha }}-${{ github.run_number }}
- name: Start LocalStack
run: pnpm docker:start
if: ${{ matrix.suite == 'plugin-cloud-storage' }}
- name: Store Playwright's Version
run: |
# Extract the version number using a more targeted regex pattern with awk
PLAYWRIGHT_VERSION=$(pnpm ls @playwright/test --depth=0 | awk '/@playwright\/test/ {print $2}')
echo "Playwright's Version: $PLAYWRIGHT_VERSION"
echo "PLAYWRIGHT_VERSION=$PLAYWRIGHT_VERSION" >> $GITHUB_ENV
- name: Cache Playwright Browsers for Playwright's Version
id: cache-playwright-browsers
uses: actions/cache@v4
with:
path: ~/.cache/ms-playwright
key: playwright-browsers-${{ env.PLAYWRIGHT_VERSION }}
- name: Setup Playwright - Browsers and Dependencies
if: steps.cache-playwright-browsers.outputs.cache-hit != 'true'
run: pnpm exec playwright install --with-deps chromium
- name: Setup Playwright - Dependencies-only
if: steps.cache-playwright-browsers.outputs.cache-hit == 'true'
run: pnpm exec playwright install-deps chromium
- name: E2E Tests
run: PLAYWRIGHT_JSON_OUTPUT_NAME=results_${{ matrix.suite }}.json pnpm test:e2e:prod:ci ${{ matrix.suite }}
env:
PLAYWRIGHT_JSON_OUTPUT_NAME: results_${{ matrix.suite }}.json
NEXT_TELEMETRY_DISABLED: 1
- uses: actions/upload-artifact@v4
if: always()
with:
name: test-results-turbo${{ matrix.suite }}
path: test/test-results/
if-no-files-found: ignore
retention-days: 1
# Disabled until this is fixed: https://github.com/daun/playwright-report-summary/issues/156
# - uses: daun/playwright-report-summary@v3
# with:
# report-file: results_${{ matrix.suite }}.json
# report-tag: ${{ matrix.suite }}
# job-summary: true
# Build listed templates with packed local packages and then runs their int and e2e tests
build-and-test-templates:
runs-on: ubuntu-24.04
needs: [changes, build]
if: ${{ needs.changes.outputs.needs_build == 'true' }}
name: build-template-${{ matrix.template }}-${{ matrix.database }}
strategy:
fail-fast: false
matrix:
include:
- template: blank
database: mongodb
- template: website
database: mongodb
- template: with-payload-cloud
database: mongodb
- template: with-vercel-mongodb
database: mongodb
# Postgres
- template: with-postgres
database: postgres
- template: with-vercel-postgres
database: postgres
- template: plugin
# Re-enable once PG conncection is figured out
# - template: with-vercel-website
# database: postgres
name: ${{ matrix.template }}-${{ matrix.database }}
env:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: payloadtests
MONGODB_VERSION: 6.0
steps:
- uses: actions/checkout@v4
@@ -548,8 +415,11 @@ jobs:
- name: Node setup
uses: ./.github/actions/setup
with:
node-version: ${{ env.NODE_VERSION }}
pnpm-version: ${{ env.PNPM_VERSION }}
pnpm-run-install: false
pnpm-restore-cache: false # Full build is restored below
pnpm-install-cache-key: pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
- name: Restore build
uses: actions/cache@v4
@@ -577,63 +447,15 @@ jobs:
echo "POSTGRES_URL=postgresql://$POSTGRES_USER:$POSTGRES_PASSWORD@localhost:5432/$POSTGRES_DB" >> $GITHUB_ENV
if: matrix.database == 'postgres'
# Avoid dockerhub rate-limiting
- name: Cache Docker images
uses: ScribeMD/docker-cache@0.5.0
with:
key: docker-${{ runner.os }}-mongo-${{ env.MONGODB_VERSION }}
- name: Start MongoDB
uses: supercharge/mongodb-github-action@1.12.0
uses: supercharge/mongodb-github-action@1.11.0
with:
mongodb-version: 6.0
if: matrix.database == 'mongodb'
- name: Build Template
run: |
pnpm run script:pack --dest templates/${{ matrix.template }}
pnpm run script:build-template-with-local-pkgs ${{ matrix.template }} $POSTGRES_URL
env:
NODE_OPTIONS: --max-old-space-size=8096
- name: Store Playwright's Version
run: |
# Extract the version number using a more targeted regex pattern with awk
PLAYWRIGHT_VERSION=$(pnpm ls @playwright/test --depth=0 | awk '/@playwright\/test/ {print $2}')
echo "Playwright's Version: $PLAYWRIGHT_VERSION"
echo "PLAYWRIGHT_VERSION=$PLAYWRIGHT_VERSION" >> $GITHUB_ENV
- name: Cache Playwright Browsers for Playwright's Version
id: cache-playwright-browsers
uses: actions/cache@v4
with:
path: ~/.cache/ms-playwright
key: playwright-browsers-${{ env.PLAYWRIGHT_VERSION }}
- name: Setup Playwright - Browsers and Dependencies
if: steps.cache-playwright-browsers.outputs.cache-hit != 'true'
run: pnpm exec playwright install --with-deps chromium
- name: Setup Playwright - Dependencies-only
if: steps.cache-playwright-browsers.outputs.cache-hit == 'true'
run: pnpm exec playwright install-deps chromium
- name: Runs Template Int Tests
run: pnpm --filter ${{ matrix.template }} run test:int
env:
NODE_OPTIONS: --max-old-space-size=8096
PAYLOAD_DATABASE: ${{ matrix.database }}
POSTGRES_URL: ${{ env.POSTGRES_URL }}
MONGODB_URL: mongodb://localhost:27017/payloadtests
- name: Runs Template E2E Tests
run: PLAYWRIGHT_JSON_OUTPUT_NAME=results_${{ matrix.template }}.json pnpm --filter ${{ matrix.template }} test:e2e
env:
NODE_OPTIONS: --max-old-space-size=8096
PAYLOAD_DATABASE: ${{ matrix.database }}
POSTGRES_URL: ${{ env.POSTGRES_URL }}
MONGODB_URL: mongodb://localhost:27017/payloadtests
NEXT_TELEMETRY_DISABLED: 1
pnpm runts scripts/build-template-with-local-pkgs.ts ${{ matrix.template }} $POSTGRES_URL
tests-type-generation:
runs-on: ubuntu-24.04
@@ -645,8 +467,11 @@ jobs:
- name: Node setup
uses: ./.github/actions/setup
with:
node-version: ${{ env.NODE_VERSION }}
pnpm-version: ${{ env.PNPM_VERSION }}
pnpm-run-install: false
pnpm-restore-cache: false # Full build is restored below
pnpm-install-cache-key: pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
- name: Restore build
uses: actions/cache@v4
@@ -667,7 +492,7 @@ jobs:
needs:
- lint
- build
- build-and-test-templates
- build-templates
- tests-unit
- tests-int
- tests-e2e
@@ -690,34 +515,3 @@ jobs:
- run: |
echo github.ref: ${{ github.ref }}
echo isV3: ${{ github.ref == 'refs/heads/main' }}
analyze:
runs-on: ubuntu-latest
needs: [changes, build]
timeout-minutes: 5
permissions:
contents: read # for checkout repository
actions: read # for fetching base branch bundle stats
pull-requests: write # for comments
steps:
- uses: actions/checkout@v4
- name: Node setup
uses: ./.github/actions/setup
with:
pnpm-run-install: false
pnpm-restore-cache: false # Full build is restored below
- name: Restore build
uses: actions/cache@v4
with:
path: ./*
key: ${{ github.sha }}-${{ github.run_number }}
- run: pnpm run build:bundle-for-analysis # Esbuild packages that haven't already been built in the build step for the purpose of analyzing bundle size
env:
DO_NOT_TRACK: 1 # Disable Turbopack telemetry
- name: Analyze esbuild bundle size
uses: exoego/esbuild-bundle-analyzer@v1
with:
metafiles: 'packages/payload/meta_index.json,packages/payload/meta_shared.json,packages/ui/meta_client.json,packages/ui/meta_shared.json,packages/next/meta_index.json,packages/richtext-lexical/meta_client.json'

View File

@@ -7,43 +7,13 @@ on:
workflow_dispatch:
env:
NODE_VERSION: 22.6.0
PNPM_VERSION: 9.7.1
DO_NOT_TRACK: 1 # Disable Turbopack telemetry
NEXT_TELEMETRY_DISABLED: 1 # Disable Next telemetry
jobs:
wait_for_release:
runs-on: ubuntu-24.04
outputs:
release_tag: ${{ steps.determine_tag.outputs.release_tag }}
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
sparse-checkout: .github/workflows
- name: Determine Release Tag
id: determine_tag
run: |
if [ "${{ github.event_name }}" == "release" ]; then
echo "Using tag from release event: ${{ github.event.release.tag_name }}"
echo "release_tag=${{ github.event.release.tag_name }}" >> "$GITHUB_OUTPUT"
else
# pull latest tag from github, must match any version except v2. Should match v3, v4, v99, etc.
echo "Fetching latest tag from github..."
LATEST_TAG=$(git describe --tags --abbrev=0 --match 'v[0-9]*' --exclude 'v2*')
echo "Latest tag: $LATEST_TAG"
echo "release_tag=$LATEST_TAG" >> "$GITHUB_OUTPUT"
fi
- name: Wait until latest versions resolve on npm registry
run: |
./.github/workflows/wait-until-package-version.sh payload ${{ steps.determine_tag.outputs.release_tag }}
./.github/workflows/wait-until-package-version.sh @payloadcms/translations ${{ steps.determine_tag.outputs.release_tag }}
./.github/workflows/wait-until-package-version.sh @payloadcms/next ${{ steps.determine_tag.outputs.release_tag }}
update_templates:
needs: wait_for_release
runs-on: ubuntu-24.04
permissions:
contents: write
@@ -55,9 +25,14 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0 # Needed for tags
- name: Setup
uses: ./.github/actions/setup
with:
node-version: ${{ env.NODE_VERSION }}
pnpm-version: ${{ env.PNPM_VERSION }}
- name: Start PostgreSQL
uses: CasperWA/postgresql-action@v1.2
@@ -78,18 +53,27 @@ jobs:
echo "DATABASE_URI=postgresql://$POSTGRES_USER:$POSTGRES_PASSWORD@localhost:5432/$POSTGRES_DB" >> $GITHUB_ENV
- name: Start MongoDB
uses: supercharge/mongodb-github-action@1.12.0
uses: supercharge/mongodb-github-action@1.11.0
with:
mongodb-version: 6.0
# The template generation script runs import map generation which needs the built payload bin scripts
- run: pnpm run build:all
env:
DO_NOT_TRACK: 1 # Disable Turbopack telemetry
- name: Update template lockfiles and migrations
run: pnpm script:gen-templates
- name: Determine Release Tag
id: determine_tag
run: |
if [ "${{ github.event_name }}" == "release" ]; then
echo "Using tag from release event: ${{ github.event.release.tag_name }}"
echo "release_tag=${{ github.event.release.tag_name }}" >> "$GITHUB_OUTPUT"
else
# pull latest tag from github, must match any version except v2. Should match v3, v4, v99, etc.
echo "Fetching latest tag from github..."
LATEST_TAG=$(git describe --tags --abbrev=0 --match 'v[0-9]*' --exclude 'v2*')
echo "Latest tag: $LATEST_TAG"
echo "release_tag=$LATEST_TAG" >> "$GITHUB_OUTPUT"
fi
- name: Commit and push changes
id: commit
env:
@@ -101,7 +85,7 @@ jobs:
git diff --name-only
export BRANCH_NAME=templates/bump-${{ needs.wait_for_release.outputs.release_tag }}-$(date +%s)
export BRANCH_NAME=templates/bump-${{ steps.determine_tag.outputs.release_tag }}-$(date +%s)
echo "branch=$BRANCH_NAME" >> "$GITHUB_OUTPUT"
- name: Create pull request
@@ -110,12 +94,12 @@ jobs:
token: ${{ secrets.GH_TOKEN_POST_RELEASE_TEMPLATES }}
labels: 'area: templates'
author: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
commit-message: 'templates: bump templates for ${{ needs.wait_for_release.outputs.release_tag }}'
commit-message: 'templates: bump templates for ${{ steps.determine_tag.outputs.release_tag }}'
branch: ${{ steps.commit.outputs.branch }}
base: main
assignees: ${{ github.actor }}
title: 'templates: bump for ${{ needs.wait_for_release.outputs.release_tag }}'
title: 'templates: bump for ${{ steps.determine_tag.outputs.release_tag }}'
body: |
🤖 Automated bump of templates for ${{ needs.wait_for_release.outputs.release_tag }}
🤖 Automated bump of templates for ${{ steps.determine_tag.outputs.release_tag }}
Triggered by user: @${{ github.actor }}

View File

@@ -12,6 +12,8 @@ on:
default: ''
env:
NODE_VERSION: 22.6.0
PNPM_VERSION: 9.7.1
DO_NOT_TRACK: 1 # Disable Turbopack telemetry
NEXT_TELEMETRY_DISABLED: 1 # Disable Next telemetry

View File

@@ -41,9 +41,7 @@ jobs:
db-vercel-postgres
db-sqlite
drizzle
email-\*
email-nodemailer
email-resend
eslint
graphql
live-preview
@@ -53,8 +51,6 @@ jobs:
plugin-cloud
plugin-cloud-storage
plugin-form-builder
plugin-import-export
plugin-multi-tenant
plugin-nested-docs
plugin-redirects
plugin-search

View File

@@ -1,43 +0,0 @@
name: publish-prerelease
on:
schedule:
# Run nightly at 10pm EST
- cron: '0 3 * * *'
workflow_dispatch:
env:
DO_NOT_TRACK: 1 # Disable Turbopack telemetry
NEXT_TELEMETRY_DISABLED: 1 # Disable Next telemetry
jobs:
release:
name: publish-prerelease-${{ github.ref_name }}-${{ github.sha }}
permissions:
id-token: write
runs-on: ubuntu-24.04
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup
uses: ./.github/actions/setup
- name: Load npm token
run: echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" >> ~/.npmrc
env:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
- name: Determine release type
id: determine_release_type
# Use 'canary' for main branch, 'internal' for others
run: |
if [[ ${{ github.ref_name }} == "main" ]]; then
echo "release_type=canary" >> $GITHUB_OUTPUT
else
echo "release_type=internal" >> $GITHUB_OUTPUT
fi
- name: Release
run: pnpm publish-prerelease --tag ${{ steps.determine_release_type.outputs.release_type }}
env:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
NPM_CONFIG_PROVENANCE: true

35
.github/workflows/release-canary.yml vendored Normal file
View File

@@ -0,0 +1,35 @@
name: release-canary
on:
workflow_dispatch:
env:
NODE_VERSION: 22.6.0
PNPM_VERSION: 9.7.1
DO_NOT_TRACK: 1 # Disable Turbopack telemetry
NEXT_TELEMETRY_DISABLED: 1 # Disable Next telemetry
jobs:
release:
name: release-canary-${{ github.ref_name }}-${{ github.sha }}
permissions:
id-token: write
runs-on: ubuntu-24.04
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup
uses: ./.github/actions/setup
with:
node-version: ${{ env.NODE_VERSION }}
pnpm-version: ${{ env.PNPM_VERSION }}
- name: Load npm token
run: echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" >> ~/.npmrc
env:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
- name: Canary release script
# dry run hard-coded to true for testing and no npm token provided
run: pnpm tsx ./scripts/publish-canary.ts
env:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
NPM_CONFIG_PROVENANCE: true

View File

@@ -2,8 +2,8 @@ name: stale
on:
schedule:
# Run nightly at 1am EST, staggered with lock-issues workflow
- cron: '0 6 * * *'
# Run nightly at 1am EST
- cron: '0 5 * * *'
workflow_dispatch:
inputs:
@@ -25,16 +25,19 @@ jobs:
with:
debug-only: ${{ inputs.dry-run || false }}
days-before-stale: 30
days-before-close: -1 # Disable closing
days-before-stale: 23
days-before-close: 7
days-before-pr-close: -1 # Never close PRs
ascending: true
operations-per-run: 300
exempt-all-assignees: true
# Issues
stale-issue-label: stale
exempt-issue-labels: 'prioritized,keep,created-by: Payload team,created-by: Contributor,status: verified'
stale-issue-message: ''
stale-issue-message: |
This issue has been marked as stale due to lack of activity.
To keep this issue open, please indicate that it is still relevant in a comment below.
close-issue-message: |
This issue was automatically closed due to lack of activity.
@@ -42,7 +45,10 @@ jobs:
# Pull Requests
stale-pr-label: stale
exempt-pr-labels: 'prioritized,keep,created-by: Payload team,created-by: Contributor'
stale-pr-message: ''
stale-pr-message: |
This PR is stale due to lack of activity.
To keep the PR open, please indicate that it is still relevant in a comment below.
close-pr-message: |
This pull request was automatically closed due to lack of activity.

View File

@@ -1,31 +0,0 @@
#!/bin/bash
if [[ "$#" -ne 2 ]]; then
echo "Usage: $0 <package-name> <version>"
exit 1
fi
PACKAGE_NAME="$1"
TARGET_VERSION=${2#v} # Git tag has leading 'v', npm version does not
TIMEOUT=300 # 5 minutes in seconds
INTERVAL=10 # 10 seconds
ELAPSED=0
echo "Waiting for version ${TARGET_VERSION} of '${PACKAGE_NAME}' to resolve... (timeout: ${TIMEOUT} seconds)"
while [[ ${ELAPSED} -lt ${TIMEOUT} ]]; do
latest_version=$(npm show "${PACKAGE_NAME}" version 2>/dev/null)
if [[ ${latest_version} == "${TARGET_VERSION}" ]]; then
echo "SUCCCESS: Version ${TARGET_VERSION} of ${PACKAGE_NAME} is available."
exit 0
else
echo "Version ${TARGET_VERSION} of ${PACKAGE_NAME} is not available yet. Retrying in ${INTERVAL} seconds... (elapsed: ${ELAPSED}s)"
fi
sleep "${INTERVAL}"
ELAPSED=$((ELAPSED + INTERVAL))
done
echo "Timed out after ${TIMEOUT} seconds waiting for version ${TARGET_VERSION} of '${PACKAGE_NAME}' to resolve."
exit 1

16
.gitignore vendored
View File

@@ -3,7 +3,6 @@ package-lock.json
dist
/.idea/*
!/.idea/runConfigurations
/.idea/runConfigurations/_template*
!/.idea/payload.iml
# Custom actions
@@ -22,13 +21,6 @@ meta_server.json
meta_index.json
meta_shared.json
packages/payload/esbuild
packages/ui/esbuild
packages/next/esbuild
packages/richtext-lexical/esbuild
audit_output.json
.turbo
# Ignore test directory media folder/files
@@ -314,8 +306,6 @@ $RECYCLE.BIN/
/build
.swc
app/(payload)/admin/importMap.js
test/admin-bar/app/(payload)/admin/importMap.js
/test/admin-bar/app/(payload)/admin/importMap.js
test/live-preview/app/(payload)/admin/importMap.js
/test/live-preview/app/(payload)/admin/importMap.js
test/admin-root/app/(payload)/admin/importMap.js
@@ -327,9 +317,3 @@ test/databaseAdapter.js
/filename-compound-index
/media-with-relation-preview
/media-without-relation-preview
/media-without-cache-tags
test/.localstack
test/google-cloud-storage
test/azurestoragedata/
licenses.csv

3
.idea/payload.iml generated
View File

@@ -80,9 +80,8 @@
<excludeFolder url="file://$MODULE_DIR$/packages/drizzle/dist" />
<excludeFolder url="file://$MODULE_DIR$/packages/db-sqlite/.turbo" />
<excludeFolder url="file://$MODULE_DIR$/packages/db-sqlite/dist" />
<excludeFolder url="file://$MODULE_DIR$/packages/plugin-import-export/dist" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>
</module>

View File

@@ -0,0 +1,9 @@
<component name="ProjectRunConfigurationManager">
<configuration default="true" type="JavaScriptTestRunnerJest">
<node-interpreter value="project" />
<node-options value="--no-deprecation" />
<envs />
<scope-kind value="ALL" />
<method v="2" />
</configuration>
</component>

View File

@@ -1 +1 @@
v23.11.0
v22.6.0

1
.npmrc
View File

@@ -1,4 +1,3 @@
symlink=true
node-linker=isolated
hoist-workspace-packages=false # the default in pnpm v9 is true, but that can break our runtime dependency checks
save-prefix=''

2
.nvmrc
View File

@@ -1 +1 @@
v23.11.0
v22.6.0

View File

@@ -8,6 +8,7 @@
**/dist/**
**/node_modules
**/temp
**/docs/**
tsconfig.json
packages/payload/*.js
packages/payload/*.d.ts

View File

@@ -1,2 +1,2 @@
pnpm 9.7.1
nodejs 23.11.0
nodejs 22.6.0

14
.vscode/launch.json vendored
View File

@@ -63,13 +63,6 @@
"request": "launch",
"type": "node-terminal"
},
{
"command": "pnpm tsx --no-deprecation test/dev.ts query-presets",
"cwd": "${workspaceFolder}",
"name": "Run Dev Query Presets",
"request": "launch",
"type": "node-terminal"
},
{
"command": "pnpm tsx --no-deprecation test/dev.ts login-with-username",
"cwd": "${workspaceFolder}",
@@ -118,13 +111,6 @@
"request": "launch",
"type": "node-terminal"
},
{
"command": "pnpm tsx --no-deprecation test/dev.ts folder-view",
"cwd": "${workspaceFolder}",
"name": "Run Dev Folder View",
"request": "launch",
"type": "node-terminal"
},
{
"command": "pnpm tsx --no-deprecation test/dev.ts localization",
"cwd": "${workspaceFolder}",

49
.vscode/settings.json vendored
View File

@@ -1,14 +1,40 @@
{
"npm.packageManager": "pnpm",
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit"
"[typescript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit"
}
},
"[typescriptreact]": {
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit"
}
},
"[javascript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit"
}
},
"[json]": {
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true
},
"[jsonc]": {
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true
},
"editor.formatOnSaveMode": "file",
"files.insertFinalNewline": true,
"files.trimTrailingWhitespace": true,
"eslint.rules.customizations": [
// Defaultt all ESLint errors to 'warn' to differentate from TypeScript's 'error' level
{ "rule": "*", "severity": "warn" },
// Silence some warnings that will get auto-fixed
{ "rule": "perfectionist/*", "severity": "off", "fixable": true },
{ "rule": "curly", "severity": "off", "fixable": true },
@@ -17,14 +43,17 @@
"typescript.tsdk": "node_modules/typescript/lib",
// Load .git-blame-ignore-revs file
"gitlens.advanced.blame.customArguments": ["--ignore-revs-file", ".git-blame-ignore-revs"],
"[javascript][typescript][typescriptreact]": {
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit"
}
},
"files.insertFinalNewline": true,
"jestrunner.jestCommand": "pnpm exec cross-env NODE_OPTIONS=\"--no-deprecation\" node 'node_modules/jest/bin/jest.js'",
"jestrunner.changeDirectoryToWorkspaceRoot": false,
"jestrunner.debugOptions": {
"runtimeArgs": ["--no-deprecation"]
},
// Essentially disables bun test buttons
"bun.test.filePattern": "bun.test.ts",
"playwright.env": {
"NODE_OPTIONS": "--no-deprecation --no-experimental-strip-types"
"search.exclude": {
"**/docs/V2_(excluded_from_search)/**": true
}
}

View File

@@ -63,7 +63,7 @@ Each test directory is split up in this way specifically to reduce friction when
The following command will start Payload with your config: `pnpm dev my-test-dir`. Example: `pnpm dev fields` for the test/`fields` test suite. This command will start up Payload using your config and refresh a test database on every restart. If you're using VS Code, the most common run configs are automatically added to your editor - you should be able to find them in your VS Code launch tab.
By default, payload will [automatically log you in](https://payloadcms.com/docs/authentication/overview#auto-login) with the default credentials. To disable that, you can either pass in the --no-auto-login flag (example: `pnpm dev my-test-dir --no-auto-login`) or set the `PAYLOAD_PUBLIC_DISABLE_AUTO_LOGIN` environment variable to `false`.
By default, payload will [automatically log you in](https://payloadcms.com/docs/authentication/overview#admin-autologin) with the default credentials. To disable that, you can either pass in the --no-auto-login flag (example: `pnpm dev my-test-dir --no-auto-login`) or set the `PAYLOAD_PUBLIC_DISABLE_AUTO_LOGIN` environment variable to `false`.
The default credentials are `dev@payloadcms.com` as E-Mail and `test` as password. These are used in the auto-login.
@@ -77,13 +77,9 @@ If you wish to use your own MongoDB database for the `test` directory instead of
### Using Postgres
Our test suites supports automatic PostgreSQL + PostGIS setup using Docker. No local PostgreSQL installation required. By default, mongodb is used.
If you have postgres installed on your system, you can also run the test suites using postgres. By default, mongodb is used.
To use postgres, simply set the `PAYLOAD_DATABASE` environment variable to `postgres`.
```bash
PAYLOAD_DATABASE=postgres pnpm dev {suite}
```
To do that, simply set the `PAYLOAD_DATABASE` environment variable to `postgres`.
### Running the e2e and int tests
@@ -91,43 +87,41 @@ You can run the entire test suite using `pnpm test`. If you wish to only run e2e
By default, `pnpm test:int` will only run int test against MongoDB. To run int tests against postgres, you can use `pnpm test:int:postgres`. You will have to have postgres installed on your system for this to work.
### Pull Requests
### Commits
For all Pull Requests, you should be extremely descriptive about both your problem and proposed solution. If there are any affected open or closed issues, please leave the issue number in your PR description.
We use [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) for our commit messages. Please follow this format when creating commits. Here are some examples:
All commits within a PR are squashed when merged, using the PR title as the commit message. For that reason, please use [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) for your PR titles.
- `feat: adds new feature`
- `fix: fixes bug`
- `docs: adds documentation`
- `chore: does chore`
Here are some examples:
Here's a breakdown of the format. At the top-level, we use the following types to categorize our commits:
- `feat: add new feature`
- `fix: fix bug`
- `docs: add documentation`
- `test: add/fix tests`
- `refactor: refactor code`
- `chore: anything that does not fit into the above categories`
If applicable, you must indicate the affected packages in parentheses to "scope" the changes. Changes to the payload chore package do not require scoping.
Here are some examples:
- `feat(ui): add new feature`
- `fix(richtext-lexical): fix bug`
- `feat`: new feature that adds functionality. These are automatically added to the changelog when creating new releases.
- `fix`: a fix to an existing feature. These are automatically added to the changelog when creating new releases.
- `docs`: changes to [docs](./docs) only. These do not appear in the changelog.
- `chore`: changes to code that is neither a fix nor a feature (e.g. refactoring, adding tests, etc.). These do not appear in the changelog.
If you are committing to [templates](./templates) or [examples](./examples), use the `chore` type with the proper scope, like this:
- `chore(templates): adds feature to template`
- `chore(examples): fixes bug in example`
## Pull Requests
For all Pull Requests, you should be extremely descriptive about both your problem and proposed solution. If there are any affected open or closed issues, please leave the issue number in your PR message.
## Previewing docs
This is how you can preview changes you made locally to the docs:
1. Clone our [website repository](https://github.com/payloadcms/website)
1. Clone our [website repository](https://github.com/payloadcms/website) `git clone https://github.com/payloadcms/website.git && cd website`
2. Run `pnpm install`
3. Duplicate the `.env.example` file and rename it to `.env`
3. Duplicate the `.env.example` file and rename it to `.env` (`cp .env.example .env`)
4. Add a `DOCS_DIR` environment variable to the `.env` file which points to the absolute path of your modified docs folder. For example `DOCS_DIR=/Users/yourname/Documents/GitHub/payload/docs`
5. Run `pnpm fetchDocs:local`. If this was successful, you should see no error messages and the following output: _Docs successfully written to /.../website/src/app/docs.json_. There could be error messages if you have incorrect markdown in your local docs folder. In this case, it will tell you how you can fix it
6. You're done! Now you can start the website locally using `pnpm dev` and preview the docs under [http://localhost:3000/docs/local](http://localhost:3000/docs/local)
5. Run `pnpm run fetchDocs:local`. If this was successful, you should see no error messages and the following output: _Docs successfully written to /.../website/src/app/docs.json_. There could be error messages if you have incorrect markdown in your local docs folder. In this case, it will tell you how you can fix it
6. You're done! Now you can start the website locally using `pnpm run dev` and preview the docs under [http://localhost:3000/docs/](http://localhost:3000/docs/)
## Internationalization (i18n)

View File

@@ -45,7 +45,7 @@ There are a couple ways to do this:
- **Granularly** - you can run individual tests in vscode by installing the Jest Runner plugin and using that to run individual tests. Clicking the `debug` button will run the test in debug mode allowing you to set break points.
<img src="https://raw.githubusercontent.com/payloadcms/payload/main/.github/assets/int-debug.png" />
<img src="https://raw.githubusercontent.com/payloadcms/payload/main/packages/payload/src/assets/images/github/int-debug.png" />
- **Manually** - you can run all int tests in the `/test/_community/int.spec.ts` file by running the following command:
@@ -62,7 +62,7 @@ The easiest way to run E2E tests is to install
Once they are installed you can open the `testing` tab in vscode sidebar and drill down to the test you want to run, i.e. `/test/_community/e2e.spec.ts`
<img src="https://raw.githubusercontent.com/payloadcms/payload/main/.github/assets/e2e-debug.png" />
<img src="https://raw.githubusercontent.com/payloadcms/payload/main/packages/payload/src/assets/images/github/e2e-debug.png" />
#### Notes

View File

@@ -1,22 +0,0 @@
MIT License
Copyright (c) 2018-2025 Payload CMS, Inc. <info@payloadcms.com>
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
'Software'), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@@ -96,11 +96,7 @@ If you want to add contributions to this repository, please follow the instructi
The [Examples Directory](./examples) is a great resource for learning how to setup Payload in a variety of different ways, but you can also find great examples in our blog and throughout our social media.
If you'd like to run the examples, you can use `create-payload-app` to create a project from one:
```sh
npx create-payload-app --example example_name
```
If you'd like to run the examples, you can either copy them to a folder outside this repo or run them directly by (1) navigating to the example's subfolder (`cd examples/your-example-folder`) and (2) using the `--ignore-workspace` flag to bypass workspace restrictions (e.g., `pnpm --ignore-workspace install` or `pnpm --ignore-workspace dev`).
You can see more examples at:

View File

@@ -1,8 +1,8 @@
import configPromise from '@payload-config'
import { getPayload } from 'payload'
import { getPayloadHMR } from '@payloadcms/next/utilities'
export const Page = async ({ params, searchParams }) => {
const payload = await getPayload({
const payload = await getPayloadHMR({
config: configPromise,
})
return <div>test ${payload?.config?.collections?.length}</div>

View File

@@ -1,6 +0,0 @@
{
"singleQuote": true,
"trailingComma": "all",
"printWidth": 80,
"semi": false
}

View File

@@ -1,342 +0,0 @@
---
title: Collection Access Control
label: Collections
order: 20
desc: With Collection-level Access Control you can define which users can create, read, update or delete Collections.
keywords: collections, access control, permissions, documentation, Content Management System, cms, headless, javascript, node, react, nextjs
---
Collection Access Control is [Access Control](../access-control/overview) used to restrict access to Documents within a [Collection](../getting-started/concepts#collections), as well as what they can and cannot see within the [Admin Panel](../admin/overview) as it relates to that Collection.
To add Access Control to a Collection, use the `access` property in your [Collection Config](../configuration/collections):
```ts
import type { CollectionConfig } from 'payload'
export const CollectionWithAccessControl: CollectionConfig = {
// ...
access: {
// highlight-line
// ...
},
}
```
## Config Options
Access Control is specific to the operation of the request.
To add Access Control to a Collection, use the `access` property in your [Collection Config](../configuration/collections):
```ts
import type { CollectionConfig } from 'payload';
export const CollectionWithAccessControl: CollectionConfig = {
// ...
// highlight-start
access: {
create: () => {...},
read: () => {...},
update: () => {...},
delete: () => {...},
// Auth-enabled Collections only
admin: () => {...},
unlock: () => {...},
// Version-enabled Collections only
readVersions: () => {...},
},
// highlight-end
}
```
The following options are available:
| Function | Allows/Denies Access |
| ------------ | -------------------------------------------------------------------- |
| **`create`** | Used in the `create` operation. [More details](#create). |
| **`read`** | Used in the `find` and `findByID` operations. [More details](#read). |
| **`update`** | Used in the `update` operation. [More details](#update). |
| **`delete`** | Used in the `delete` operation. [More details](#delete). |
If a Collection supports [`Authentication`](../authentication/overview), the following additional options are available:
| Function | Allows/Denies Access |
| ------------ | ---------------------------------------------------------------------------------------- |
| **`admin`** | Used to restrict access to the [Admin Panel](../admin/overview). [More details](#admin). |
| **`unlock`** | Used to restrict which users can access the `unlock` operation. [More details](#unlock). |
If a Collection supports [Versions](../versions/overview), the following additional options are available:
| Function | Allows/Denies Access |
| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **`readVersions`** | Used to control who can read versions, and who can't. Will automatically restrict the Admin UI version viewing access. [More details](#read-versions). |
### Create
Returns a boolean which allows/denies access to the `create` request.
To add create Access Control to a Collection, use the `create` property in the [Collection Config](../configuration/collections):
```ts
import type { CollectionConfig } from 'payload'
export const CollectionWithCreateAccess: CollectionConfig = {
// ...
access: {
// highlight-start
create: ({ req: { user }, data }) => {
return Boolean(user)
},
// highlight-end
},
}
```
The following arguments are provided to the `create` function:
| Option | Description |
| ---------- | ----------------------------------------------------------------------------------------------------------------------------- |
| **`req`** | The [Request](https://developer.mozilla.org/en-US/docs/Web/API/Request) object containing the currently authenticated `user`. |
| **`data`** | The data passed to create the document with. |
### Read
Returns a boolean which allows/denies access to the `read` request.
To add read Access Control to a Collection, use the `read` property in the [Collection Config](../configuration/collections):
```ts
import type { CollectionConfig } from 'payload'
export const CollectionWithReadAccess: CollectionConfig = {
// ...
access: {
// highlight-start
read: ({ req: { user } }) => {
return Boolean(user)
},
// highlight-end
},
}
```
<Banner type="success">
**Tip:** Return a [Query](../queries/overview) to limit the Documents to only
those that match the constraint. This can be helpful to restrict users' access
to specific Documents. [More details](../queries/overview).
</Banner>
As your application becomes more complex, you may want to define your function in a separate file and import them into your Collection Config:
```ts
import type { Access } from 'payload'
export const canReadPage: Access = ({ req: { user } }) => {
// Allow authenticated users
if (user) {
return true
}
// By returning a Query, guest users can read public Documents
// Note: this assumes you have a `isPublic` checkbox field on your Collection
return {
isPublic: {
equals: true,
},
}
}
```
The following arguments are provided to the `read` function:
| Option | Description |
| --------- | ----------------------------------------------------------------------------------------------------------------------------- |
| **`req`** | The [Request](https://developer.mozilla.org/en-US/docs/Web/API/Request) object containing the currently authenticated `user`. |
| **`id`** | `id` of document requested, if within `findByID`. |
### Update
Returns a boolean which allows/denies access to the `update` request.
To add update Access Control to a Collection, use the `update` property in the [Collection Config](../configuration/collections):
```ts
import type { CollectionConfig } from 'payload'
export const CollectionWithUpdateAccess: CollectionConfig = {
// ...
access: {
// highlight-start
update: ({ req: { user } }) => {
return Boolean(user)
},
// highlight-end
},
}
```
<Banner type="success">
**Tip:** Return a [Query](../queries/overview) to limit the Documents to only
those that match the constraint. This can be helpful to restrict users' access
to specific Documents. [More details](../queries/overview).
</Banner>
As your application becomes more complex, you may want to define your function in a separate file and import them into your Collection Config:
```ts
import type { Access } from 'payload'
export const canUpdateUser: Access = ({ req: { user }, id }) => {
// Allow users with a role of 'admin'
if (user.roles && user.roles.some((role) => role === 'admin')) {
return true
}
// allow any other users to update only oneself
return user.id === id
}
```
The following arguments are provided to the `update` function:
| Option | Description |
| ---------- | ----------------------------------------------------------------------------------------------------------------------------- |
| **`req`** | The [Request](https://developer.mozilla.org/en-US/docs/Web/API/Request) object containing the currently authenticated `user`. |
| **`id`** | `id` of document requested to update. |
| **`data`** | The data passed to update the document with. |
### Delete
Similarly to the Update function, returns a boolean or a [query constraint](/docs/queries/overview) to limit which documents can be deleted by which users.
To add delete Access Control to a Collection, use the `delete` property in the [Collection Config](../configuration/collections):
```ts
import type { CollectionConfig } from 'payload'
export const CollectionWithDeleteAccess: CollectionConfig = {
// ...
access: {
// highlight-start
delete: ({ req: { user } }) => {
return Boolean(user)
},
// highlight-end
},
}
```
As your application becomes more complex, you may want to define your function in a separate file and import them into your Collection Config:
```ts
import type { Access } from 'payload'
export const canDeleteCustomer: Access = async ({ req, id }) => {
if (!id) {
// allow the admin UI to show controls to delete since it is indeterminate without the `id`
return true
}
// Query another Collection using the `id`
const result = await req.payload.find({
collection: 'contracts',
limit: 0,
depth: 0,
where: {
customer: { equals: id },
},
})
return result.totalDocs === 0
}
```
The following arguments are provided to the `delete` function:
| Option | Description |
| --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **`req`** | The [Request](https://developer.mozilla.org/en-US/docs/Web/API/Request) object with additional `user` property, which is the currently logged in user. |
| **`id`** | `id` of document requested to delete. |
### Admin
If the Collection is used to access the [Admin Panel](../admin/overview#the-admin-user-collection), the `Admin` Access Control function determines whether or not the currently logged in user can access the admin UI.
To add Admin Access Control to a Collection, use the `admin` property in the [Collection Config](../configuration/collections):
```ts
import type { CollectionConfig } from 'payload'
export const CollectionWithAdminAccess: CollectionConfig = {
// ...
access: {
// highlight-start
admin: ({ req: { user } }) => {
return Boolean(user)
},
// highlight-end
},
}
```
The following arguments are provided to the `admin` function:
| Option | Description |
| --------- | ----------------------------------------------------------------------------------------------------------------------------- |
| **`req`** | The [Request](https://developer.mozilla.org/en-US/docs/Web/API/Request) object containing the currently authenticated `user`. |
### Unlock
Determines which users can [unlock](/docs/authentication/operations#unlock) other users who may be blocked from authenticating successfully due to [failing too many login attempts](/docs/authentication/overview#config-options).
To add Unlock Access Control to a Collection, use the `unlock` property in the [Collection Config](../configuration/collections):
```ts
import type { CollectionConfig } from 'payload'
export const CollectionWithUnlockAccess: CollectionConfig = {
// ...
access: {
// highlight-start
unlock: ({ req: { user } }) => {
return Boolean(user)
},
// highlight-end
},
}
```
The following arguments are provided to the `unlock` function:
| Option | Description |
| --------- | ----------------------------------------------------------------------------------------------------------------------------- |
| **`req`** | The [Request](https://developer.mozilla.org/en-US/docs/Web/API/Request) object containing the currently authenticated `user`. |
### Read Versions
If the Collection has [Versions](../versions/overview) enabled, the `readVersions` Access Control function determines whether or not the currently logged in user can access the version history of a Document.
To add Read Versions Access Control to a Collection, use the `readVersions` property in the [Collection Config](../configuration/collections):
```ts
import type { CollectionConfig } from 'payload'
export const CollectionWithVersionsAccess: CollectionConfig = {
// ...
access: {
// highlight-start
readVersions: ({ req: { user } }) => {
return Boolean(user)
},
// highlight-end
},
}
```
The following arguments are provided to the `readVersions` function:
| Option | Description |
| --------- | ----------------------------------------------------------------------------------------------------------------------------- |
| **`req`** | The [Request](https://developer.mozilla.org/en-US/docs/Web/API/Request) object containing the currently authenticated `user`. |

View File

@@ -1,105 +0,0 @@
---
title: Field-level Access Control
label: Fields
order: 40
desc: Field-level Access Control is specified within a field's config, and allows you to define which users can create, read or update Fields.
keywords: fields, access control, permissions, documentation, Content Management System, cms, headless, javascript, node, react, nextjs
---
Field Access Control is [Access Control](../access-control/overview) used to restrict access to specific [Fields](../fields/overview) within a Document.
To add Access Control to a Field, use the `access` property in your [Field Config](../fields/overview):
```ts
import type { Field } from 'payload'
export const FieldWithAccessControl: Field = {
// ...
access: {
// highlight-line
// ...
},
}
```
<Banner type="warning">
**Note:** Field Access Controls does not support returning
[Query](../queries/overview) constraints like [Collection Access
Control](./collections) does.
</Banner>
## Config Options
Access Control is specific to the operation of the request.
To add Access Control to a Field, use the `access` property in the [Field Config](../fields/overview):
```ts
import type { CollectionConfig } from 'payload';
export const Posts: CollectionConfig = {
slug: 'posts',
fields: [
{
name: 'title',
type: 'text',
// highlight-start
access: {
create: ({ req: { user } }) => { ... },
read: ({ req: { user } }) => { ... },
update: ({ req: { user } }) => { ... },
},
// highlight-end
};
],
};
```
The following options are available:
| Function | Purpose |
| ------------ | ---------------------------------------------------------------------------------------------------------- |
| **`create`** | Allows or denies the ability to set a field's value when creating a new document. [More details](#create). |
| **`read`** | Allows or denies the ability to read a field's value. [More details](#read). |
| **`update`** | Allows or denies the ability to update a field's value [More details](#update). |
### Create
Returns a boolean which allows or denies the ability to set a field's value when creating a new document. If `false` is returned, any passed values will be discarded.
**Available argument properties:**
| Option | Description |
| ----------------- | ---------------------------------------------------------------------------------------------------------------------------- |
| **`req`** | The [Request](https://developer.mozilla.org/en-US/docs/Web/API/Request) object containing the currently authenticated `user` |
| **`data`** | The full data passed to create the document. |
| **`siblingData`** | Immediately adjacent field data passed to create the document. |
### Read
Returns a boolean which allows or denies the ability to read a field's value. If `false`, the entire property is omitted from the resulting document.
**Available argument properties:**
| Option | Description |
| ----------------- | ---------------------------------------------------------------------------------------------------------------------------- |
| **`req`** | The [Request](https://developer.mozilla.org/en-US/docs/Web/API/Request) object containing the currently authenticated `user` |
| **`id`** | `id` of the document being read |
| **`doc`** | The full document data. |
| **`siblingData`** | Immediately adjacent field data of the document being read. |
### Update
Returns a boolean which allows or denies the ability to update a field's value. If `false` is returned, any passed values will be discarded.
If `false` is returned and you attempt to update the field's value, the operation will **not** throw an error however the field will be omitted from the update operation and the value will remain unchanged.
**Available argument properties:**
| Option | Description |
| ----------------- | ---------------------------------------------------------------------------------------------------------------------------- |
| **`req`** | The [Request](https://developer.mozilla.org/en-US/docs/Web/API/Request) object containing the currently authenticated `user` |
| **`id`** | `id` of the document being updated |
| **`data`** | The full data passed to update the document. |
| **`siblingData`** | Immediately adjacent field data passed to update the document with. |
| **`doc`** | The full document data, before the update is applied. |

View File

@@ -1,143 +0,0 @@
---
title: Globals Access Control
label: Globals
order: 30
desc: Global-level Access Control is specified within each Global's `access` property and allows you to define which users can read or update Globals.
keywords: globals, access control, permissions, documentation, Content Management System, cms, headless, javascript, node, react, nextjs
---
Global Access Control is [Access Control](../access-control/overview) used to restrict access to [Global](../configuration/globals) Documents, as well as what they can and cannot see within the [Admin Panel](../admin/overview) as it relates to that Global.
To add Access Control to a Global, use the `access` property in your [Global Config](../configuration/globals):
```ts
import type { GlobalConfig } from 'payload'
export const GlobalWithAccessControl: GlobalConfig = {
// ...
access: {
// highlight-line
// ...
},
}
```
## Config Options
Access Control is specific to the operation of the request.
To add Access Control to a [Global](../configuration/globals), use the `access` property in the [Global Config](../configuration/globals):
```ts
import { GlobalConfig } from 'payload'
const GlobalWithAccessControl: GlobalConfig = {
// ...
// highlight-start
access: {
read: ({ req: { user } }) => {...},
update: ({ req: { user } }) => {...},
// Version-enabled Globals only
readVersions: () => {...},
},
// highlight-end
}
export default Header
```
The following options are available:
| Function | Allows/Denies Access |
| ------------ | --------------------------------------------------------------- |
| **`read`** | Used in the `findOne` Global operation. [More details](#read). |
| **`update`** | Used in the `update` Global operation. [More details](#update). |
If a Global supports [Versions](../versions/overview), the following additional options are available:
| Function | Allows/Denies Access |
| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **`readVersions`** | Used to control who can read versions, and who can't. Will automatically restrict the Admin UI version viewing access. [More details](#read-versions). |
### Read
Returns a boolean result or optionally a [query constraint](../queries/overview) which limits who can read this global based on its current properties.
To add read Access Control to a [Global](../configuration/globals), use the `access` property in the [Global Config](../configuration/globals):
```ts
import { GlobalConfig } from 'payload'
const Header: GlobalConfig = {
// ...
// highlight-start
access: {
read: ({ req: { user } }) => {
return Boolean(user)
},
},
// highlight-end
}
```
The following arguments are provided to the `read` function:
| Option | Description |
| --------- | ----------------------------------------------------------------------------------------------------------------------------- |
| **`req`** | The [Request](https://developer.mozilla.org/en-US/docs/Web/API/Request) object containing the currently authenticated `user`. |
### Update
Returns a boolean result or optionally a [query constraint](../queries/overview) which limits who can update this global based on its current properties.
To add update Access Control to a [Global](../configuration/globals), use the `access` property in the [Global Config](../configuration/globals):
```ts
import { GlobalConfig } from 'payload'
const Header: GlobalConfig = {
// ...
// highlight-start
access: {
update: ({ req: { user }, data }) => {
return Boolean(user)
},
},
// highlight-end
}
```
The following arguments are provided to the `update` function:
| Option | Description |
| ---------- | ----------------------------------------------------------------------------------------------------------------------------- |
| **`req`** | The [Request](https://developer.mozilla.org/en-US/docs/Web/API/Request) object containing the currently authenticated `user`. |
| **`data`** | The data passed to update the global with. |
### Read Versions
If the Global has [Versions](../versions/overview) enabled, the `readVersions` Access Control function determines whether or not the currently logged in user can access the version history of a Document.
To add Read Versions Access Control to a Collection, use the `readVersions` property in the [Global Config](../configuration/globals):
```ts
import type { GlobalConfig } from 'payload'
export const GlobalWithVersionsAccess: GlobalConfig = {
// ...
access: {
// highlight-start
readVersions: ({ req: { user } }) => {
return Boolean(user)
},
// highlight-end
},
}
```
The following arguments are provided to the `readVersions` function:
| Option | Description |
| --------- | ----------------------------------------------------------------------------------------------------------------------------- |
| **`req`** | The [Request](https://developer.mozilla.org/en-US/docs/Web/API/Request) object containing the currently authenticated `user`. |

View File

@@ -1,82 +0,0 @@
---
title: Access Control
label: Overview
order: 10
desc: Payload makes it simple to define and manage Access Control. By declaring roles, you can set permissions and restrict what your users can interact with.
keywords: overview, access control, permissions, documentation, Content Management System, cms, headless, javascript, node, react, nextjs
---
<YouTube id="DoPLyXG26Dg" title="Overview of Payload Access Control" />
Access Control determines what a user can and cannot do with any given Document, as well as what they can and cannot see within the [Admin Panel](../admin/overview). By implementing Access Control, you can define granular restrictions based on the user, their roles (RBAC), Document data, or any other criteria your application requires.
Access Control functions are scoped to the _operation_, meaning you can have different rules for `create`, `read`, `update`, `delete`, etc. Access Control functions are executed _before_ any changes are made and _before_ any operations are completed. This allows you to determine if the user has the necessary permissions before fulfilling the request.
There are many use cases for Access Control, including:
- Allowing anyone `read` access to all posts
- Only allowing public access to posts where a `status` field is equal to `published`
- Giving only users with a `role` field equal to `admin` the ability to delete posts
- Allowing anyone to submit contact forms, but only logged in users to `read`, `update` or `delete` them
- Restricting a user to only be able to see their own orders, but no-one else's
- Allowing users that belong to a certain organization to access only that organization's resources
There are three main types of Access Control in Payload:
- [Collection Access Control](./collections)
- [Global Access Control](./globals)
- [Field Access Control](./fields)
## Default Access Control
Payload provides default Access Control so that your data is secured behind [Authentication](../authentication/overview) without additional configuration. To do this, Payload sets a default function that simply checks if a user is present on the request. You can override this default behavior by defining your own Access Control functions as needed.
Here is the default Access Control that Payload provides:
```ts
const defaultPayloadAccess = ({ req: { user } }) => {
// Return `true` if a user is found
// and `false` if it is undefined or null
return Boolean(user) // highlight-line
}
```
<Banner type="warning">
**Important:** In the [Local API](../local-api/overview), all Access Control
is _skipped_ by default. This allows your server to have full control over
your application. To opt back in, you can set the `overrideAccess` option to
`false` in your requests.
</Banner>
## The Access Operation
The Admin Panel responds dynamically to your changes to Access Control. For example, if you restrict editing `ExampleCollection` to only users that feature an "admin" role, Payload will **hide** that Collection from the Admin Panel entirely. This is super powerful and allows you to control who can do what within your Admin Panel using the same functions that secure your APIs.
To accomplish this, Payload exposes the [Access Operation](../authentication/operations#access). Upon login, Payload executes each Access Control function at the top level, across all Collections, Globals, and Fields, and returns a response that contains a reflection of what the currently authenticated user can do within your application.
<Banner type="warning">
**Important:** When your access control functions are executed via the [Access
Operation](../authentication/operations#access), the `id` and `data` arguments
will be `undefined`. This is because Payload is executing your functions
without referencing a specific Document.
</Banner>
If you use `id` or `data` within your access control functions, make sure to check that they are defined first. If they are not, then you can assume that your Access Control is being executed via the Access Operation to determine solely what the user can do within the Admin Panel.
## Locale Specific Access Control
To implement locale-specific access control, you can use the `req.locale` argument in your access control functions. This argument allows you to evaluate the current locale of the request and determine access permissions accordingly.
Here is an example:
```ts
const access = ({ req }) => {
// Grant access if the locale is 'en'
if (req.locale === 'en') {
return true
}
// Deny access for all other locales
return false
}
```

View File

@@ -1,89 +0,0 @@
---
title: Customizing CSS & SCSS
label: Customizing CSS
order: 50
desc: Customize the Payload Admin Panel further by adding your own CSS or SCSS style sheet to the configuration, powerful theme and design options are waiting for you.
keywords: admin, css, scss, documentation, Content Management System, cms, headless, javascript, node, react, nextjs
---
Customizing the Payload [Admin Panel](./overview) through CSS alone is one of the easiest and most powerful ways to customize the look and feel of the dashboard. To allow for this level of customization, Payload:
1. Exposes a [root-level stylesheet](#global-css) for you to inject custom selectors
1. Provides a [CSS library](#css-library) that can be easily overridden or extended
1. Uses [BEM naming conventions](http://getbem.com) so that class names are globally accessible
To customize the CSS within the Admin UI, determine scope and change you'd like to make, and then add your own CSS or SCSS to the configuration as needed.
## Global CSS
Global CSS refers to the CSS that is applied to the entire [Admin Panel](./overview). This is where you can have a significant impact to the look and feel of the Admin UI through just a few lines of code.
You can add your own global CSS through the root `custom.scss` file of your app. This file is loaded into the root of the Admin Panel and can be used to inject custom selectors or styles however needed.
Here is an example of how you might target the Dashboard View and change the background color:
```scss
.dashboard {
background-color: red; // highlight-line
}
```
<Banner type="warning">
**Note:** If you are building [Custom
Components](../custom-components/overview), it is best to import your own
stylesheets directly into your components, rather than using the global
stylesheet. You can continue to use the [CSS library](#css-library) as needed.
</Banner>
### Specificity rules
All Payload CSS is encapsulated inside CSS layers under `@layer payload-default`. Any custom css will now have the highest possible specificity.
We have also provided a layer `@layer payload` if you want to use layers and ensure that your styles are applied after payload.
To override existing styles in a way that the previous rules of specificity would be respected you can use the default layer like so
```css
@layer payload-default {
// my styles within the Payload specificity
}
```
## Re-using Payload SCSS variables and utilities
You can re-use Payload's SCSS variables and utilities in your own stylesheets by importing it from the UI package.
```scss
@import '~@payloadcms/ui/scss';
```
## CSS Library
To make it as easy as possible for you to override default styles, Payload uses [BEM naming conventions](http://getbem.com/) for all CSS within the Admin UI. If you provide your own CSS, you can override any built-in styles easily, including targeting nested components and their various component states.
You can also override Payload's built-in [CSS Variables](https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_custom_properties). These variables are widely consumed by the Admin Panel, so modifying them has a significant impact on the look and feel of the Admin UI.
The following variables are defined and can be overridden:
- [Breakpoints](https://github.com/payloadcms/payload/blob/main/packages/ui/src/scss/queries.scss)
- [Colors](https://github.com/payloadcms/payload/blob/main/packages/ui/src/scss/colors.scss)
- Base color shades (white to black by default)
- Success / warning / error color shades
- Theme-specific colors (background, input background, text color, etc.)
- Elevation colors (used to determine how "bright" something should be when compared to the background)
- [Sizing](https://github.com/payloadcms/payload/blob/main/packages/ui/src/scss/app.scss)
- Horizontal gutter
- Transition speeds
- Font sizes
- Etc.
For an up-to-date, comprehensive list of all available variables, please refer to the [Source Code](https://github.com/payloadcms/payload/blob/main/packages/ui/src/scss).
<Banner type="warning">
**Warning:** If you're overriding colors or theme elevations, make sure to
consider how [your changes will affect dark mode](#dark-mode).
</Banner>
#### Dark Mode
Colors are designed to automatically adapt to theme of the [Admin Panel](./overview). By default, Payload automatically overrides all `--theme-elevation` colors and inverts all success / warning / error shades to suit dark mode. We also update some base theme variables like `--theme-bg`, `--theme-text`, etc.

View File

@@ -1,242 +0,0 @@
---
title: Page Metadata
label: Metadata
order: 70
desc: Customize the metadata of your pages within the Admin Panel
keywords: admin, components, custom, documentation, Content Management System, cms, headless, javascript, node, react, nextjs
---
Every page within the Admin Panel automatically receives dynamic, auto-generated metadata derived from live document data, the user's current locale, and more. This includes the page title, description, og:image, etc. and requires no additional configuration.
Metadata is fully configurable at the root level and cascades down to individual collections, documents, and custom views. This allows for the ability to control metadata on any page with high precision, while also providing sensible defaults.
All metadata is injected into Next.js' [`generateMetadata`](https://nextjs.org/docs/app/api-reference/functions/generate-metadata) function. This used to generate the `<head>` of pages within the Admin Panel. All metadata options that are available in Next.js are exposed by Payload.
Within the Admin Panel, metadata can be customized at the following levels:
- [Root Metadata](#root-metadata)
- [Collection Metadata](#collection-metadata)
- [Global Metadata](#global-metadata)
- [View Metadata](#view-metadata)
All of these types of metadata share a similar structure, with a few key differences on the Root level. To customize metadata, consult the list of available scopes. Determine the scope that corresponds to what you are trying to accomplish, then author your metadata within the Payload Config accordingly.
## Root Metadata
Root Metadata is the metadata that is applied to all pages within the Admin Panel. This is where you can control things like the suffix appended onto each page's title, the favicon displayed in the browser's tab, and the Open Graph data that is used when sharing the Admin Panel on social media.
To customize Root Metadata, use the `admin.meta` key in your Payload Config:
```ts
{
// ...
admin: {
// highlight-start
meta: {
// highlight-end
title: 'My Admin Panel',
description: 'The best admin panel in the world',
icons: [
{
rel: 'icon',
type: 'image/png',
url: '/favicon.png',
},
],
},
},
}
```
The following options are available for Root Metadata:
| Key | Type | Description |
| -------------------- | --------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `defaultOGImageType` | `dynamic` (default), `static`, or `off` | The type of default OG image to use. If set to `dynamic`, Payload will use Next.js image generation to create an image with the title of the page. If set to `static`, Payload will use the `defaultOGImage` URL. If set to `off`, Payload will not generate an OG image. |
| `titleSuffix` | `string` | A suffix to append to the end of the title of every page. Defaults to "- Payload". |
| `[keyof Metadata]` | `unknown` | Any other properties that Next.js supports within the `generateMetadata` function. [More details](https://nextjs.org/docs/app/api-reference/functions/generate-metadata). |
<Banner type="success">
**Reminder:** These are the _root-level_ options for the Admin Panel. You can
also customize metadata on the [Collection](../configuration/collections),
[Global](../configuration/globals), and Document levels through their
respective configs.
</Banner>
### Icons
The Icons Config corresponds to the `<link>` tags that are used to specify icons for the Admin Panel. The `icons` key is an array of objects, each of which represents an individual icon. Icons are differentiated from one another by their `rel` attribute, which specifies the relationship between the document and the icon.
The most common icon type is the favicon, which is displayed in the browser tab. This is specified by the `rel` attribute `icon`. Other common icon types include `apple-touch-icon`, which is used by Apple devices when the Admin Panel is saved to the home screen, and `mask-icon`, which is used by Safari to mask the Admin Panel icon.
To customize icons, use the `admin.meta.icons` property in your Payload Config:
```ts
{
// ...
admin: {
meta: {
// highlight-start
icons: [
// highlight-end
{
rel: 'icon',
type: 'image/png',
url: '/favicon.png',
},
{
rel: 'apple-touch-icon',
type: 'image/png',
url: '/apple-touch-icon.png',
},
],
},
},
}
```
For a full list of all available Icon options, see the [Next.js documentation](https://nextjs.org/docs/app/api-reference/functions/generate-metadata#icons).
### Open Graph
Open Graph metadata is a set of tags that are used to control how URLs are displayed when shared on social media platforms. Open Graph metadata is automatically generated by Payload, but can be customized at the Root level.
To customize Open Graph metadata, use the `admin.meta.openGraph` property in your Payload Config:
```ts
{
// ...
admin: {
meta: {
// highlight-start
openGraph: {
// highlight-end
description: 'The best admin panel in the world',
images: [
{
url: 'https://example.com/image.jpg',
width: 800,
height: 600,
},
],
siteName: 'Payload',
title: 'My Admin Panel',
},
},
},
}
```
For a full list of all available Open Graph options, see the [Next.js documentation](https://nextjs.org/docs/app/api-reference/functions/generate-metadata#opengraph).
### Robots
Setting the `robots` property will allow you to control the `robots` meta tag that is rendered within the `<head>` of the Admin Panel. This can be used to control how search engines index pages and displays them in search results.
By default, the Admin Panel is set to prevent search engines from indexing pages within the Admin Panel.
To customize the Robots Config, use the `admin.meta.robots` property in your Payload Config:
```ts
{
// ...
admin: {
meta: {
// highlight-start
robots: 'noindex, nofollow',
// highlight-end
},
},
}
```
For a full list of all available Robots options, see the [Next.js documentation](https://nextjs.org/docs/app/api-reference/functions/generate-metadata#robots).
##### Prevent Crawling
While setting meta tags via `admin.meta.robots` can prevent search engines from _indexing_ web pages, it does not prevent them from being _crawled_.
To prevent your pages from being crawled altogether, add a `robots.txt` file to your root directory.
```text
User-agent: *
Disallow: /admin/
```
<Banner type="info">
**Note:** If you've customized the path to your Admin Panel via
`config.routes`, be sure to update the `Disallow` directive to match your
custom path.
</Banner>
## Collection Metadata
Collection Metadata is the metadata that is applied to all pages within any given Collection within the Admin Panel. This metadata is used to customize the title and description of all views within any given Collection, unless overridden by the view itself.
To customize Collection Metadata, use the `admin.meta` key within your Collection Config:
```ts
import type { CollectionConfig } from 'payload'
export const MyCollection: CollectionConfig = {
// ...
admin: {
// highlight-start
meta: {
// highlight-end
title: 'My Collection',
description: 'The best collection in the world',
},
},
}
```
The Collection Meta config has the same options as the [Root Metadata](#root-metadata) config.
## Global Metadata
Global Metadata is the metadata that is applied to all pages within any given Global within the Admin Panel. This metadata is used to customize the title and description of all views within any given Global, unless overridden by the view itself.
To customize Global Metadata, use the `admin.meta` key within your Global Config:
```ts
import { GlobalConfig } from 'payload'
export const MyGlobal: GlobalConfig = {
// ...
admin: {
// highlight-start
meta: {
// highlight-end
title: 'My Global',
description: 'The best admin panel in the world',
},
},
}
```
The Global Meta config has the same options as the [Root Metadata](#root-metadata) config.
## View Metadata
View Metadata is the metadata that is applied to specific [Views](../custom-components/custom-views) within the Admin Panel. This metadata is used to customize the title and description of a specific view, overriding any metadata set at the [Root](#root-metadata), [Collection](#collection-metadata), or [Global](#global-metadata) level.
To customize View Metadata, use the `meta` key within your View Config:
```ts
{
// ...
admin: {
views: {
dashboard: {
// highlight-start
meta: {
// highlight-end
title: 'My Dashboard',
description: 'The best dashboard in the world',
}
},
},
},
}
```

View File

@@ -1,300 +0,0 @@
---
title: The Admin Panel
label: Overview
order: 10
desc: Manage your data and customize the Payload Admin Panel by swapping in your own React components. Create, modify or remove views, fields, styles and much more.
keywords: admin, components, custom, customize, documentation, Content Management System, cms, headless, javascript, node, react, nextjs
---
Payload dynamically generates a beautiful, [fully type-safe](../typescript/overview) Admin Panel to manage your users and data. It is highly performant, even with 100+ fields, and is translated in over 30 languages. Within the Admin Panel you can manage content, [render your site](../live-preview/overview), [preview drafts](./preview), [diff versions](../versions/overview), and so much more.
The Admin Panel is designed to [white-label your brand](https://payloadcms.com/blog/white-label-admin-ui). You can endlessly customize and extend the Admin UI by swapping in your own [Custom Components](../custom-components/overview)—everything from simple field labels to entire views can be modified or replaced to perfectly tailor the interface for your editors.
The Admin Panel is written in [TypeScript](https://www.typescriptlang.org) and built with [React](https://react.dev) using the [Next.js App Router](https://nextjs.org/docs/app). It supports [React Server Components](https://react.dev/reference/rsc/server-components), enabling the use of the [Local API](/docs/local-api/overview) on the front-end. You can install Payload into any [existing Next.js app in just one line](../getting-started/installation) and [deploy it anywhere](../production/deployment).
<Banner type="success">
The Payload Admin Panel is designed to be as minimal and straightforward as
possible to allow easy customization and control. [Learn
more](../custom-components/overview).
</Banner>
<LightDarkImage
srcLight="https://payloadcms.com/images/docs/admin.jpg"
srcDark="https://payloadcms.com/images/docs/admin-dark.jpg"
alt="Admin Panel with collapsible sidebar"
caption="Redesigned Admin Panel with a collapsible sidebar that's open by default, providing greater extensibility and enhanced horizontal real estate."
/>
## Project Structure
The Admin Panel serves as the entire HTTP layer for Payload, providing a full CRUD interface for your app. This means that both the [REST](../rest-api/overview) and [GraphQL](../graphql/overview) APIs are simply [Next.js Routes](https://nextjs.org/docs/app/building-your-application/routing) that exist directly alongside your front-end application.
Once you [install Payload](../getting-started/installation), the following files and directories will be created in your app:
```plaintext
app
├─ (payload)
├── admin
├─── [[...segments]]
├──── page.tsx
├──── not-found.tsx
├── api
├─── [...slug]
├──── route.ts
├── graphql
├──── route.ts
├── graphql-playground
├──── route.ts
├── custom.scss
├── layout.tsx
```
<Banner type="info">
If you are not familiar with Next.js project structure, you can [learn more
about it here](https://nextjs.org/docs/getting-started/project-structure).
</Banner>
As shown above, all Payload routes are nested within the `(payload)` route group. This creates a boundary between the Admin Panel and the rest of your application by scoping all layouts and styles. The `layout.tsx` file within this directory, for example, is where Payload manages the `html` tag of the document to set proper [`lang`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/lang) and [`dir`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/dir) attributes, etc.
The `admin` directory contains all the _pages_ related to the interface itself, whereas the `api` and `graphql` directories contains all the _routes_ related to the [REST API](../rest-api/overview) and [GraphQL API](../graphql/overview). All admin routes are [easily configurable](#customizing-routes) to meet your application's exact requirements.
<Banner type="warning">
**Note:** If you don't intend to use the Admin Panel, [REST
API](../rest-api/overview), or [GraphQL API](../graphql/overview), you can
opt-out by simply deleting their corresponding directories within your Next.js
app. The overhead, however, is completely constrained to these routes, and
will not slow down or affect Payload outside when not in use.
</Banner>
Finally, the `custom.scss` file is where you can add or override globally-oriented styles in the Admin Panel, such as modify the color palette. Customizing the look and feel through CSS alone is a powerful feature of the Admin Panel, [more on that here](./customizing-css).
All auto-generated files will contain the following comments at the top of each file:
```tsx
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */,
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
```
## Admin Options
All options for the Admin Panel are defined in your [Payload Config](../configuration/overview) under the `admin` property:
```ts
import { buildConfig } from 'payload'
const config = buildConfig({
// ...
// highlight-start
admin: {
// ...
},
// highlight-end
})
```
The following options are available:
| Option | Description |
| -------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
| `avatar` | Set account profile picture. Options: `gravatar`, `default` or a custom React component. |
| `autoLogin` | Used to automate log-in for dev and demonstration convenience. [More details](../authentication/overview). |
| `components` | Component overrides that affect the entirety of the Admin Panel. [More details](../custom-components/overview). |
| `custom` | Any custom properties you wish to pass to the Admin Panel. |
| `dateFormat` | The date format that will be used for all dates within the Admin Panel. Any valid [date-fns](https://date-fns.org/) format pattern can be used. |
| `livePreview` | Enable real-time editing for instant visual feedback of your front-end application. [More details](../live-preview/overview). |
| `meta` | Base metadata to use for the Admin Panel. [More details](./metadata). |
| `routes` | Replace built-in Admin Panel routes with your own custom routes. [More details](#customizing-routes). |
| `suppressHydrationWarning` | If set to `true`, suppresses React hydration mismatch warnings during the hydration of the root `<html>` tag. Defaults to `false`. |
| `theme` | Restrict the Admin Panel theme to use only one of your choice. Default is `all`. |
| `timezones` | Configure the timezone settings for the admin panel. [More details](#timezones) |
| `user` | The `slug` of the Collection that you want to allow to login to the Admin Panel. [More details](#the-admin-user-collection). |
<Banner type="success">
**Reminder:** These are the _root-level_ options for the Admin Panel. You can
also customize [Collection Admin
Options](../configuration/collections#admin-options) and [Global Admin
Options](../configuration/globals#admin-options) through their respective
`admin` keys.
</Banner>
### The Admin User Collection
To specify which Collection to allow to login to the Admin Panel, pass the `admin.user` key equal to the slug of any auth-enabled Collection:
```ts
import { buildConfig } from 'payload'
const config = buildConfig({
// ...
admin: {
user: 'admins', // highlight-line
},
})
```
<Banner type="warning">
**Important:**
The Admin Panel can only be used by a single auth-enabled Collection. To enable authentication for a Collection, simply set `auth: true` in the Collection's configuration. See [Authentication](../authentication/overview) for more information.
</Banner>
By default, if you have not specified a Collection, Payload will automatically provide a `User` Collection with access to the Admin Panel. You can customize or override the fields and settings of the default `User` Collection by adding your own Collection with `slug: 'users'`. Doing this will force Payload to use your provided `User` Collection instead of its default version.
You can use whatever Collection you'd like to access the Admin Panel as long as the Collection supports [Authentication](/docs/authentication/overview). It doesn't need to be called `users`. For example, you may wish to have two Collections that both support authentication:
- `admins` - meant to have a higher level of permissions to manage your data and access the Admin Panel
- `customers` - meant for end users of your app that should not be allowed to log into the Admin Panel
To do this, specify `admin: { user: 'admins' }` in your config. This will provide access to the Admin Panel to only `admins`. Any users authenticated as `customers` will be prevented from accessing the Admin Panel. See [Access Control](/docs/access-control/overview) for full details.
### Role-based Access Control
It is also possible to allow multiple user types into the Admin Panel with limited permissions, known as role-based access control (RBAC). For example, you may wish to have two roles within the `admins` Collection:
- `super-admin` - full access to the Admin Panel to perform any action
- `editor` - limited access to the Admin Panel to only manage content
To do this, add a `roles` or similar field to your auth-enabled Collection, then use the `access.admin` property to grant or deny access based on the value of that field. See [Access Control](/docs/access-control/overview) for full details. For a complete, working example of role-based access control, check out the official [Auth Example](https://github.com/payloadcms/payload/tree/main/examples/auth).
## Customizing Routes
You have full control over the routes that Payload binds itself to. This includes both [Root-level Routes](#root-level-routes) such as the [REST API](../rest-api/overview), and [Admin-level Routes](#admin-level-routes) such as the user's account page. You can customize these routes to meet the needs of your application simply by specifying the desired paths in your config.
### Root-level Routes
Root-level routes are those that are not behind the `/admin` path, such as the [REST API](../rest-api/overview) and [GraphQL API](../graphql/overview), or the root path of the Admin Panel itself.
To customize root-level routes, use the `routes` property in your [Payload Config](../configuration/overview):
```ts
import { buildConfig } from 'payload'
const config = buildConfig({
// ...
routes: {
admin: '/custom-admin-route', // highlight-line
},
})
```
The following options are available:
| Option | Default route | Description |
| ------------------- | --------------------- | ------------------------------------------------- |
| `admin` | `/admin` | The Admin Panel itself. |
| `api` | `/api` | The [REST API](../rest-api/overview) base path. |
| `graphQL` | `/graphql` | The [GraphQL API](../graphql/overview) base path. |
| `graphQLPlayground` | `/graphql-playground` | The GraphQL Playground. |
<Banner type="warning">
**Important:** Changing Root-level Routes also requires a change to [Project
Structure](#project-structure) to match the new route. [More
details](#customizing-root-level-routes).
</Banner>
<Banner type="success">
**Tip:** You can easily add _new_ routes to the Admin Panel through [Custom
Endpoints](../rest-api/overview#custom-endpoints) and [Custom
Views](../custom-components/custom-views).
</Banner>
#### Customizing Root-level Routes
You can change the Root-level Routes as needed, such as to mount the Admin Panel at the root of your application.
This change, however, also requires a change to your [Project Structure](#project-structure) to match the new route.
For example, if you set `routes.admin` to `/`:
```ts
import { buildConfig } from 'payload'
const config = buildConfig({
// ...
routes: {
admin: '/', // highlight-line
},
})
```
Then you would need to completely remove the `admin` directory from the project structure:
```plaintext
app
├─ (payload)
├── [[...segments]]
├──── ...
├── layout.tsx
```
<Banner type="warning">
**Note:** If you set Root-level Routes _before_ auto-generating the Admin
Panel via `create-payload-app`, your [Project Structure](#project-structure)
will already be set up correctly.
</Banner>
### Admin-level Routes
Admin-level routes are those behind the `/admin` path. These are the routes that are part of the Admin Panel itself, such as the user's account page, the login page, etc.
To customize admin-level routes, use the `admin.routes` property in your [Payload Config](../configuration/overview):
```ts
import { buildConfig } from 'payload'
const config = buildConfig({
// ...
admin: {
routes: {
account: '/my-account', // highlight-line
},
},
})
```
The following options are available:
| Option | Default route | Description |
| ----------------- | -------------------- | ----------------------------------------- |
| `account` | `/account` | The user's account page. |
| `createFirstUser` | `/create-first-user` | The page to create the first user. |
| `forgot` | `/forgot` | The password reset page. |
| `inactivity` | `/logout-inactivity` | The page to redirect to after inactivity. |
| `login` | `/login` | The login page. |
| `logout` | `/logout` | The logout page. |
| `reset` | `/reset` | The password reset page. |
| `unauthorized` | `/unauthorized` | The unauthorized page. |
<Banner type="success">
**Note:** You can also swap out entire _views_ out for your own, using the
`admin.views` property of the Payload Config. See [Custom
Views](../custom-components/custom-views) for more information.
</Banner>
## I18n
The Payload Admin Panel is translated in over [30 languages and counting](https://github.com/payloadcms/payload/tree/main/packages/translations). Languages are automatically detected based on the user's browser and used by the Admin Panel to display all text in that language. If no language was detected, or if the user's language is not yet supported, English will be chosen. Users can easily specify their language by selecting one from their account page. See [I18n](../configuration/i18n) for more information.
## Light and Dark Modes
Users in the Admin Panel have the ability to choose between light mode and dark mode for their editing experience. Users can select their preferred theme from their account page. Once selected, it is saved to their user's preferences and persisted across sessions and devices. If no theme was selected, the Admin Panel will automatically detect the operation system's theme and use that as the default.
## Timezones
The `admin.timezones` configuration allows you to configure timezone settings for the Admin Panel. You can customise the available list of timezones and in the future configure the default timezone for the Admin Panel and for all users.
The following options are available:
| Option | Description |
| -------------------- | --------------------------------------------------------------------------------------------------------------- |
| `supportedTimezones` | An array of label/value options for selectable timezones where the value is the IANA name eg. `America/Detroit` |
| `defaultTimezone` | The `value` of the default selected timezone. eg. `America/Los_Angeles` |
We validate the supported timezones array by checking the value against the list of IANA timezones supported via the Intl API, specifically `Intl.supportedValuesOf('timeZone')`.
<Banner type="info">
**Important** You must enable timezones on each individual date field via
`timezone: true`. See [Date Fields](../fields/overview#date) for more
information.
</Banner>

View File

@@ -1,146 +0,0 @@
---
title: Managing User Preferences
label: Preferences
order: 60
desc: Store the preferences of your users as they interact with the Admin Panel.
keywords: admin, preferences, custom, customize, documentation, Content Management System, cms, headless, javascript, node, react, nextjs
---
As your users interact with the [Admin Panel](./overview), you might want to store their preferences in a persistent manner, so that when they revisit the Admin Panel in a different session or from a different device, they can pick right back up where they left off.
Out of the box, Payload handles the persistence of your users' preferences in a handful of ways, including:
1. Columns in the Collection List View: their active state and order
1. The user's last active [Locale](../configuration/localization)
1. The "collapsed" state of `blocks`, `array`, and `collapsible` fields
1. The last-known state of the `Nav` component, etc.
<Banner type="warning">
**Important:**
All preferences are stored on an individual user basis. Payload automatically recognizes the user
that is reading or setting a preference via all provided authentication methods.
</Banner>
## Use Cases
This API is used significantly for internal operations of the Admin Panel, as mentioned above. But, if you're building your own React components for use in the Admin Panel, you can allow users to set their own preferences in correspondence to their usage of your components. For example:
- If you have built a "color picker", you could "remember" the last used colors that the user has set for easy access next time
- If you've built a custom `Nav` component, and you've built in an "accordion-style" UI, you might want to store the `collapsed` state of each Nav collapsible item. This way, if an editor returns to the panel, their `Nav` state is persisted automatically
- You might want to store `recentlyAccessed` documents to give admin editors an easy shortcut back to their recently accessed documents on the `Dashboard` or similar
- Many other use cases exist. Invent your own! Give your editors an intelligent and persistent editing experience.
## Database
Payload automatically creates an internally used `payload-preferences` Collection that stores user preferences. Each document in the `payload-preferences` Collection contains the following shape:
| Key | Value |
| ----------------- | ----------------------------------------------------------------- |
| `id` | A unique ID for each preference stored. |
| `key` | A unique `key` that corresponds to the preference. |
| `user.value` | The ID of the `user` that is storing its preference. |
| `user.relationTo` | The `slug` of the Collection that the `user` is logged in as. |
| `value` | The value of the preference. Can be any data shape that you need. |
| `createdAt` | A timestamp of when the preference was created. |
| `updatedAt` | A timestamp set to the last time the preference was updated. |
## APIs
Preferences are available to both [GraphQL](/docs/graphql/overview#preferences) and [REST](/docs/rest-api/overview#preferences) APIs.
## Adding or reading Preferences in your own components
The Payload Admin Panel offers a `usePreferences` hook. The hook is only meant for use within the Admin Panel itself. It provides you with two methods:
#### `getPreference`
This async method provides an easy way to retrieve a user's preferences by `key`. It will return a promise containing the resulting preference value.
**Arguments**
- `key`: the `key` of your preference to retrieve.
#### `setPreference`
Also async, this method provides you with an easy way to set a user preference. It returns `void`.
**Arguments:**
- `key`: the `key` of your preference to set.
- `value`: the `value` of your preference that you're looking to set.
## Example
Here is an example for how you can utilize `usePreferences` within your custom Admin Panel components. Note - this example is not fully useful and is more just a reference for how to utilize the Preferences API. In this case, we are demonstrating how to set and retrieve a user's last used colors history within a `ColorPicker` or similar type component.
```tsx
'use client'
import React, { Fragment, useState, useEffect, useCallback } from 'react'
import { usePreferences } from '@payloadcms/ui'
const lastUsedColorsPreferenceKey = 'last-used-colors'
export function CustomComponent() {
const { getPreference, setPreference } = usePreferences()
// Store the last used colors in local state
const [lastUsedColors, setLastUsedColors] = useState([])
// Callback to add a color to the last used colors
const updateLastUsedColors = useCallback(
(color) => {
// First, check if color already exists in last used colors.
// If it already exists, there is no need to update preferences
const colorAlreadyExists = lastUsedColors.indexOf(color) > -1
if (!colorAlreadyExists) {
const newLastUsedColors = [...lastUsedColors, color]
setLastUsedColors(newLastUsedColors)
setPreference(lastUsedColorsPreferenceKey, newLastUsedColors)
}
},
[lastUsedColors, setPreference],
)
// Retrieve preferences on component mount
// This will only be run one time, because the `getPreference` method never changes
useEffect(() => {
const asyncGetPreference = async () => {
const lastUsedColorsFromPreferences = await getPreference(
lastUsedColorsPreferenceKey,
)
setLastUsedColors(lastUsedColorsFromPreferences)
}
asyncGetPreference()
}, [getPreference])
return (
<div>
<button type="button" onClick={() => updateLastUsedColors('red')}>
Use red
</button>
<button type="button" onClick={() => updateLastUsedColors('blue')}>
Use blue
</button>
<button type="button" onClick={() => updateLastUsedColors('purple')}>
Use purple
</button>
<button type="button" onClick={() => updateLastUsedColors('yellow')}>
Use yellow
</button>
{lastUsedColors && (
<Fragment>
<h5>Last used colors:</h5>
<ul>
{lastUsedColors?.map((color) => <li key={color}>{color}</li>)}
</ul>
</Fragment>
)}
</div>
)
}
```

View File

@@ -1,233 +0,0 @@
---
title: Preview
label: Preview
order: 30
desc: Enable links to your front-end to preview published or draft content.
keywords: admin, components, preview, documentation, Content Management System, cms, headless, javascript, node, react, nextjs
---
Preview is a feature that allows you to generate a direct link to your front-end application. When enabled, a "preview" button will appear on the Edit View within the [Admin Panel](./overview) with an href pointing to the URL you provide. This will provide your editors with a quick way of navigating to the front-end application where that Document's data is represented. Otherwise, they'd have to determine that URL themselves which is not always straightforward especially in complex apps.
The Preview feature can also be used to achieve something known as "Draft Preview". With Draft Preview, you can navigate to your front-end application and enter "draft mode", where your queries are modified to fetch draft content instead of published content. This is useful for seeing how your content will look before being published. [More details](#draft-preview).
<Banner type="warning">
**Note:** Preview is different than [Live Preview](../live-preview/overview).
Live Preview loads your app within an iframe and renders it in the Admin Panel
allowing you to see changes in real-time. Preview, on the other hand, allows
you to generate a direct link to your front-end application.
</Banner>
To add Preview, pass a function to the `admin.preview` property in any [Collection Config](../configuration/collections#admin-options) or [Global Config](../configuration/globals#admin-options):
```ts
import type { CollectionConfig } from 'payload'
export const Pages: CollectionConfig = {
slug: 'pages',
admin: {
preview: ({ slug }) => `http://localhost:3000/${slug}`,
},
fields: [
{
name: 'slug',
type: 'text',
},
],
}
```
## Options
The `preview` function resolves to a string that points to your front-end application with additional URL parameters. This can be an absolute URL or a relative path, and can run async if needed.
The following arguments are provided to the `preview` function:
| Path | Description |
| ------------- | ------------------------------------------------------------------------------------------ |
| **`doc`** | The data of the Document being edited. This includes changes that have not yet been saved. |
| **`options`** | An object with additional properties. |
The `options` object contains the following properties:
| Path | Description |
| ------------ | ----------------------------------------------------- |
| **`locale`** | The current locale of the Document being edited. |
| **`req`** | The Payload Request object. |
| **`token`** | The JWT token of the currently authenticated in user. |
If your application requires a fully qualified URL, such as within deploying to Vercel Preview Deployments, you can use the `req` property to build this URL:
```ts
preview: (doc, { req }) => `${req.protocol}//${req.host}/${doc.slug}` // highlight-line
```
## Draft Preview
The Preview feature can be used to achieve "Draft Preview". After clicking the preview button from the Admin Panel, you can enter into "draft mode" within your front-end application. This will allow you to adjust your page queries to include the `draft: true` param. When this param is present on the request, Payload will send back a draft document as opposed to a published one based on the document's `_status` field.
To enter draft mode, the URL provided to the `preview` function can point to a custom endpoint in your front-end application that sets a cookie or session variable to indicate that draft mode is enabled. This is framework specific, so the mechanisms here vary from framework to framework although the underlying concept is the same.
### Next.js
If you're using Next.js, you can do the following code to enter [Draft Mode](https://nextjs.org/docs/app/building-your-application/configuring/draft-mode).
#### Step 1: Format the Preview URL
First, format your `admin.preview` function to point to a custom endpoint that you'll open on your front-end. This URL should include a few key query search params:
```ts
import type { CollectionConfig } from 'payload'
export const Pages: CollectionConfig = {
slug: 'pages',
admin: {
preview: ({ slug, collection }) => {
const encodedParams = new URLSearchParams({
slug,
collection,
path: `/${slug}`,
previewSecret: process.env.PREVIEW_SECRET || '',
})
return `/preview?${encodedParams.toString()}` // highlight-line
},
},
fields: [
{
name: 'slug',
type: 'text',
},
],
}
```
#### Step 2: Create the Preview Route
Then, create an API route that verifies the preview secret, authenticates the user, and enters draft mode:
`/app/preview/route.ts`
```ts
import type { CollectionSlug, PayloadRequest } from 'payload'
import { getPayload } from 'payload'
import { draftMode } from 'next/headers'
import { redirect } from 'next/navigation'
import configPromise from '@payload-config'
export async function GET(
req: {
cookies: {
get: (name: string) => {
value: string
}
}
} & Request,
): Promise<Response> {
const payload = await getPayload({ config: configPromise })
const { searchParams } = new URL(req.url)
const path = searchParams.get('path')
const collection = searchParams.get('collection') as CollectionSlug
const slug = searchParams.get('slug')
const previewSecret = searchParams.get('previewSecret')
if (previewSecret !== process.env.PREVIEW_SECRET) {
return new Response('You are not allowed to preview this page', {
status: 403,
})
}
if (!path || !collection || !slug) {
return new Response('Insufficient search params', { status: 404 })
}
if (!path.startsWith('/')) {
return new Response(
'This endpoint can only be used for relative previews',
{ status: 500 },
)
}
let user
try {
user = await payload.auth({
req: req as unknown as PayloadRequest,
headers: req.headers,
})
} catch (error) {
payload.logger.error(
{ err: error },
'Error verifying token for live preview',
)
return new Response('You are not allowed to preview this page', {
status: 403,
})
}
const draft = await draftMode()
if (!user) {
draft.disable()
return new Response('You are not allowed to preview this page', {
status: 403,
})
}
// You can add additional checks here to see if the user is allowed to preview this page
draft.enable()
redirect(path)
}
```
#### Step 3: Query Draft Content
Finally, in your front-end application, you can detect draft mode and adjust your queries to include drafts:
`/app/[slug]/page.tsx`
```ts
export default async function Page({ params: paramsPromise }) {
const { slug = 'home' } = await paramsPromise
const { isEnabled: isDraftMode } = await draftMode()
const payload = await getPayload({ config })
const page = await payload.find({
collection: 'pages',
depth: 0,
draft: isDraftMode, // highlight-line
limit: 1,
overrideAccess: isDraftMode,
where: {
slug: {
equals: slug,
},
},
})?.then(({ docs }) => docs?.[0])
if (page === null) {
return notFound()
}
return (
<main>
<h1>{page?.title}</h1>
</main>
)
}
```
<Banner type="success">
**Note:** For fully working example of this, check of the official [Draft
Preview
Example](https://github.com/payloadcms/payload/tree/main/examples/draft-preview)
in the [Examples
Directory](https://github.com/payloadcms/payload/tree/main/examples).
</Banner>

File diff suppressed because it is too large Load Diff

View File

@@ -1,98 +0,0 @@
---
title: Custom Strategies
label: Custom Strategies
order: 60
desc: Create custom authentication strategies to handle everything auth in Payload.
keywords: authentication, config, configuration, overview, documentation, Content Management System, cms, headless, javascript, node, react, nextjs
---
<Banner type="warning">
This is an advanced feature, so only attempt this if you are an experienced
developer. Otherwise, just let Payload's built-in authentication handle user
auth for you.
</Banner>
### Creating a strategy
At the core, a strategy is a way to authenticate a user making a request. As of `3.0` we moved away from [Passport](https://www.passportjs.org) in favor of pulling back the curtain and putting you in full control.
A strategy is made up of the following:
| Parameter | Description |
| --------------------- | ------------------------------------------------------------------------- |
| **`name`** \* | The name of your strategy |
| **`authenticate`** \* | A function that takes in the parameters below and returns a user or null. |
The `authenticate` function is passed the following arguments:
| Argument | Description |
| ---------------------- | ------------------------------------------------------------------------------------------------------------------- |
| **`canSetHeaders`** \* | Whether or not the strategy is being executed from a context where response headers can be set. Default is `false`. |
| **`headers`** \* | The headers on the incoming request. Useful for retrieving identifiable information on a request. |
| **`payload`** \* | The Payload class. Useful for authenticating the identifiable information against Payload. |
| **`isGraphQL`** | Whether or not the strategy is being executed within the GraphQL endpoint. Default is `false`. |
### Example Strategy
At its core a strategy simply takes information from the incoming request and returns a user. This is exactly how Payload's built-in strategies function.
Your `authenticate` method should return an object containing a Payload user document and any optional headers that you'd like Payload to set for you when we return a response.
```ts
import type { CollectionConfig } from 'payload'
export const Users: CollectionConfig = {
slug: 'users',
auth: {
disableLocalStrategy: true,
// highlight-start
strategies: [
{
name: 'custom-strategy',
authenticate: ({ payload, headers }) => {
const usersQuery = await payload.find({
collection: 'users',
where: {
code: {
equals: headers.get('code'),
},
secret: {
equals: headers.get('secret'),
},
},
})
return {
// Send the user with the collection slug back to authenticate,
// or send null if no user should be authenticated
user: usersQuery.docs[0] ? {
collection: 'users'
...usersQuery.docs[0],
} : null,
// Optionally, you can return headers
// that you'd like Payload to set here when
// it returns the response
responseHeaders: new Headers({
'some-header': 'my header value'
})
}
}
}
]
// highlight-end
},
fields: [
{
name: 'code',
type: 'text',
index: true,
unique: true,
},
{
name: 'secret',
type: 'text',
},
]
}
```

View File

@@ -1,205 +0,0 @@
---
title: Authentication Emails
label: Email Verification
order: 30
desc: Email Verification allows users to verify their email address before they're account is fully activated. Email Verification ties directly into the Email functionality that Payload provides.
keywords: authentication, email, config, configuration, documentation, Content Management System, cms, headless, javascript, node, react, nextjs
---
[Authentication](./overview) ties directly into the [Email](../email/overview) functionality that Payload provides. This allows you to send emails to users for verification, password resets, and more. While Payload provides default email templates for these actions, you can customize them to fit your brand.
## Email Verification
Email Verification forces users to prove they have access to the email address they can authenticate. This will help to reduce spam accounts and ensure that users are who they say they are.
To enable Email Verification, use the `auth.verify` property on your [Collection Config](../configuration/collections):
```ts
import type { CollectionConfig } from 'payload'
export const Customers: CollectionConfig = {
// ...
auth: {
verify: true, // highlight-line
},
}
```
<Banner type="info">
**Tip:** Verification emails are fully customizable. [More
details](#generateemailhtml).
</Banner>
The following options are available:
| Option | Description |
| -------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
| **`generateEmailHTML`** | Allows for overriding the HTML within emails that are sent to users indicating how to validate their account. [More details](#generateemailhtml). |
| **`generateEmailSubject`** | Allows for overriding the subject of the email that is sent to users indicating how to validate their account. [More details](#generateemailsubject). |
#### generateEmailHTML
Function that accepts one argument, containing `{ req, token, user }`, that allows for overriding the HTML within emails that are sent to users indicating how to validate their account. The function should return a string that supports HTML, which can optionally be a full HTML email.
```ts
import type { CollectionConfig } from 'payload'
export const Customers: CollectionConfig = {
// ...
auth: {
verify: {
// highlight-start
generateEmailHTML: ({ req, token, user }) => {
// Use the token provided to allow your user to verify their account
const url = `https://yourfrontend.com/verify?token=${token}`
return `Hey ${user.email}, verify your email by clicking here: ${url}`
},
// highlight-end
},
},
}
```
<Banner type="warning">
**Important:** If you specify a different URL to send your users to for email
verification, such as a page on the frontend of your app or similar, you need
to handle making the call to the Payload REST or GraphQL verification
operation yourself on your frontend, using the token that was provided for
you. Above, it was passed via query parameter.
</Banner>
#### generateEmailSubject
Similarly to the above `generateEmailHTML`, you can also customize the subject of the email. The function arguments are the same but you can only return a string - not HTML.
```ts
import type { CollectionConfig } from 'payload'
export const Customers: CollectionConfig = {
// ...
auth: {
verify: {
// highlight-start
generateEmailSubject: ({ req, user }) => {
return `Hey ${user.email}, reset your password!`
},
// highlight-end
},
},
}
```
## Forgot Password
You can customize how the Forgot Password workflow operates with the following options on the `auth.forgotPassword` property:
```ts
import type { CollectionConfig } from 'payload'
export const Customers: CollectionConfig = {
// ...
auth: {
forgotPassword: {
// highlight-line
// ...
},
},
}
```
The following options are available:
| Option | Description |
| -------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
| **`expiration`** | Configure how long password reset tokens remain valid, specified in milliseconds. |
| **`generateEmailHTML`** | Allows for overriding the HTML within emails that are sent to users attempting to reset their password. [More details](#generateEmailHTML). |
| **`generateEmailSubject`** | Allows for overriding the subject of the email that is sent to users attempting to reset their password. [More details](#generateEmailSubject). |
#### generateEmailHTML
This function allows for overriding the HTML within emails that are sent to users attempting to reset their password. The function should return a string that supports HTML, which can be a full HTML email.
```ts
import type { CollectionConfig } from 'payload'
export const Customers: CollectionConfig = {
// ...
auth: {
forgotPassword: {
// highlight-start
generateEmailHTML: ({ req, token, user }) => {
// Use the token provided to allow your user to reset their password
const resetPasswordURL = `https://yourfrontend.com/reset-password?token=${token}`
return `
<!doctype html>
<html>
<body>
<h1>Here is my custom email template!</h1>
<p>Hello, ${user.email}!</p>
<p>Click below to reset your password.</p>
<p>
<a href="${resetPasswordURL}">${resetPasswordURL}</a>
</p>
</body>
</html>
`
},
// highlight-end
},
},
}
```
<Banner type="warning">
**Important:** If you specify a different URL to send your users to for
resetting their password, such as a page on the frontend of your app or
similar, you need to handle making the call to the Payload REST or GraphQL
reset-password operation yourself on your frontend, using the token that was
provided for you. Above, it was passed via query parameter.
</Banner>
<Banner type="success">
**Tip:** HTML templating can be used to create custom email templates, inline
CSS automatically, and more. You can make a reusable function that
standardizes all email sent from Payload, which makes sending custom emails
more DRY. Payload doesn't ship with an HTML templating engine, so you are free
to choose your own.
</Banner>
The following arguments are passed to the `generateEmailHTML` function:
| Argument | Description |
| -------- | ----------------------------------------------------------------- |
| `req` | The request object. |
| `token` | The token that is generated for the user to reset their password. |
| `user` | The user document that is attempting to reset their password. |
#### generateEmailSubject
Similarly to the above `generateEmailHTML`, you can also customize the subject of the email. The function arguments are the same but you can only return a string - not HTML.
```ts
import type { CollectionConfig } from 'payload'
export const Customers: CollectionConfig = {
// ...
auth: {
forgotPassword: {
// highlight-start
generateEmailSubject: ({ req, user }) => {
return `Hey ${user.email}, reset your password!`
},
// highlight-end
},
},
}
```
The following arguments are passed to the `generateEmailSubject` function:
| Argument | Description |
| -------- | ------------------------------------------------------------- |
| `req` | The request object. |
| `user` | The user document that is attempting to reset their password. |

View File

@@ -1,450 +0,0 @@
---
title: Authentication Operations
label: Operations
order: 20
desc: Enabling Authentication automatically makes key operations available such as Login, Logout, Verify, Unlock, Reset Password and more.
keywords: authentication, config, configuration, documentation, Content Management System, cms, headless, javascript, node, react, nextjs
---
Enabling [Authentication](./overview) on a [Collection](../configuration/collections) automatically exposes additional auth-based operations in the [Local API](../local-api/overview), [REST API](../rest-api/overview), and [GraphQL API](../graphql/overview).
## Access
The Access operation returns what a logged in user can and can't do with the collections and globals that are registered via your config. This data can be immensely helpful if your app needs to show and hide certain features based on [Access Control](../access-control/overview), just as the [Admin Panel](../admin/overview) does.
**REST API endpoint**:
`GET http://localhost:3000/api/access`
Example response:
```ts
{
canAccessAdmin: true,
collections: {
pages: {
create: {
permission: true,
},
read: {
permission: true,
},
update: {
permission: true,
},
delete: {
permission: true,
},
fields: {
title: {
create: {
permission: true,
},
read: {
permission: true,
},
update: {
permission: true,
},
}
}
}
}
}
```
**Example GraphQL Query**:
```graphql
query {
Access {
pages {
read {
permission
}
}
}
}
```
Document access can also be queried on a collection/global basis. Access on a global can be queried like `http://localhost:3000/api/global-slug/access`, Collection document access can be queried like `http://localhost:3000/api/collection-slug/access/:id`.
## Me
Returns either a logged in user with token or null when there is no logged in user.
**REST API endpoint**:
`GET http://localhost:3000/api/[collection-slug]/me`
Example response:
```ts
{
user: { // The JWT "payload" ;) from the logged in user
email: 'dev@payloadcms.com',
createdAt: "2020-12-27T21:16:45.645Z",
updatedAt: "2021-01-02T18:37:41.588Z",
id: "5ae8f9bde69e394e717c8832"
},
token: '34o4345324...', // The token that can be used to authenticate the user
exp: 1609619861, // Unix timestamp representing when the user's token will expire
}
```
**Example GraphQL Query**:
```graphql
query {
me[collection-singular-label] {
user {
email
}
exp
}
}
```
## Login
Accepts an `email` and `password`. On success, it will return the logged in user as well as a token that can be used to authenticate. In the GraphQL and REST APIs, this operation also automatically sets an HTTP-only cookie including the user's token. If you pass a `res` to the Local API operation, Payload will set a cookie there as well.
**Example REST API login**:
```ts
const res = await fetch('http://localhost:3000/api/[collection-slug]/login', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
email: 'dev@payloadcms.com',
password: 'this-is-not-our-password...or-is-it?',
}),
})
const json = await res.json()
// JSON will be equal to the following:
/*
{
user: {
email: 'dev@payloadcms.com',
createdAt: "2020-12-27T21:16:45.645Z",
updatedAt: "2021-01-02T18:37:41.588Z",
id: "5ae8f9bde69e394e717c8832"
},
token: '34o4345324...',
exp: 1609619861
}
*/
```
**Example GraphQL Mutation**:
```graphql
mutation {
login[collection-singular-label](email: "dev@payloadcms.com", password: "yikes") {
user {
email
}
exp
token
}
}
```
**Example Local API login**:
```ts
const result = await payload.login({
collection: 'collection-slug',
data: {
email: 'dev@payloadcms.com',
password: 'get-out',
},
})
```
<Banner type="success">
**Server Functions:** Payload offers a ready-to-use `login` server function
that utilizes the Local API. For integration details and examples, check out
the [Server Function
docs](../local-api/server-functions#reusable-payload-server-functions).
</Banner>
## Logout
As Payload sets HTTP-only cookies, logging out cannot be done by just removing a cookie in JavaScript, as HTTP-only cookies are inaccessible by JS within the browser. So, Payload exposes a `logout` operation to delete the token in a safe way.
**Example REST API logout**:
```ts
const res = await fetch(
'http://localhost:3000/api/[collection-slug]/logout?allSessions=false',
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
},
)
```
**Example GraphQL Mutation**:
```
mutation {
logoutUser(allSessions: false)
}
```
<Banner type="success">
**Server Functions:** Payload provides a ready-to-use `logout` server function
that manages the user's cookie for a seamless logout. For integration details
and examples, check out the [Server Function
docs](../local-api/server-functions#reusable-payload-server-functions).
</Banner>
#### Logging out with sessions enabled
By default, logging out will only end the session pertaining to the JWT that was used to log out with. However, you can pass `allSessions: true` to the logout operation in order to end all sessions for the user logging out.
## Refresh
Allows for "refreshing" JWTs. If your user has a token that is about to expire, but the user is still active and using the app, you might want to use the `refresh` operation to receive a new token by executing this operation via the authenticated user.
This operation requires a non-expired token to send back a new one. If the user's token has already expired, you will need to allow them to log in again to retrieve a new token.
If successful, this operation will automatically renew the user's HTTP-only cookie and will send back the updated token in JSON.
**Example REST API token refresh**:
```ts
const res = await fetch(
'http://localhost:3000/api/[collection-slug]/refresh-token',
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
},
)
const json = await res.json()
// JSON will be equal to the following:
/*
{
user: {
email: 'dev@payloadcms.com',
createdAt: "2020-12-27T21:16:45.645Z",
updatedAt: "2021-01-02T18:37:41.588Z",
id: "5ae8f9bde69e394e717c8832"
},
refreshedToken: '34o4345324...',
exp: 1609619861
}
*/
```
**Example GraphQL Mutation**:
```
mutation {
refreshToken[collection-singular-label] {
user {
email
}
refreshedToken
}
}
```
<Banner type="success">
**Server Functions:** Payload exports a ready-to-use `refresh` server function
that automatically renews the user's token and updates the associated cookie.
For integration details and examples, check out the [Server Function
docs](../local-api/server-functions#reusable-payload-server-functions).
</Banner>
## Verify by Email
If your collection supports email verification, the Verify operation will be exposed which accepts a verification token and sets the user's `_verified` property to `true`, thereby allowing the user to authenticate with the Payload API.
**Example REST API user verification**:
```ts
const res = await fetch(
`http://localhost:3000/api/[collection-slug]/verify/${TOKEN_HERE}`,
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
},
)
```
**Example GraphQL Mutation**:
```graphql
mutation {
verifyEmail[collection-singular-label](token: "TOKEN_HERE")
}
```
**Example Local API verification**:
```ts
const result = await payload.verifyEmail({
collection: 'collection-slug',
token: 'TOKEN_HERE',
})
```
**Note:** the token you need to pass to the `verifyEmail` function is unique to verification and is not the same as the token that you can retrieve from the `forgotPassword` operation. It can be found on the user document, as a hidden `_verificationToken` field. If you'd like to retrieve this token, you can use the Local API's `find` or `findByID` methods, setting `showHiddenFields: true`.
**Note:** if you do not have a `config.serverURL` set, Payload will attempt to create one for you if the user was created via REST or GraphQL by looking at the incoming `req`. But this is not supported if you are creating the user via the Local API's `payload.create()` method. If this applies to you, and you do not have a `serverURL` set, you may want to override your `verify.generateEmailHTML` function to provide a full URL to link the user to a proper verification page.
## Unlock
If a user locks themselves out and you wish to deliberately unlock them, you can utilize the Unlock operation. The [Admin Panel](../admin/overview) features an Unlock control automatically for all collections that feature max login attempts, but you can programmatically unlock users as well by using the Unlock operation.
To restrict who is allowed to unlock users, you can utilize the [`unlock`](../access-control/collections#unlock) access control function.
**Example REST API unlock**:
```ts
const res = await fetch(`http://localhost:3000/api/[collection-slug]/unlock`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
})
```
**Example GraphQL Mutation**:
```
mutation {
unlock[collection-singular-label]
}
```
**Example Local API unlock**:
```ts
const result = await payload.unlock({
collection: 'collection-slug',
})
```
## Forgot Password
Payload comes with built-in forgot password functionality. Submitting an email address to the Forgot Password operation will generate an email and send it to the respective email address with a link to reset their password.
The link to reset the user's password contains a token which is what allows the user to securely reset their password.
By default, the Forgot Password operations send users to the [Admin Panel](../admin/overview) to reset their password, but you can customize the generated email to send users to the frontend of your app instead by [overriding the email HTML](/docs/authentication/email#forgot-password).
**Example REST API Forgot Password**:
```ts
const res = await fetch(
`http://localhost:3000/api/[collection-slug]/forgot-password`,
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
email: 'dev@payloadcms.com',
}),
},
)
```
**Example GraphQL Mutation**:
```
mutation {
forgotPassword[collection-singular-label](email: "dev@payloadcms.com")
}
```
**Example Local API forgot password**:
```ts
const token = await payload.forgotPassword({
collection: 'collection-slug',
data: {
email: 'dev@payloadcms.com',
},
disableEmail: false, // you can disable the auto-generation of email via Local API
})
```
<Banner type="info">
**Note:** if you do not have a `config.serverURL` set, Payload will attempt to
create one for you if the `forgot-password` operation was triggered via REST
or GraphQL by looking at the incoming `req`. But this is not supported if you
are calling `payload.forgotPassword()` via the Local API. If you do not have a
`serverURL` set, you may want to override your
`auth.forgotPassword.generateEmailHTML` function to provide a full URL to link
the user to a proper reset-password page.
</Banner>
<Banner type="success">
**Tip:**
You can stop the reset-password email from being sent via using the Local API. This is helpful if
you need to create user accounts programmatically, but not set their password for them. This
effectively generates a reset password token which you can then use to send to a page you create,
allowing a user to "complete" their account by setting their password. In the background, you'd
use the token to "reset" their password.
</Banner>
## Reset Password
After a user has "forgotten" their password and a token is generated, that token can be used to send to the reset password operation along with a new password which will allow the user to reset their password securely.
**Example REST API Reset Password**:
```ts
const res = await fetch(`http://localhost:3000/api/[collection-slug]/reset-password`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
token: 'TOKEN_GOES_HERE'
password: 'not-today',
}),
});
const json = await res.json();
// JSON will be equal to the following:
/*
{
user: {
email: 'dev@payloadcms.com',
createdAt: "2020-12-27T21:16:45.645Z",
updatedAt: "2021-01-02T18:37:41.588Z",
id: "5ae8f9bde69e394e717c8832"
},
token: '34o4345324...',
exp: 1609619861
}
*/
```
**Example GraphQL Mutation**:
```graphql
mutation {
resetPassword[collection-singular-label](token: "TOKEN_GOES_HERE", password: "not-today")
}
```

View File

@@ -1,244 +0,0 @@
---
title: Authentication Overview
label: Overview
order: 10
desc: Payload provides highly secure user Authentication out of the box, and you can fully customize, override, or remove the default Authentication support.
keywords: authentication, config, configuration, overview, documentation, Content Management System, cms, headless, javascript, node, react, nextjs
---
<YouTube
id="CT4KafeJjTI"
title="Simplified Authentication for Headless CMS: Unlocking Reusability in One Line"
/>
Authentication is a critical part of any application. Payload provides a secure, portable way to manage user accounts out of the box. Payload Authentication is designed to be used in both the [Admin Panel](../admin/overview), as well as your own external applications, completely eliminating the need for paid, third-party platforms and services.
Here are some common use cases of Authentication in your own applications:
- Customer accounts for an e-commerce app
- User accounts for a SaaS product
- P2P apps or social sites where users need to log in and manage their profiles
- Online games where players need to track their progress over time
When Authentication is enabled on a [Collection](../configuration/collections), Payload injects all necessary functionality to support the entire user flow. This includes all [auth-related operations](./operations) like account creation, logging in and out, and resetting passwords, all [auth-related emails](./email) like email verification and password reset, as well as any necessary UI to manage users from the Admin Panel.
To enable Authentication on a Collection, use the `auth` property in the [Collection Config](../configuration/collections#config-options):
```ts
import type { CollectionConfig } from 'payload'
export const Users: CollectionConfig = {
// ...
auth: true, // highlight-line
}
```
![Authentication Admin Panel functionality](https://payloadcms.com/images/docs/auth-admin.jpg)
_Admin Panel screenshot depicting an Admins Collection with Auth enabled_
## Config Options
Any [Collection](../configuration/collections) can opt-in to supporting Authentication. Once enabled, each Document that is created within the Collection can be thought of as a "user". This enables a complete authentication workflow on your Collection, such as logging in and out, resetting their password, and more.
<Banner type="warning">
**Note:** By default, Payload provides an auth-enabled `User` Collection which
is used to access the Admin Panel. [More
details](../admin/overview#the-admin-user-collection).
</Banner>
To enable Authentication on a Collection, use the `auth` property in the [Collection Config](../configuration/collections):
```ts
import type { CollectionConfig } from 'payload'
export const Admins: CollectionConfig = {
// ...
// highlight-start
auth: {
tokenExpiration: 7200, // How many seconds to keep the user logged in
verify: true, // Require email verification before being allowed to authenticate
maxLoginAttempts: 5, // Automatically lock a user out after X amount of failed logins
lockTime: 600 * 1000, // Time period to allow the max login attempts
// More options are available
},
// highlight-end
}
```
<Banner type="info">
**Tip:** For default auth behavior, set `auth: true`. This is a good starting
point for most applications.
</Banner>
<Banner type="warning">
**Note:** Auth-enabled Collections will be automatically injected with the
`hash`, `salt`, and `email` fields. [More
details](../fields/overview#field-names).
</Banner>
The following options are available:
| Option | Description |
| ------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **`cookies`** | Set cookie options, including `secure`, `sameSite`, and `domain`. For advanced users. |
| **`depth`** | How many levels deep a `user` document should be populated when creating the JWT and binding the `user` to the `req`. Defaults to `0` and should only be modified if absolutely necessary, as this will affect performance. |
| **`disableLocalStrategy`** | Advanced - disable Payload's built-in local auth strategy. Only use this property if you have replaced Payload's auth mechanisms with your own. |
| **`forgotPassword`** | Customize the way that the `forgotPassword` operation functions. [More details](./email#forgot-password). |
| **`lockTime`** | Set the time (in milliseconds) that a user should be locked out if they fail authentication more times than `maxLoginAttempts` allows for. |
| **`loginWithUsername`** | Ability to allow users to login with username/password. [More](/docs/authentication/overview#login-with-username) |
| **`maxLoginAttempts`** | Only allow a user to attempt logging in X amount of times. Automatically locks out a user from authenticating if this limit is passed. Set to `0` to disable. |
| **`removeTokenFromResponses`** | Set to true if you want to remove the token from the returned authentication API responses such as login or refresh. |
| **`strategies`** | Advanced - an array of custom authentication strategies to extend this collection's authentication with. [More details](./custom-strategies). |
| **`tokenExpiration`** | How long (in seconds) to keep the user logged in. JWTs and HTTP-only cookies will both expire at the same time. |
| **`useAPIKey`** | Payload Authentication provides for API keys to be set on each user within an Authentication-enabled Collection. [More details](./api-keys). |
| **`useSessions`** | True by default. Set to `false` to use stateless JWTs for authentication instead of sessions. |
| **`verify`** | Set to `true` or pass an object with verification options to require users to verify by email before they are allowed to log into your app. [More details](./email#email-verification). |
### Login With Username
You can allow users to login with their username instead of their email address by setting the `loginWithUsername` property to `true`.
Example:
```ts
{
slug: 'customers',
auth: {
loginWithUsername: true,
},
}
```
Or, you can pass an object with additional options:
```ts
{
slug: 'customers',
auth: {
loginWithUsername: {
allowEmailLogin: true, // default: false
requireEmail: false, // default: false
},
},
}
```
**`allowEmailLogin`**
If set to `true`, users can log in with either their username or email address. If set to `false`, users can only log in with their username.
**`requireEmail`**
If set to `true`, an email address is required when creating a new user. If set to `false`, email is not required upon creation.
## Auto-Login
For testing and demo purposes you may want to skip forcing the user to login in order to access your application. Typically, all users should be required to login, however, you can speed up local development time by enabling auto-login.
To enable auto-login, set the `autoLogin` property in the [Payload Config](../admin/overview#admin-options):
```ts
import { buildConfig } from 'payload'
export default buildConfig({
// ...
// highlight-start
admin: {
autoLogin:
process.env.NODE_ENV === 'development'
? {
email: 'test@example.com',
password: 'test',
prefillOnly: true,
}
: false,
},
// highlight-end
})
```
<Banner type="warning">
**Warning:** The recommended way to use this feature is behind an [Environment
Variable](../configuration/environment-vars). This will ensure it is
_disabled_ in production.
</Banner>
The following options are available:
| Option | Description |
| ----------------- | --------------------------------------------------------------------------------------------------------------- |
| **`username`** | The username of the user to login as |
| **`email`** | The email address of the user to login as |
| **`password`** | The password of the user to login as. This is only needed if `prefillOnly` is set to true |
| **`prefillOnly`** | If set to true, the login credentials will be prefilled but the user will still need to click the login button. |
## Operations
All auth-related operations are available via Payload's REST, Local, and GraphQL APIs. These operations are automatically added to your Collection when you enable Authentication. [More details](./operations).
## Strategies
Out of the box Payload ships with three powerful Authentication strategies:
- [HTTP-Only Cookies](./cookies)
- [JSON Web Tokens (JWT)](./jwt)
- [API-Keys](./api-keys)
Each of these strategies can work together or independently. You can also create your own custom strategies to fit your specific needs. [More details](./custom-strategies).
### HTTP-Only Cookies
[HTTP-only cookies](https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies) are a highly secure method of storing identifiable data on a user's device so that Payload can automatically recognize a returning user until their cookie expires. They are totally protected from common XSS attacks and **cannot be read by JavaScript in the browser**, unlike JWT's. [More details](./cookies).
### JSON Web Tokens
JWT (JSON Web Tokens) can also be utilized to perform authentication. Tokens are generated on `login`, `refresh` and `me` operations and can be attached to future requests to authenticate users. [More details](./jwt).
### API Keys
API Keys can be enabled on auth collections. These are particularly useful when you want to authenticate against Payload from a third party service. [More details](./api-keys).
### Custom Strategies
There are cases where these may not be enough for your application. Payload is extendable by design so you can wire up your own strategy when you need to. [More details](./custom-strategies).
### Access Control
Default auth fields including `email`, `username`, and `password` can be overridden by defining a custom field with the same name in your collection config. This allows you to customize the field — including access control — while preserving the underlying auth functionality. For example, you might want to restrict the `email` field from being updated once it is created, or only allow it to be read by certain user roles. You can achieve this by redefining the field and setting access rules accordingly.
Here's an example of how to restrict access to default auth fields:
```ts
import type { CollectionConfig } from 'payload'
export const Auth: CollectionConfig = {
slug: 'users',
auth: true,
fields: [
{
name: 'email', // or 'username'
type: 'text',
access: {
create: () => true,
read: () => false,
update: () => false,
},
},
{
name: 'password', // this will be applied to all password-related fields including new password, confirm password.
type: 'text',
hidden: true, // needed only for the password field to prevent duplication in the Admin panel
access: {
update: () => false,
},
},
],
}
```
**Note:**
- Access functions will apply across the application — I.e. if `read` access is disabled on `email`, it will not appear in the Admin panel UI or API.
- Restricting `read` on the `email` or `username` disables the **Unlock** action in the Admin panel as this function requires access to a user-identifying field.
- When overriding the `password` field, you may need to include `hidden: true` to prevent duplicate fields being displayed in the Admin panel.

View File

@@ -1,62 +0,0 @@
---
title: Project Configuration
label: Configuration
order: 20
desc: Quickly configure and deploy your Payload Cloud project in a few simple steps.
keywords: configuration, config, settings, project, cloud, payload cloud, deploy, deployment
---
## Select your plan
Once you have created a project, you will need to select your plan. This will determine the resources that are allocated to your project and the features that are available to you.
<Banner type="success">
Note: All Payload Cloud teams that deploy a project require a card on file.
This helps us prevent fraud and abuse on our platform. If you select a plan
with a free trial, you will not be charged until your trial period is over.
Well remind you 7 days before your trial ends and you can cancel anytime.
</Banner>
## Project Details
| Option | Description |
| ---------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Region** | Select the region closest to your audience. This will ensure the fastest communication between your data and your client. |
| **Project Name** | A name for your project. You can change this at any time. |
| **Project Slug** | Choose a unique slug to identify your project. This needs to be unique for your team and you can change it any time. |
| **Team** | Select the team you want to create the project under. If this is your first project, a personal team will be created for you automatically. You can modify your team settings and invite new members at any time from the Team Settings page. |
## Build Settings
If you are deploying a new project from a template, the following settings will be automatically configured for you. If you are using your own repository, you need to make sure your build settings are accurate for your project to deploy correctly.
| Option | Description |
| -------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Root Directory** | The folder where your `package.json` file lives. |
| **Install Command** | The command used to install your modules, for example: `yarn install` or `npm install` |
| **Build Command** | The command used to build your application, for example: `yarn build` or `npm run build` |
| **Serve Command** | The command used to serve your application, for example: `yarn serve` or `npm run serve` |
| **Branch to Deploy** | Select the branch of your repository that you want to deploy from. This is the branch that will be used to build your project when you commit new changes. |
| **Default Domain** | Set a default domain for your project. This must be unique and you will not able to change it. You can always add a custom domain later in your project settings. |
## Environment Variables
Any of the features in Payload Cloud that require environment variables will automatically be provided to your application. If your app requires any custom environment variables, you can set them here.
<Banner type="warning">
Note: For security reasons, any variables you wish to provide to the [Admin
Panel](../admin/overview) must be prefixed with `NEXT_PUBLIC_`.  Learn more
[here](../configuration/environment-vars).
</Banner>
## Payment
Payment methods can be set per project and can be updated any time. You can use teams default payment method, or add a new one. Modify your payment methods in your Project settings / Team settings.
<Banner type="success">
**Note:** All Payload Cloud teams that deploy a project require a card on
file. This helps us prevent fraud and abuse on our platform. If you select a
plan with a free trial, you will not be charged until your trial period is
over. Well remind you 7 days before your trial ends and you can cancel
anytime.
</Banner>

View File

@@ -1,53 +0,0 @@
---
title: Getting Started
label: Getting Started
order: 10
desc: Get started with Payload Cloud, a deployment solution specifically designed for Node + MongoDB applications.
keywords: cloud, hosted, database, storage, email, deployment, serverless, node, mongodb, s3, aws, cloudflare, atlas, resend, payload, cms
---
A deployment solution specifically designed for Node.js + MongoDB applications, offering seamless deployment of your entire stack in one place. You can get started in minutes with a one-click template or bring your own codebase with you.
Payload Cloud offers various plans tailored to meet your specific needs, including a MongoDB Atlas database, S3 file storage, and email delivery powered by [Resend](https://resend.com). To see a full breakdown of features and plans, see our [Cloud Pricing page](https://payloadcms.com/cloud-pricing).
To get started, you first need to create an account. Head over to [the login screen](https://payloadcms.com/login) and **Register for Free**.
<Banner type="success">
To create your first project, you can either select [a
template](#starting-from-a-template) or [import an existing
project](#importing-from-an-existing-codebase) from GitHub.
</Banner>
## Starting from a Template
Templates come preconfigured and provide a one-click solution to quickly deploy a new application.
![Screen for creating a new project from a template](https://payloadcms.com/images/docs/cloud/create-from-template.jpg)
_Creating a new project from a template._
After creating an account, select your desired template from the Projects page. At this point, you need to connect to authorize the Payload Cloud application with your GitHub account. Click Continue with GitHub and follow the prompts to authorize the app.
Next, select your `GitHub Scope`. If you belong to multiple organizations, they will show up here. If you do not see the organization you are looking for, you may need to adjust your GitHub app permissions.
After selecting your scope, create a unique `repository name` and select whether you want your repository to be public or private on GitHub.
<Banner type="warning">
**Note:** Public repositories can be accessed by anyone online, while private
repositories grant access only to you and anyone you explicitly authorize.
</Banner>
Once you are ready, click **Create Project**. This will clone the selected template to a new repository in your GitHub account, and take you to the configuration page to set up your project for deployment.
## Importing from an Existing Codebase
Payload Cloud works for any Node.js + MongoDB app. From the New Project page, select **import an existing Git codebase**. Choose the organization and select the repository you want to import. From here, you will be taken to the configuration page to set up your project for deployment.
![Screen for creating a new project from an existing repository](https://payloadcms.com/images/docs/cloud/create-from-existing.jpg)
_Creating a new project from an existing repository._
<Banner type="warning">
**Note:** In order to make use of the features of Payload Cloud in your own
codebase, you will need to add the [Cloud
Plugin](https://github.com/payloadcms/payload/tree/main/packages/payload-cloud)
to your Payload app.
</Banner>

View File

@@ -1,137 +0,0 @@
---
title: Cloud Projects
label: Projects
order: 40
desc: Manage your Payload Cloud projects.
keywords: cloud, payload cloud, projects, project, overview, database, file storage, build settings, environment variables, custom domains, email, developing locally
---
## Overview
<Banner>
The overview tab shows your most recent deployment, along with build and
deployment logs. From here, you can see your live URL, deployment details like
timestamps and commit hash, as well as the status of your deployment. You can
also trigger a redeployment manually, which will rebuild your project using
the current configuration.
</Banner>
![Payload Cloud Overview Page](https://payloadcms.com/images/docs/cloud/overview-page.jpg)
_A screenshot of the Overview page for a Cloud project._
## Database
Your Payload Cloud project comes with a MongoDB serverless Atlas DB instance or a Dedicated Atlas cluster, depending on your plan. To interact with your cloud database, you will be provided with a MongoDB connection string. This can be found under the **Database** tab of your project.
`mongodb+srv://your_connection_string`
## File Storage
Payload Cloud gives you S3 file storage backed by Cloudflare as a CDN, and this plugin extends Payload so that all of your media will be stored in S3 rather than locally.
AWS Cognito is used for authentication to your S3 bucket. The [Payload Cloud Plugin](https://github.com/payloadcms/payload/tree/main/packages/payload-cloud) will automatically pick up these values. These values are only if you'd like to access your files directly, outside of Payload Cloud.
### Accessing Files Outside of Payload Cloud
If you'd like to access your files outside of Payload Cloud, you'll need to retrieve some values from your project's settings and put them into your environment variables. In Payload Cloud, navigate to the File Storage tab and copy the values using the copy button. Put these values in your .env file. Also copy the Cognito Password value separately and put into your .env file as well.
When you are done, you should have the following values in your .env file:
```env
PAYLOAD_CLOUD=true
PAYLOAD_CLOUD_ENVIRONMENT=prod
PAYLOAD_CLOUD_COGNITO_USER_POOL_CLIENT_ID=
PAYLOAD_CLOUD_COGNITO_USER_POOL_ID=
PAYLOAD_CLOUD_COGNITO_IDENTITY_POOL_ID=
PAYLOAD_CLOUD_PROJECT_ID=
PAYLOAD_CLOUD_BUCKET=
PAYLOAD_CLOUD_BUCKET_REGION=
PAYLOAD_CLOUD_COGNITO_PASSWORD=
```
The plugin will pick up these values and use them to access your files.
## Build Settings
You can update settings from your Projects Settings tab. Changes to your build settings will trigger a redeployment of your project.
## Environment Variables
From the Environment Variables page of the Settings tab, you can add, update and delete variables for use in your project. Like build settings, these changes will trigger a redeployment of your project.
<Banner>
Note: For security reasons, any variables you wish to provide to the [Admin
Panel](../admin/overview) must be prefixed with `NEXT_PUBLIC_`. [More
details](../configuration/environment-vars).
</Banner>
## Custom Domains
With Payload Cloud, you can add custom domain names to your project. To do so, first go to the Domains page of the Settings tab of your project. Here you can see your default domain. To add a new domain, type in the domain name you wish to use.
<Banner>
Note: do not include the protocol (http:// or https://) or any paths (/page).
Only include the domain name and extension, and optionally a subdomain. -
your-domain.com - backend.your-domain.com
</Banner>
Once you click save, a DNS record will be generated for your domain name to point to your live project. Add this record into your DNS providers records, and once the records are resolving properly (this can take 1hr to 48hrs in some cases), your domain will now to point to your live project.
You will also need to configure your Payload project to use your specified domain. In your `payload.config.ts` file, specify your `serverURL` with your domain:
```ts
export default buildConfig({
serverURL: 'https://example.com',
// the rest of your config,
})
```
## Email
Powered by [Resend](https://resend.com), Payload Cloud comes with integrated email support out of the box. No configuration is needed, and you can use `payload.sendEmail()` to send email right from your Payload app. To learn more about sending email with Payload, checkout the [Email Configuration](../email/overview) overview.
If you are on the Pro or Enterprise plan, you can add your own custom Email domain name. From the Email page of your projects Settings, add the domain you wish to use for email delivery. This will generate a set of DNS records. Add these records to your DNS provider and click verify to check that your records are resolving properly. Once verified, your emails will now be sent from your custom domain name.
## Developing Locally
To make changes to your project, you will need to clone the repository defined in your project settings to your local machine. In order to run your project locally, you will need configure your local environment first. Refer to your repositorys `README.md` file to see the steps needed for your specific template.
From there, you are ready to make updates to your project. When you are ready to make your changes live, commit your changes to the branch you specified in your Project settings, and your application will automatically trigger a redeploy and build from your latest commit.
## Cloud Plugin
Projects generated from a template will come pre-configured with the official Cloud Plugin, but if you are using your own repository you will need to add this into your project. To do so, add the plugin to your Payload Config:
`pnpm add @payloadcms/payload-cloud`
```js
import { payloadCloudPlugin } from '@payloadcms/payload-cloud'
import { buildConfig } from 'payload'
export default buildConfig({
plugins: [payloadCloudPlugin()],
// rest of config
})
```
<Banner type="warning">
**Note:** If your Payload Config already has an email with transport, this
will take precedence over Payload Cloud's email service.
</Banner>
<Banner type="info">
Good to know: the Payload Cloud Plugin was previously named
`@payloadcms/plugin-cloud`. If you are using this plugin, you should update to
the new package name.
</Banner>
#### **Optional configuration**
If you wish to opt-out of any Payload cloud features, the plugin also accepts options to do so.
```js
payloadCloud({
storage: false, // Disable file storage
email: false, // Disable email delivery
})
```

View File

@@ -1,35 +0,0 @@
---
title: Cloud Teams
label: Teams
order: 30
desc: Manage your Payload Cloud team and billing settings.
keywords: team, teams, billing, subscription, payment, plan, plans, cloud, payload cloud
---
<Banner>
Within Payload Cloud, the team management feature offers you the ability to
manage your organization, team members, billing, and subscription settings.
</Banner>
![Payload Cloud Team Settings](https://payloadcms.com/images/docs/cloud/team-settings.jpg)
_A screenshot of the Team Settings page._
## Members
Each team has members that can interact with your projects. You can invite multiple people to your team and each individual can belong to more than one team. You can assign them either `owner` or `user` permissions. Owners are able to make admin-only changes, such as deleting projects, and editing billing information.
## Adding Members
To add a new member to your team, visit your Teams Settings page, and click “Invite Teammate”. You can then add their email address, and assign their role. Press “Save” to send the invitations, which will send an email to the invited team member where they can create a new account.
## Billing
Users can update billing settings and subscriptions for any teams where they are designated as an `owner`. To make updates to the teams payment methods, visit the Billing page under the Team Settings tab. You can add new cards, delete cards, and set a payment method as a default. The default payment method will be used in the event that another payment method fails.
## Subscriptions
From the Subscriptions page, a team owner can see all current plans for their team. From here, you can see the price of each plan, if there is an active trial, and when you will be billed next.
## Invoices
The Invoices page will you show you the invoices for your account, as well as the status on their payment.

View File

@@ -1,294 +0,0 @@
---
title: Collection Configs
label: Collections
order: 20
desc: Structure your Collections for your needs by defining fields, adding slugs and labels, establishing access control, tying in hooks, setting timestamps and more.
keywords: collections, config, configuration, documentation, Content Management System, cms, headless, javascript, node, react, nextjs
---
A Collection is a group of records, called Documents, that all share a common schema. You can define as many Collections as your application needs. Each Document in a Collection is stored in the [Database](../database/overview) based on the [Fields](../fields/overview) that you define, and automatically generates a [Local API](../local-api/overview), [REST API](../rest-api/overview), and [GraphQL API](../graphql/overview) used to manage your Documents.
Collections are also used to achieve [Authentication](../authentication/overview) in Payload. By defining a Collection with `auth` options, that Collection receives additional operations to support user authentication.
Collections are the primary way to structure recurring data in your application, such as users, products, pages, posts, and other types of content that you might want to manage. Each Collection can have its own unique [Access Control](../access-control/overview), [Hooks](../hooks/overview), [Admin Options](#admin-options), and more.
To define a Collection Config, use the `collection` property in your [Payload Config](./overview):
```ts
import { buildConfig } from 'payload'
export default buildConfig({
// ...
collections: [
// highlight-line
// Your Collections go here
],
})
```
<Banner type="success">
**Tip:** If your Collection is only ever meant to contain a single Document,
consider using a [Global](./globals) instead.
</Banner>
## Config Options
It's often best practice to write your Collections in separate files and then import them into the main [Payload Config](./overview).
Here is what a simple Collection Config might look like:
```ts
import type { CollectionConfig } from 'payload'
export const Posts: CollectionConfig = {
slug: 'posts',
fields: [
{
name: 'title',
type: 'text',
},
],
}
```
<Banner type="success">
**Reminder:** For more complex examples, see the
[Templates](https://github.com/payloadcms/payload/tree/main/templates) and
[Examples](https://github.com/payloadcms/payload/tree/main/examples)
directories in the Payload repository.
</Banner>
The following options are available:
| Option | Description |
| -------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `admin` | The configuration options for the Admin Panel. [More details](#admin-options). |
| `access` | Provide Access Control functions to define exactly who should be able to do what with Documents in this Collection. [More details](../access-control/collections). |
| `auth` | Specify options if you would like this Collection to feature authentication. [More details](../authentication/overview). |
| `custom` | Extension point for adding custom data (e.g. for plugins) |
| `disableDuplicate` | When true, do not show the "Duplicate" button while editing documents within this Collection and prevent `duplicate` from all APIs. |
| `defaultSort` | Pass a top-level field to sort by default in the Collection List View. Prefix the name of the field with a minus symbol ("-") to sort in descending order. Multiple fields can be specified by using a string array. |
| `dbName` | Custom table or Collection name depending on the Database Adapter. Auto-generated from slug if not defined. |
| `endpoints` | Add custom routes to the REST API. Set to `false` to disable routes. [More details](../rest-api/overview#custom-endpoints). |
| `fields` \* | Array of field types that will determine the structure and functionality of the data stored within this Collection. [More details](../fields/overview). |
| `graphQL` | Manage GraphQL-related properties for this collection. [More](#graphql) |
| `hooks` | Entry point for Hooks. [More details](../hooks/overview#collection-hooks). |
| `orderable` | If true, enables custom ordering for the collection, and documents can be reordered via drag and drop. Uses [fractional indexing](https://observablehq.com/@dgreensp/implementing-fractional-indexing) for efficient reordering. |
| `labels` | Singular and plural labels for use in identifying this Collection throughout Payload. Auto-generated from slug if not defined. |
| `enableQueryPresets` | Enable query presets for this Collection. [More details](../query-presets/overview). |
| `lockDocuments` | Enables or disables document locking. By default, document locking is enabled. Set to an object to configure, or set to `false` to disable locking. [More details](../admin/locked-documents). |
| `slug` \* | Unique, URL-friendly string that will act as an identifier for this Collection. |
| `timestamps` | Set to false to disable documents' automatically generated `createdAt` and `updatedAt` timestamps. |
| `typescript` | An object with property `interface` as the text used in schema generation. Auto-generated from slug if not defined. |
| `upload` | Specify options if you would like this Collection to support file uploads. For more, consult the [Uploads](../upload/overview) documentation. |
| `versions` | Set to true to enable default options, or configure with object properties. [More details](../versions/overview#collection-config). |
| `defaultPopulate` | Specify which fields to select when this Collection is populated from another document. [More Details](../queries/select#defaultpopulate-collection-config-property). |
| `indexes` | Define compound indexes for this collection. This can be used to either speed up querying/sorting by 2 or more fields at the same time or to ensure uniqueness between several fields. [More details](../database/indexes#compound-indexes). |
| `forceSelect` | Specify which fields should be selected always, regardless of the `select` query which can be useful that the field exists for access control / hooks |
| `disableBulkEdit` | Disable the bulk edit operation for the collection in the admin panel and the REST API |
_\* An asterisk denotes that a property is required._
### Fields
Fields define the schema of the Documents within a Collection. To learn more, go to the [Fields](../fields/overview) documentation.
### Access Control
[Collection Access Control](../access-control/overview) determines what a user can and cannot do with any given Document within a Collection. To learn more, go to the [Access Control](../access-control/overview) documentation.
### Hooks
[Collection Hooks](../hooks/collections) allow you to tie into the lifecycle of your Documents so you can execute your own logic during specific events. To learn more, go to the [Hooks](../hooks/overview) documentation.
## Admin Options
The behavior of Collections within the [Admin Panel](../admin/overview) can be fully customized to fit the needs of your application. This includes grouping or hiding their navigation links, adding [Custom Components](../custom-components/overview), selecting which fields to display in the List View, and more.
To configure Admin Options for Collections, use the `admin` property in your Collection Config:
```ts
import type { CollectionConfig } from 'payload'
export const MyCollection: CollectionConfig = {
// ...
admin: {
// highlight-line
// ...
},
}
```
The following options are available:
| Option | Description |
| ---------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `group` | Text or localization object used to group Collection and Global links in the admin navigation. Set to `false` to hide the link from the navigation while keeping its routes accessible. |
| `hidden` | Set to true or a function, called with the current user, returning true to exclude this Collection from navigation and admin routing. |
| `hooks` | Admin-specific hooks for this Collection. [More details](../hooks/collections). |
| `useAsTitle` | Specify a top-level field to use for a document title throughout the Admin Panel. If no field is defined, the ID of the document is used as the title. |
| `description` | Text to display below the Collection label in the List View to give editors more information. Alternatively, you can use the `admin.components.Description` to render a React component. [More details](#custom-components). |
| `defaultColumns` | Array of field names that correspond to which columns to show by default in this Collection's List View. |
| `disableCopyToLocale` | Disables the "Copy to Locale" button while editing documents within this Collection. Only applicable when localization is enabled. |
| `hideAPIURL` | Hides the "API URL" meta field while editing documents within this Collection. |
| `enableRichTextLink` | The [Rich Text](../fields/rich-text) field features a `Link` element which allows for users to automatically reference related documents within their rich text. Set to `true` by default. |
| `enableRichTextRelationship` | The [Rich Text](../fields/rich-text) field features a `Relationship` element which allows for users to automatically reference related documents within their rich text. Set to `true` by default. |
| `folders` | A boolean to enable folders for a given collection. Defaults to `false`. [More details](../folders/overview). |
| `meta` | Page metadata overrides to apply to this Collection within the Admin Panel. [More details](../admin/metadata). |
| `preview` | Function to generate preview URLs within the Admin Panel that can point to your app. [More details](../admin/preview). |
| `livePreview` | Enable real-time editing for instant visual feedback of your front-end application. [More details](../live-preview/overview). |
| `components` | Swap in your own React components to be used within this Collection. [More details](#custom-components). |
| `listSearchableFields` | Specify which fields should be searched in the List search view. [More details](#list-searchable-fields). |
| `pagination` | Set pagination-specific options for this Collection. [More details](#pagination). |
| `baseListFilter` | You can define a default base filter for this collection's List view, which will be merged into any filters that the user performs. |
<Banner type="warning">
**Note:** If you set `useAsTitle` to a relationship or join field, it will use
only the ID of the related document(s) as the title. To display a specific
field (i.e. title) from the related document instead, create a virtual field
that extracts the desired data, and set `useAsTitle` to that virtual field.
</Banner>
### Custom Components
Collections can set their own [Custom Components](../custom-components/overview) which only apply to Collection-specific UI within the [Admin Panel](../admin/overview). This includes elements such as the Save Button, or entire layouts such as the Edit View.
To override Collection Components, use the `admin.components` property in your Collection Config:
```ts
import type { CollectionConfig } from 'payload'
export const MyCollection: CollectionConfig = {
// ...
admin: {
components: {
// highlight-line
// ...
},
},
}
```
The following options are available:
| Option | Description |
| ----------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `afterList` | An array of components to inject _after_ the built-in List View. [More details](../custom-components/list-view#afterlist). |
| `afterListTable` | An array of components to inject _after_ the built-in List View's table. [More details](../custom-components/list-view#afterlisttable). |
| `beforeList` | An array of components to inject _before_ the built-in List View. [More details](../custom-components/list-view#beforelist). |
| `beforeListTable` | An array of components to inject _before_ the built-in List View's table. [More details](../custom-components/list-view#beforelisttable). |
| `listMenuItems` | An array of components to render within a menu next to the List Controls (after the Columns and Filters options) |
| `Description` | A component to render below the Collection label in the List View. An alternative to the `admin.description` property. [More details](../custom-components/list-view#description). |
| `edit` | Override specific components within the Edit View. [More details](#edit-view-options). |
| `views` | Override or create new views within the Admin Panel. [More details](../custom-components/custom-views). |
#### Edit View Options
```ts
import type { CollectionConfig } from 'payload'
export const MyCollection: CollectionConfig = {
// ...
admin: {
components: {
edit: {
// highlight-line
// ...
},
},
},
}
```
The following options are available:
| Option | Description |
| ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `beforeDocumentControls` | Inject custom components before the Save / Publish buttons. [More details](../custom-components/edit-view#beforedocumentcontrols). |
| `editMenuItems` | Inject custom components within the 3-dot menu dropdown located in the document controls bar. [More details](../custom-components/edit-view#editmenuitems). |
| `SaveButton` | Replace the default Save Button within the Edit View. [Drafts](../versions/drafts) must be disabled. [More details](../custom-components/edit-view#savebutton). |
| `SaveDraftButton` | Replace the default Save Draft Button within the Edit View. [Drafts](../versions/drafts) must be enabled and autosave must be disabled. [More details](../custom-components/edit-view#savedraftbutton). |
| `PublishButton` | Replace the default Publish Button within the Edit View. [Drafts](../versions/drafts) must be enabled. [More details](../custom-components/edit-view#publishbutton). |
| `PreviewButton` | Replace the default Preview Button within the Edit View. [Preview](../admin/preview) must be enabled. [More details](../custom-components/edit-view#previewbutton). |
| `Upload` | Replace the default Upload component within the Edit View. [Upload](../upload/overview) must be enabled. [More details](../custom-components/edit-view#upload). |
<Banner type="success">
**Note:** For details on how to build Custom Components, see [Building Custom
Components](../custom-components/overview#building-custom-components).
</Banner>
### Pagination
All Collections receive their own List View which displays a paginated list of documents that can be sorted and filtered. The pagination behavior of the List View can be customized on a per-Collection basis, and uses the same [Pagination](../queries/pagination) API that Payload provides.
To configure pagination options, use the `admin.pagination` property in your Collection Config:
```ts
import type { CollectionConfig } from 'payload'
export const Posts: CollectionConfig = {
// ...
admin: {
// highlight-start
pagination: {
defaultLimit: 10,
limits: [10, 20, 50],
},
// highlight-end
},
}
```
The following options are available:
| Option | Description |
| -------------- | --------------------------------------------------------------------------------------------------- |
| `defaultLimit` | Integer that specifies the default per-page limit that should be used. Defaults to 10. |
| `limits` | Provide an array of integers to use as per-page options for admins to choose from in the List View. |
### List Searchable Fields
In the List View, there is a "search" box that allows you to quickly find a document through a simple text search. By default, it searches on the ID field. If defined, the `admin.useAsTitle` field is used. Or, you can explicitly define which fields to search based on the needs of your application.
To define which fields should be searched, use the `admin.listSearchableFields` property in your Collection Config:
```ts
import type { CollectionConfig } from 'payload'
export const Posts: CollectionConfig = {
// ...
admin: {
// highlight-start
listSearchableFields: ['title', 'slug'],
// highlight-end
},
}
```
<Banner type="warning">
**Tip:** If you are adding `listSearchableFields`, make sure you index each of
these fields so your admin queries can remain performant.
</Banner>
## GraphQL
You can completely disable GraphQL for this collection by passing `graphQL: false` to your collection config. This will completely disable all queries, mutations, and types from appearing in your GraphQL schema.
You can also pass an object to the collection's `graphQL` property, which allows you to define the following properties:
| Option | Description |
| ------------------ | ----------------------------------------------------------------------------------- |
| `singularName` | Override the "singular" name that will be used in GraphQL schema generation. |
| `pluralName` | Override the "plural" name that will be used in GraphQL schema generation. |
| `disableQueries` | Disable all GraphQL queries that correspond to this collection by passing `true`. |
| `disableMutations` | Disable all GraphQL mutations that correspond to this collection by passing `true`. |
## TypeScript
You can import types from Payload to help make writing your Collection configs easier and type-safe. There are two main types that represent the Collection Config, `CollectionConfig` and `SanitizedCollectionConfig`.
The `CollectionConfig` type represents a raw Collection Config in its full form, where only the bare minimum properties are marked as required. The `SanitizedCollectionConfig` type represents a Collection Config after it has been fully sanitized. Generally, this is only used internally by Payload.
```ts
import type { CollectionConfig, SanitizedCollectionConfig } from 'payload'
```

View File

@@ -1,99 +0,0 @@
---
title: Environment Variables
label: Environment Variables
order: 60
desc: Learn how to use Environment Variables in your Payload project
---
Environment Variables are a way to store sensitive information that your application needs to function. This could be anything from API keys to [Database](../database/overview) credentials. Payload allows you to easily use Environment Variables within your config and throughout your application.
## Next.js Applications
If you are using Next.js, no additional setup is required other than creating your `.env` file.
To use Environment Variables, add a `.env` file to the root of your project:
```plaintext
project-name/
├─ .env
├─ package.json
├─ payload.config.ts
```
Here is an example of what an `.env` file might look like:
```plaintext
SERVER_URL=localhost:3000
DATABASE_URI=mongodb://localhost:27017/my-database
```
To use Environment Variables in your Payload Config, you can access them directly from `process.env`:
```ts
import { buildConfig } from 'payload'
export default buildConfig({
serverURL: process.env.SERVER_URL, // highlight-line
// ...
})
```
## Client-side Environments
For security and safety reasons, the [Admin Panel](../admin/overview) does **not** include Environment Variables in its _client-side_ bundle by default. But, Next.js provides a mechanism to expose Environment Variables to the client-side bundle when needed.
If you are building a [Custom Component](../custom-components/overview) and need to access Environment Variables from the client-side, you can do so by prefixing them with `NEXT_PUBLIC_`.
<Banner type="warning">
**Important:** Be careful about what variables you provide to your client-side
code. Analyze every single one to make sure that you're not accidentally
leaking sensitive information. Only ever include keys that are safe for the
public to read in plain text.
</Banner>
For example, if you've got the following Environment Variable:
```bash
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_test_XXXXXXXXXXXXXXXXXX
```
This key will automatically be made available to the client-side Payload bundle and can be referenced in your Custom Component as follows:
```tsx
'use client'
import React from 'react'
const stripeKey = process.env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY // highlight-line
const MyClientComponent = () => {
// do something with the key
return <div>My Client Component</div>
}
```
For more information, check out the [Next.js documentation](https://nextjs.org/docs/app/building-your-application/configuring/environment-variables).
## Outside of Next.js
If you are using Payload outside of Next.js, we suggest using the [`dotenv`](https://www.npmjs.com/package/dotenv) package to handle Environment Variables from `.env` files. This will automatically load your Environment Variables into `process.env`.
To do this, import the package as high up in your application as possible:
```ts
import dotenv from 'dotenv'
dotenv.config() // highlight-line
import { buildConfig } from 'payload'
export default buildConfig({
serverURL: process.env.SERVER_URL,
// ...
})
```
<Banner type="warning">
**Tip:** Be sure that `dotenv` can find your `.env` file. By default, it will
look for a file named `.env` in the root of your project. If you need to
specify a different file, pass the path into the config options.
</Banner>

View File

@@ -1,214 +0,0 @@
---
title: Global Configs
label: Globals
order: 30
desc: Set up your Global config for your needs by defining fields, adding slugs and labels, establishing access control, tying in hooks and more.
keywords: globals, config, configuration, documentation, Content Management System, cms, headless, javascript, node, react, nextjs
---
Globals are in many ways similar to [Collections](./collections), except that they correspond to only a single Document. You can define as many Globals as your application needs. Each Global Document is stored in the [Database](../database/overview) based on the [Fields](../fields/overview) that you define, and automatically generates a [Local API](../local-api/overview), [REST API](../rest-api/overview), and [GraphQL API](../graphql/overview) used to manage your Documents.
Globals are the primary way to structure singletons in Payload, such as a header navigation, site-wide banner alerts, or app-wide localized strings. Each Global can have its own unique [Access Control](../access-control/overview), [Hooks](../hooks/overview), [Admin Options](#admin-options), and more.
To define a Global Config, use the `globals` property in your [Payload Config](./overview):
```ts
import { buildConfig } from 'payload'
export default buildConfig({
// ...
globals: [
// highlight-line
// Your Globals go here
],
})
```
<Banner type="success">
**Tip:** If you have more than one Global that share the same structure,
consider using a [Collection](./collections) instead.
</Banner>
## Config Options
It's often best practice to write your Globals in separate files and then import them into the main [Payload Config](./overview).
Here is what a simple Global Config might look like:
```ts
import { GlobalConfig } from 'payload'
export const Nav: GlobalConfig = {
slug: 'nav',
fields: [
{
name: 'items',
type: 'array',
required: true,
maxRows: 8,
fields: [
{
name: 'page',
type: 'relationship',
relationTo: 'pages', // "pages" is the slug of an existing collection
required: true,
},
],
},
],
}
```
<Banner type="success">
**Reminder:** For more complex examples, see the
[Templates](https://github.com/payloadcms/payload/tree/main/templates) and
[Examples](https://github.com/payloadcms/payload/tree/main/examples)
directories in the Payload repository.
</Banner>
The following options are available:
| Option | Description |
| --------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `access` | Provide Access Control functions to define exactly who should be able to do what with this Global. [More details](../access-control/globals). |
| `admin` | The configuration options for the Admin Panel. [More details](#admin-options). |
| `custom` | Extension point for adding custom data (e.g. for plugins) |
| `dbName` | Custom table or collection name for this Global depending on the Database Adapter. Auto-generated from slug if not defined. |
| `description` | Text or React component to display below the Global header to give editors more information. |
| `endpoints` | Add custom routes to the REST API. [More details](../rest-api/overview#custom-endpoints). |
| `fields` \* | Array of field types that will determine the structure and functionality of the data stored within this Global. [More details](../fields/overview). |
| `graphQL` | Manage GraphQL-related properties related to this global. [More details](#graphql) |
| `hooks` | Entry point for Hooks. [More details](../hooks/overview#global-hooks). |
| `label` | Text for the name in the Admin Panel or an object with keys for each language. Auto-generated from slug if not defined. |
| `lockDocuments` | Enables or disables document locking. By default, document locking is enabled. Set to an object to configure, or set to `false` to disable locking. [More details](../admin/locked-documents). |
| `slug` \* | Unique, URL-friendly string that will act as an identifier for this Global. |
| `typescript` | An object with property `interface` as the text used in schema generation. Auto-generated from slug if not defined. |
| `versions` | Set to true to enable default options, or configure with object properties. [More details](../versions/overview#global-config). |
| `forceSelect` | Specify which fields should be selected always, regardless of the `select` query which can be useful that the field exists for access control / hooks |
_\* An asterisk denotes that a property is required._
### Fields
Fields define the schema of the Global. To learn more, go to the [Fields](../fields/overview) documentation.
### Access Control
[Global Access Control](../access-control/globals) determines what a user can and cannot do with any given Global Document. To learn more, go to the [Access Control](../access-control/overview) documentation.
### Hooks
[Global Hooks](../hooks/globals) allow you to tie into the lifecycle of your Documents so you can execute your own logic during specific events. To learn more, go to the [Hooks](../hooks/overview) documentation.
## Admin Options
The behavior of Globals within the [Admin Panel](../admin/overview) can be fully customized to fit the needs of your application. This includes grouping or hiding their navigation links, adding [Custom Components](../custom-components/overview), setting page metadata, and more.
To configure Admin Options for Globals, use the `admin` property in your Global Config:
```ts
import { GlobalConfig } from 'payload'
export const MyGlobal: GlobalConfig = {
// ...
admin: {
// highlight-line
// ...
},
}
```
The following options are available:
| Option | Description |
| ------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `group` | Text or localization object used to group Collection and Global links in the admin navigation. Set to `false` to hide the link from the navigation while keeping its routes accessible. |
| `hidden` | Set to true or a function, called with the current user, returning true to exclude this Global from navigation and admin routing. |
| `components` | Swap in your own React components to be used within this Global. [More details](#custom-components). |
| `preview` | Function to generate a preview URL within the Admin Panel for this Global that can point to your app. [More details](../admin/preview). |
| `livePreview` | Enable real-time editing for instant visual feedback of your front-end application. [More details](../live-preview/overview). |
| `hideAPIURL` | Hides the "API URL" meta field while editing documents within this collection. |
| `meta` | Page metadata overrides to apply to this Global within the Admin Panel. [More details](../admin/metadata). |
### Custom Components
Globals can set their own [Custom Components](../custom-components/overview) which only apply to Global-specific UI within the [Admin Panel](../admin/overview). This includes elements such as the Save Button, or entire layouts such as the Edit View.
To override Global Components, use the `admin.components` property in your Global Config:
```ts
import type { SanitizedGlobalConfig } from 'payload'
export const MyGlobal: SanitizedGlobalConfig = {
// ...
admin: {
components: {
// highlight-line
// ...
},
},
}
```
The following options are available:
#### General
| Option | Description |
| ---------- | ------------------------------------------------------------------------------------------------------- |
| `elements` | Override or create new elements within the Edit View. [More details](#edit-view-options). |
| `views` | Override or create new views within the Admin Panel. [More details](../custom-components/custom-views). |
#### Edit View Options
```ts
import type { SanitizedGlobalConfig } from 'payload'
export const MyGlobal: SanitizedGlobalConfig = {
// ...
admin: {
components: {
elements: {
// highlight-line
// ...
},
},
},
}
```
The following options are available:
| Option | Description |
| ----------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `SaveButton` | Replace the default Save Button with a Custom Component. [Drafts](../versions/drafts) must be disabled. [More details](../custom-components/edit-view#savebutton). |
| `SaveDraftButton` | Replace the default Save Draft Button with a Custom Component. [Drafts](../versions/drafts) must be enabled and autosave must be disabled. [More details](../custom-components/edit-view#savedraftbutton). |
| `PublishButton` | Replace the default Publish Button with a Custom Component. [Drafts](../versions/drafts) must be enabled. [More details](../custom-components/edit-view#publishbutton). |
| `PreviewButton` | Replace the default Preview Button with a Custom Component. [Preview](../admin/preview) must be enabled. [More details](../custom-components/edit-view#previewbutton). |
<Banner type="success">
**Note:** For details on how to build Custom Components, see [Building Custom
Components](../custom-components/overview#building-custom-components).
</Banner>
## GraphQL
You can completely disable GraphQL for this global by passing `graphQL: false` to your global config. This will completely disable all queries, mutations, and types from appearing in your GraphQL schema.
You can also pass an object to the global's `graphQL` property, which allows you to define the following properties:
| Option | Description |
| ------------------ | ------------------------------------------------------------------------------- |
| `name` | Override the name that will be used in GraphQL schema generation. |
| `disableQueries` | Disable all GraphQL queries that correspond to this global by passing `true`. |
| `disableMutations` | Disable all GraphQL mutations that correspond to this global by passing `true`. |
## TypeScript
You can import types from Payload to help make writing your Global configs easier and type-safe. There are two main types that represent the Global Config, `GlobalConfig` and `SanitizedGlobalConfig`.
The `GlobalConfig` type represents a raw Global Config in its full form, where only the bare minimum properties are marked as required. The `SanitizedGlobalConfig` type represents a Global Config after it has been fully sanitized. Generally, this is only used internally by Payload.
```ts
import type { GlobalConfig, SanitizedGlobalConfig } from 'payload'
```

View File

@@ -1,281 +0,0 @@
---
title: I18n
label: I18n
order: 40
desc: Manage and customize internationalization support in your CMS editor experience
keywords: internationalization, i18n, config, configuration, documentation, Content Management System, cms, headless, javascript, node, react, nextjs
---
The [Admin Panel](../admin/overview) is translated in over [30 languages and counting](https://github.com/payloadcms/payload/tree/main/packages/translations). With I18n, editors can navigate the interface and read API error messages in their preferred language. This is similar to [Localization](./localization), but instead of managing translations for the data itself, you are managing translations for your application's interface.
By default, Payload comes preinstalled with English, but you can easily load other languages into your own application. Languages are automatically detected based on the request. If no language is detected, or if the user's language is not yet supported by your application, English will be chosen.
To add I18n to your project, you first need to install the `@payloadcms/translations` package:
```bash
pnpm install @payloadcms/translations
```
Once installed, it can be configured using the `i18n` key in your [Payload Config](./overview):
```ts
import { buildConfig } from 'payload'
export default buildConfig({
// ...
i18n: {
// highlight-line
// ...
},
})
```
<Banner type="success">
**Note:** If there is a language that Payload does not yet support, we accept
[code
contributions](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md).
</Banner>
## Config Options
You can easily customize and override any of the i18n settings that Payload provides by default. Payload will use your custom options and merge them in with its own.
```ts
import { buildConfig } from 'payload'
export default buildConfig({
// ...
// highlight-start
i18n: {
fallbackLanguage: 'en', // default
},
// highlight-end
})
```
The following options are available:
| Option | Description |
| -------------------- | ------------------------------------------------------------------------------------------------------------------ |
| `fallbackLanguage` | The language to fall back to if the user's preferred language is not supported. Default is `'en'`. |
| `translations` | An object containing the translations. The keys are the language codes and the values are the translations. |
| `supportedLanguages` | An object containing the supported languages. The keys are the language codes and the values are the translations. |
## Adding Languages
You can easily add new languages to your Payload app by providing the translations for the new language. Payload maintains a number of built-in translations that can be imported from `@payloadcms/translations`, but you can also provide your own [Custom Translations](#custom-translations) to support any language.
To add a new language, use the `i18n.supportedLanguages` key in your [Payload Config](./overview):
```ts
import { buildConfig } from 'payload'
import { en } from '@payloadcms/translations/languages/en'
import { de } from '@payloadcms/translations/languages/de'
export default buildConfig({
// ...
// highlight-start
i18n: {
supportedLanguages: { en, de },
},
// highlight-end
})
```
<Banner type="warning">
**Tip:** It's best to only support the languages that you need so that the
bundled JavaScript is kept to a minimum for your project.
</Banner>
### Custom Translations
You can customize Payload's built-in translations either by extending existing languages or by adding new languages entirely. This can be done by injecting new translation strings into existing languages, or by providing an entirely new language keys altogether.
To add Custom Translations, use the `i18n.translations` key in your [Payload Config](./overview):
```ts
import { buildConfig } from 'payload'
export default buildConfig({
//...
i18n: {
// highlight-start
translations: {
en: {
custom: {
// namespace can be anything you want
key1: 'Translation with {{variable}}', // translation
},
// override existing translation keys
general: {
dashboard: 'Home',
},
},
},
// highlight-end
},
//...
})
```
### Project Translations
While Payload's built-in features come fully translated, you may also want to translate parts of your own project. This is possible in places like [Collections](./collections) and [Globals](./globals), such as on their labels and groups, field labels, descriptions or input placeholder text.
To do this, provide the translations wherever applicable, keyed to the language code:
```ts
import type { CollectionConfig } from 'payload'
export const Articles: CollectionConfig = {
slug: 'articles',
labels: {
singular: {
// highlight-start
en: 'Article',
es: 'Artículo',
// highlight-end
},
plural: {
// highlight-start
en: 'Articles',
es: 'Artículos',
// highlight-end
},
},
admin: {
group: {
// highlight-start
en: 'Content',
es: 'Contenido',
// highlight-end
},
},
fields: [
{
name: 'title',
type: 'text',
label: {
// highlight-start
en: 'Title',
es: 'Título',
// highlight-end
},
admin: {
placeholder: {
// highlight-start
en: 'Enter title',
es: 'Introduce el título',
// highlight-end
},
},
},
],
}
```
## Changing Languages
Users can change their preferred language in their account settings or by otherwise manipulating their [User Preferences](../admin/preferences).
## Node.js#node
Payload's backend sets the language on incoming requests before they are handled. This allows backend validation to return error messages in the user's own language or system generated emails to be sent using the correct translation. You can make HTTP requests with the `accept-language` header and Payload will use that language.
Anywhere in your Payload app that you have access to the `req` object, you can access Payload's extensive internationalization features assigned to `req.i18n`. To access text translations you can use `req.t('namespace:key')`.
## TypeScript
In order to use [Custom Translations](#custom-translations) in your project, you need to provide the types for the translations.
Here we create a shareable translations object. We will import this in both our custom components and in our Payload config.
In this example we show how to extend English, but you can do the same for any language you want.
```ts
// <rootDir>/custom-translations.ts
import { enTranslations } from '@payloadcms/translations/languages/en'
import type { NestedKeysStripped } from '@payloadcms/translations'
export const customTranslations = {
en: {
general: {
myCustomKey: 'My custom english translation',
},
fields: {
addLabel: 'Add!',
},
},
}
export type CustomTranslationsObject = typeof customTranslations.en &
typeof enTranslations
export type CustomTranslationsKeys =
NestedKeysStripped<CustomTranslationsObject>
```
Import the shared translations object into our Payload config so they are available for use:
```ts
// <rootDir>/payload.config.ts
import { buildConfig } from 'payload'
import { customTranslations } from './custom-translations'
export default buildConfig({
//...
i18n: {
translations: customTranslations,
},
//...
})
```
Import the shared translation types to use in your [Custom Component](../custom-components/overview):
```ts
// <rootDir>/components/MyComponent.tsx
'use client'
import type React from 'react'
import { useTranslation } from '@payloadcms/ui'
import type {
CustomTranslationsObject,
CustomTranslationsKeys,
} from '../custom-translations'
export const MyComponent: React.FC = () => {
const { i18n, t } = useTranslation<
CustomTranslationsObject,
CustomTranslationsKeys
>() // These generics merge your custom translations with the default client translations
return t('general:myCustomKey')
}
```
Additionally, Payload exposes the `t` function in various places, for example in labels. Here is how you would type those:
```ts
// <rootDir>/fields/myField.ts
import type {
DefaultTranslationKeys,
TFunction,
} from '@payloadcms/translations'
import type { Field } from 'payload'
import { CustomTranslationsKeys } from '../custom-translations'
const field: Field = {
name: 'myField',
type: 'text',
label: ({ t: defaultT }) => {
const t = defaultT as TFunction<CustomTranslationsKeys>
return t('fields:addLabel')
},
}
```

View File

@@ -1,244 +0,0 @@
---
title: Localization
label: Localization
order: 50
desc: Add and maintain as many locales as you need by adding Localization to your Payload Config, set options for default locale, fallbacks, fields and more.
keywords: localization, internationalization, i18n, config, configuration, documentation, Content Management System, cms, headless, javascript, node, react, nextjs
---
Localization is one of the most important features of a modern CMS. It allows you to manage content in multiple languages, then serve it to your users based on their requested language. This is similar to [I18n](./i18n), but instead of managing translations for your application's interface, you are managing translations for the data itself.
With Localization, you can begin to serve personalized content to your users based on their specific language preferences, such as a multilingual website or multi-site application. There are no limits to the number of locales you can add to your Payload project.
To configure Localization, use the `localization` key in your [Payload Config](./overview):
```ts
import { buildConfig } from 'payload'
export default buildConfig({
// ...
localization: {
// highlight-line
// ...
},
})
```
## Config Options
Add the `localization` property to your Payload Config to enable Localization project-wide. You'll need to provide a list of all locales that you'd like to support as well as set a few other options.
To configure locales, use the `localization.locales` property in your [Payload Config](./overview):
```ts
import { buildConfig } from 'payload'
export default buildConfig({
// ...
localization: {
locales: ['en', 'es', 'de'], // required
defaultLocale: 'en', // required
},
})
```
You can also define locales using [full configuration objects](#locale-object):
```ts
import { buildConfig } from 'payload'
export default buildConfig({
collections: [
// collections go here
],
localization: {
locales: [
{
label: 'English',
code: 'en',
},
{
label: 'Arabic',
code: 'ar',
// opt-in to setting default text-alignment on Input fields to rtl (right-to-left)
// when current locale is rtl
rtl: true,
},
],
defaultLocale: 'en', // required
fallback: true, // defaults to true
},
})
```
<Banner type="success">
**Tip:** Localization works very well alongside
[I18n](/docs/configuration/i18n).
</Banner>
The following options are available:
| Option | Description |
| ---------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **`locales`** | Array of all the languages that you would like to support. [More details](#locales) |
| **`defaultLocale`** | Required string that matches one of the locale codes from the array provided. By default, if no locale is specified, documents will be returned in this locale. |
| **`fallback`** | Boolean enabling "fallback" locale functionality. If a document is requested in a locale, but a field does not have a localized value corresponding to the requested locale, then if this property is enabled, the document will automatically fall back to the fallback locale value. If this property is not enabled, the value will not be populated unless a fallback is explicitly provided in the request. True by default. |
| **`filterAvailableLocales`** | A function that is called with the array of `locales` and the `req`, it should return locales to show in admin UI selector. [See more](#filter-available-options). |
### Locales
The locales array is a list of all the languages that you would like to support. This can be strings for each language code, or [full configuration objects](#locale-object) for more advanced options.
The locale codes do not need to be in any specific format. It's up to you to define how to represent your locales. Common patterns are to use two-letter ISO 639 language codes or four-letter language and country codes (ISO 31661) such as `en-US`, `en-UK`, `es-MX`, etc.
#### Locale Object
| Option | Description |
| -------------------- | ------------------------------------------------------------------------------------------------------------------------------ |
| **`code`** \* | Unique code to identify the language throughout the APIs for `locale` and `fallbackLocale` |
| **`label`** | A string to use for the selector when choosing a language, or an object keyed on the i18n keys for different languages in use. |
| **`rtl`** | A boolean that when true will make the admin UI display in Right-To-Left. |
| **`fallbackLocale`** | The code for this language to fallback to when properties of a document are not present. |
_\* An asterisk denotes that a property is required._
#### Filter Available Options
In some projects you may want to filter the available locales shown in the admin UI selector. You can do this by providing a `filterAvailableLocales` function in your Payload Config. This is called on the server side and is passed the array of locales. This means that you can determine what locales are visible in the localizer selection menu at the top of the admin panel. You could do this per user, or implement a function that scopes these to tenants and more. Here is an example using request headers in a multi-tenant application:
```ts
// ... rest of Payload config
localization: {
defaultLocale: 'en',
locales: ['en', 'es'],
filterAvailableLocales: async ({ req, locales }) => {
if (getTenantFromCookie(req.headers, 'text')) {
const fullTenant = await req.payload.findByID({
id: getTenantFromCookie(req.headers, 'text') as string,
collection: 'tenants',
req,
})
if (fullTenant && fullTenant.supportedLocales?.length) {
return locales.filter((locale) => {
return fullTenant.supportedLocales?.includes(locale.code as 'en' | 'es')
})
}
}
return locales
},
}
```
Since the filtering happens at the root level of the application and its result is not calculated every time you navigate to a new page, you may want to call `router.refresh` in a custom component that watches when values that affect the result change. In the example above, you would want to do this when `supportedLocales` changes on the tenant document.
## Field Localization
Payload Localization works on a **field** level—not a document level. In addition to configuring the base Payload Config to support Localization, you need to specify each field that you would like to localize.
**Here is an example of how to enable Localization for a field:**
```js
{
name: 'title',
type: 'text',
// highlight-start
localized: true,
// highlight-end
}
```
With the above configuration, the `title` field will now be saved in the database as an object of all locales instead of a single string.
All field types with a `name` property support the `localized` property—even the more complex field types like `array`s and `block`s.
<Banner type="info">
**Note:** Enabling Localization for field types that support nested fields
will automatically create localized "sets" of all fields contained within the
field. For example, if you have a page layout using a blocks field type, you
have the choice of either localizing the full layout, by enabling Localization
on the top-level blocks field, or only certain fields within the layout.
</Banner>
<Banner type="warning">
**Important:** When converting an existing field to or from `localized: true`
the data structure in the document will change for this field and so existing
data for this field will be lost. Before changing the Localization setting on
fields with existing data, you may need to consider a field migration
strategy.
</Banner>
## Retrieving Localized Docs
When retrieving documents, you can specify which locale you'd like to receive as well as which fallback locale should be
used.
#### REST API
REST API locale functionality relies on URL query parameters.
**`?locale=`**
Specify your desired locale by providing the `locale` query parameter directly in the endpoint URL.
**`?fallback-locale=`**
Specify fallback locale to be used by providing the `fallback-locale` query parameter. This can be provided as either a
valid locale as provided to your base Payload Config, or `'null'`, `'false'`, or `'none'` to disable falling back.
**Example:**
```
fetch('https://localhost:3000/api/pages?locale=es&fallback-locale=none');
```
#### GraphQL API
In the GraphQL API, you can specify `locale` and `fallbackLocale` args to all relevant queries and mutations.
The `locale` arg will only accept valid locales, but locales will be formatted automatically as valid GraphQL enum
values (dashes or special characters will be converted to underscores, spaces will be removed, etc.). If you are curious
to see how locales are auto-formatted, you can use the [GraphQL playground](/docs/graphql/overview#graphql-playground).
The `fallbackLocale` arg will accept valid locales as well as `none` to disable falling back.
**Example:**
```graphql
query {
Posts(locale: de, fallbackLocale: none) {
docs {
title
}
}
}
```
<Banner>
In GraphQL, specifying the locale at the top level of a query will
automatically apply it throughout all nested relationship fields. You can
override this behavior by re-specifying locale arguments in nested related
document queries.
</Banner>
#### Local API
You can specify `locale` as well as `fallbackLocale` within the Local API as well as properties on the `options`
argument. The `locale` property will accept any valid locale, and the `fallbackLocale` property will accept any valid
locale as well as `'null'`, `'false'`, `false`, and `'none'`.
**Example:**
```js
const posts = await payload.find({
collection: 'posts',
locale: 'es',
fallbackLocale: false,
})
```
<Banner type="success">
**Tip:** The REST and Local APIs can return all Localization data in one
request by passing 'all' or '*' as the **locale** parameter. The response will
be structured so that field values come back as the full objects keyed for
each locale instead of the single, translated value.
</Banner>

View File

@@ -1,320 +0,0 @@
---
title: The Payload Config
label: Overview
order: 10
desc: The Payload Config is central to everything that Payload does, from adding custom React components, to modifying collections, controlling localization and much more.
keywords: overview, config, configuration, documentation, Content Management System, cms, headless, javascript, node, react, nextjs
---
Payload is a _config-based_, code-first CMS and application framework. The Payload Config is central to everything that Payload does, allowing for deep configuration of your application through a simple and intuitive API. The Payload Config is a fully-typed JavaScript object that can be infinitely extended upon.
Everything from your [Database](../database/overview) choice to the appearance of the [Admin Panel](../admin/overview) is fully controlled through the Payload Config. From here you can define [Fields](../fields/overview), add [Localization](./localization), enable [Authentication](../authentication/overview), configure [Access Control](../access-control/overview), and so much more.
The Payload Config is a `payload.config.ts` file typically located in the root of your project:
```ts
import { buildConfig } from 'payload'
export default buildConfig({
// Your config goes here
})
```
The Payload Config is strongly typed and ties directly into Payload's TypeScript codebase. This means your IDE (such as VSCode) will provide helpful information like type-ahead suggestions while you write your config.
<Banner type="success">
**Tip:** The location of your Payload Config can be customized. [More
details](#customizing-the-config-location).
</Banner>
## Config Options
To author your Payload Config, first determine which [Database](../database/overview) you'd like to use, then use [Collections](./collections) or [Globals](./globals) to define the schema of your data through [Fields](../fields/overview).
Here is one of the simplest possible Payload configs:
```ts
import { buildConfig } from 'payload'
import { mongooseAdapter } from '@payloadcms/db-mongodb'
export default buildConfig({
secret: process.env.PAYLOAD_SECRET,
db: mongooseAdapter({
url: process.env.DATABASE_URI,
}),
collections: [
{
slug: 'pages',
fields: [
{
name: 'title',
type: 'text',
},
],
},
],
})
```
<Banner type="success">
**Note:** For more complex examples, see the
[Templates](https://github.com/payloadcms/payload/tree/main/templates) and
[Examples](https://github.com/payloadcms/payload/tree/main/examples)
directories in the Payload repository.
</Banner>
The following options are available:
| Option | Description |
| -------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **`admin`** | The configuration options for the Admin Panel, including Custom Components, Live Preview, etc. [More details](../admin/overview#admin-options). |
| **`bin`** | Register custom bin scripts for Payload to execute. [More Details](#custom-bin-scripts). |
| **`editor`** | The Rich Text Editor which will be used by `richText` fields. [More details](../rich-text/overview). |
| **`db`** \* | The Database Adapter which will be used by Payload. [More details](../database/overview). |
| **`serverURL`** | A string used to define the absolute URL of your app. This includes the protocol, for example `https://example.com`. No paths allowed, only protocol, domain and (optionally) port. |
| **`collections`** | An array of Collections for Payload to manage. [More details](./collections). |
| **`compatibility`** | Compatibility flags for earlier versions of Payload. [More details](#compatibility-flags). |
| **`globals`** | An array of Globals for Payload to manage. [More details](./globals). |
| **`cors`** | Cross-origin resource sharing (CORS) is a mechanism that accept incoming requests from given domains. You can also customize the `Access-Control-Allow-Headers` header. [More details](#cors). |
| **`localization`** | Opt-in to translate your content into multiple locales. [More details](./localization). |
| **`logger`** | Logger options, logger options with a destination stream, or an instantiated logger instance. [More details](https://getpino.io/#/docs/api?id=options). |
| **`loggingLevels`** | An object to override the level to use in the logger for Payload's errors. |
| **`graphQL`** | Manage GraphQL-specific functionality, including custom queries and mutations, query complexity limits, etc. [More details](../graphql/overview#graphql-options). |
| **`cookiePrefix`** | A string that will be prefixed to all cookies that Payload sets. |
| **`csrf`** | A whitelist array of URLs to allow Payload to accept cookies from. [More details](../authentication/cookies#csrf-attacks). |
| **`defaultDepth`** | If a user does not specify `depth` while requesting a resource, this depth will be used. [More details](../queries/depth). |
| **`defaultMaxTextLength`** | The maximum allowed string length to be permitted application-wide. Helps to prevent malicious public document creation. |
| `folders` | An optional object to configure global folder settings. [More details](../folders/overview). |
| `queryPresets` | An object that to configure Collection Query Presets. [More details](../query-presets/overview). |
| **`maxDepth`** | The maximum allowed depth to be permitted application-wide. This setting helps prevent against malicious queries. Defaults to `10`. [More details](../queries/depth). |
| **`indexSortableFields`** | Automatically index all sortable top-level fields in the database to improve sort performance and add database compatibility for Azure Cosmos and similar. |
| **`upload`** | Base Payload upload configuration. [More details](../upload/overview#payload-wide-upload-options). |
| **`routes`** | Control the routing structure that Payload binds itself to. [More details](../admin/overview#root-level-routes). |
| **`email`** | Configure the Email Adapter for Payload to use. [More details](../email/overview). |
| **`onInit`** | A function that is called immediately following startup that receives the Payload instance as its only argument. |
| **`debug`** | Enable to expose more detailed error information. |
| **`telemetry`** | Disable Payload telemetry by passing `false`. [More details](#telemetry). |
| **`hooks`** | An array of Root Hooks. [More details](../hooks/overview). |
| **`plugins`** | An array of Plugins. [More details](../plugins/overview). |
| **`endpoints`** | An array of Custom Endpoints added to the Payload router. [More details](../rest-api/overview#custom-endpoints). |
| **`custom`** | Extension point for adding custom data (e.g. for plugins). |
| **`i18n`** | Internationalization configuration. Pass all i18n languages you'd like the admin UI to support. Defaults to English-only. [More details](./i18n). |
| **`secret`** \* | A secure, unguessable string that Payload will use for any encryption workflows - for example, password salt / hashing. |
| **`sharp`** | If you would like Payload to offer cropping, focal point selection, and automatic media resizing, install and pass the Sharp module to the config here. |
| **`typescript`** | Configure TypeScript settings here. [More details](#typescript). |
_\* An asterisk denotes that a property is required._
<Banner type="warning">
**Note:** Some properties are removed from the client-side bundle. [More
details](../custom-components/overview#accessing-the-payload-config).
</Banner>
### TypeScript Config
Payload exposes a variety of TypeScript settings that you can leverage. These settings are used to auto-generate TypeScript interfaces for your [Collections](./collections) and [Globals](./globals), and to ensure that Payload uses your [Generated Types](../typescript/overview) for all [Local API](../local-api/overview) methods.
To customize the TypeScript settings, use the `typescript` property in your Payload Config:
```ts
import { buildConfig } from 'payload'
export default buildConfig({
// ...
// highlight-start
typescript: {
// ...
},
// highlight-end
})
```
The following options are available:
| Option | Description |
| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **`autoGenerate`** | By default, Payload will auto-generate TypeScript interfaces for all collections and globals that your config defines. Opt out by setting `typescript.autoGenerate: false`. [More details](../typescript/overview). |
| **`declare`** | By default, Payload adds a `declare` block to your generated types, which makes sure that Payload uses your generated types for all Local API methods. Opt out by setting `typescript.declare: false`. |
| **`outputFile`** | Control the output path and filename of Payload's auto-generated types by defining the `typescript.outputFile` property to a full, absolute path. |
## Config Location
For Payload command-line scripts, we need to be able to locate your Payload Config. We'll check a variety of locations for the presence of `payload.config.ts` by default, including:
1. The root current working directory
1. The `compilerOptions` in your `tsconfig`\*
1. The `dist` directory\*
_\* Config location detection is different between development and production environments. See below for more details._
<Banner type="warning">
**Important:** Ensure your `tsconfig.json` is properly configured for Payload
to auto-detect your config location. If it does not exist, or does not specify
the proper `compilerOptions`, Payload will default to the current working
directory.
</Banner>
**Development Mode**
In development mode, if the configuration file is not found at the root, Payload will attempt to read your `tsconfig.json`, and attempt to find the config file specified in the `rootDir`:
```json
{
// ...
// highlight-start
"compilerOptions": {
"rootDir": "src"
}
// highlight-end
}
```
**Production Mode**
In production mode, Payload will first attempt to find the config file in the `outDir` of your `tsconfig.json`, and if not found, will fallback to the `rootDir` directory:
```json
{
// ...
// highlight-start
"compilerOptions": {
"outDir": "dist",
"rootDir": "src"
}
// highlight-end
}
```
If none was in either location, Payload will finally check the `dist` directory.
### Customizing the Config Location
In addition to the above automated detection, you can specify your own location for the Payload Config. This can be useful in situations where your config is not in a standard location, or you wish to switch between multiple configurations. To do this, Payload exposes an [Environment Variable](../configuration/environment-vars) to bypass all automatic config detection.
To use a custom config location, set the `PAYLOAD_CONFIG_PATH` environment variable:
```json
{
"scripts": {
"payload": "PAYLOAD_CONFIG_PATH=/path/to/custom-config.ts payload"
}
}
```
<Banner type="info">
**Tip:** `PAYLOAD_CONFIG_PATH` can be either an absolute path, or path
relative to your current working directory.
</Banner>
## Telemetry
Payload collects **completely anonymous** telemetry data about general usage. This data is super important to us and helps us accurately understand how we're growing and what we can do to build the software into everything that it can possibly be. The telemetry that we collect also help us demonstrate our growth in an accurate manner, which helps us as we seek investment to build and scale our team. If we can accurately demonstrate our growth, we can more effectively continue to support Payload as free and open-source software. To opt out of telemetry, you can pass `telemetry: false` within your Payload Config.
For more information about what we track, take a look at our [privacy policy](/privacy).
## Cross-origin resource sharing (CORS)#cors
Cross-origin resource sharing (CORS) can be configured with either a whitelist array of URLS to allow CORS requests from, a wildcard string (`*`) to accept incoming requests from any domain, or an object with the following properties:
| Option | Description |
| ------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
| **`origins`** | Either a whitelist array of URLS to allow CORS requests from, or a wildcard string (`'*'`) to accept incoming requests from any domain. |
| **`headers`** | A list of allowed headers that will be appended in `Access-Control-Allow-Headers`. |
Here's an example showing how to allow incoming requests from any domain:
```ts
import { buildConfig } from 'payload'
export default buildConfig({
// ...
// highlight-start
cors: '*',
// highlight-end
})
```
Here's an example showing how to append a new header (`x-custom-header`) in `Access-Control-Allow-Headers`:
```ts
import { buildConfig } from 'payload'
export default buildConfig({
// ...
// highlight-start
cors: {
origins: ['http://localhost:3000'],
headers: ['x-custom-header'],
},
// highlight-end
})
```
## TypeScript
You can import types from Payload to help make writing your config easier and type-safe. There are two main types that represent the Payload Config, `Config` and `SanitizedConfig`.
The `Config` type represents a raw Payload Config in its full form. Only the bare minimum properties are marked as required. The `SanitizedConfig` type represents a Payload Config after it has been fully sanitized. Generally, this is only used internally by Payload.
```ts
import type { Config, SanitizedConfig } from 'payload'
```
## Server vs. Client
The Payload Config only lives on the server and is not allowed to contain any client-side code. That way, you can load up the Payload Config in any server environment or standalone script, without having to use Bundlers or Node.js loaders to handle importing client-only modules (e.g. scss files or React Components) without any errors.
Behind the curtains, the Next.js-based Admin Panel generates a ClientConfig, which strips away any server-only code and enriches the config with React Components.
## Compatibility flags
The Payload Config can accept compatibility flags for running the newest versions but with older databases. You should only use these flags if you need to, and should confirm that you need to prior to enabling these flags.
`allowLocalizedWithinLocalized`
Payload localization works on a field-by-field basis. As you can nest fields within other fields, you could potentially nest a localized field within a localized field—but this would be redundant and unnecessary. There would be no reason to define a localized field within a localized parent field, given that the entire data structure from the parent field onward would be localized.
By default, Payload will remove the `localized: true` property from sub-fields if a parent field is localized. Set this compatibility flag to `true` only if you have an existing Payload MongoDB database from pre-3.0, and you have nested localized fields that you would like to maintain without migrating.
## Custom bin scripts
Using the `bin` configuration property, you can inject your own scripts to `npx payload`.
Example for `pnpm payload seed`:
Step 1: create `seed.ts` file in the same folder with `payload.config.ts` with:
```ts
import type { SanitizedConfig } from 'payload'
import payload from 'payload'
// Script must define a "script" function export that accepts the sanitized config
export const script = async (config: SanitizedConfig) => {
await payload.init({ config })
await payload.create({
collection: 'pages',
data: { title: 'my title' },
})
payload.logger.info('Successfully seeded!')
process.exit(0)
}
```
Step 2: add the `seed` script to `bin`:
```ts
export default buildConfig({
bin: [
{
scriptPath: path.resolve(dirname, 'seed.ts'),
key: 'seed',
},
],
})
```
Now you can run the command using:
```sh
pnpm payload seed
```

View File

@@ -1,48 +0,0 @@
---
title: Swap in your own React Context providers
label: Custom Providers
order: 30
desc:
keywords: admin, components, custom, documentation, Content Management System, cms, headless, javascript, node, react, nextjs
---
As you add more and more [Custom Components](./overview) to your [Admin Panel](../admin/overview), you may find it helpful to add additional [React Context](https://react.dev/learn/scaling-up-with-reducer-and-context)(s) to your app. Payload allows you to inject your own context providers where you can export your own custom hooks, etc.
To add a Custom Provider, use the `admin.components.providers` property in your [Payload Config](../configuration/overview):
```ts
import { buildConfig } from 'payload'
export default buildConfig({
// ...
admin: {
components: {
providers: ['/path/to/MyProvider'], // highlight-line
},
},
})
```
Then build your Custom Provider as follows:
```tsx
'use client'
import React, { createContext, use } from 'react'
const MyCustomContext = React.createContext(myCustomValue)
export function MyProvider({ children }: { children: React.ReactNode }) {
return <MyCustomContext value={myCustomValue}>{children}</MyCustomContext>
}
export const useMyCustomContext = () => use(MyCustomContext)
```
_For details on how to build Custom Components, see [Building Custom Components](./overview#building-custom-components)._
<Banner type="warning">
**Reminder:** React Context exists only within Client Components. This means
they must include the `use client` directive at the top of their files and
cannot contain server-only code. To use a Server Component here, simply _wrap_
your Client Component with it.
</Banner>

View File

@@ -1,361 +0,0 @@
---
title: Customizing Views
label: Customizing Views
order: 40
desc:
keywords:
---
Views are the individual pages that make up the [Admin Panel](../admin/overview), such as the Dashboard, [List View](./list-view), and [Edit View](./edit-view). One of the most powerful ways to customize the Admin Panel is to create Custom Views. These are [Custom Components](./overview) that can either replace built-in views or be entirely new.
There are four types of views within the Admin Panel:
- [Root Views](#root-views)
- [Collection Views](#collection-views)
- [Global Views](#global-views)
- [Document Views](./document-views)
To swap in your own Custom View, first determine the scope that corresponds to what you are trying to accomplish, consult the list of available components, then [author your React component(s)](#building-custom-views) accordingly.
## Configuration
### Replacing Views
To customize views, use the `admin.components.views` property in your [Payload Config](../configuration/overview). This is an object with keys for each view you want to customize. Each key corresponds to the view you want to customize.
The exact list of available keys depends on the scope of the view you are customizing, depending on whether it's a [Root View](#root-views), [Collection View](#collection-views), or [Global View](#global-views). Regardless of the scope, the principles are the same.
Here is an example of how to swap out a built-in view:
```ts
import { buildConfig } from 'payload'
const config = buildConfig({
// ...
admin: {
components: {
views: {
// highlight-start
dashboard: {
Component: '/path/to/MyCustomDashboard',
},
// highlight-end
},
},
},
})
```
For more granular control, pass a configuration object instead. Payload exposes the following properties for each view:
| Property | Description |
| -------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `Component` \* | Pass in the component path that should be rendered when a user navigates to this route. |
| `path` \* | Any valid URL path or array of paths that [`path-to-regexp`](https://www.npmjs.com/package/path-to-regex) understands. Must begin with a forward slash (`/`). |
| `exact` | Boolean. When true, will only match if the path matches the `usePathname()` exactly. |
| `strict` | When true, a path that has a trailing slash will only match a `location.pathname` with a trailing slash. This has no effect when there are additional URL segments in the pathname. |
| `sensitive` | When true, will match if the path is case sensitive. |
| `meta` | Page metadata overrides to apply to this view within the Admin Panel. [More details](../admin/metadata). |
_\* An asterisk denotes that a property is required._
### Adding New Views
To add a _new_ view to the [Admin Panel](../admin/overview), simply add your own key to the `views` object. This is true for all view scopes.
New views require at least the `Component` and `path` properties:
```ts
import { buildConfig } from 'payload'
const config = buildConfig({
// ...
admin: {
components: {
views: {
// highlight-start
myCustomView: {
Component: '/path/to/MyCustomView#MyCustomViewComponent',
path: '/my-custom-view',
},
// highlight-end
},
},
},
})
```
<Banner type="warning">
**Note:** Routes are cascading, so unless explicitly given the `exact`
property, they will match on URLs that simply _start_ with the route's path.
This is helpful when creating catch-all routes in your application.
Alternatively, define your nested route _before_ your parent route.
</Banner>
## Building Custom Views
Custom Views are simply [Custom Components](./overview) rendered at the page-level. Custom Views can either [replace existing views](#replacing-views) or [add entirely new ones](#adding-new-views). The process is generally the same regardless of the type of view you are customizing.
To understand how to build Custom Views, first review the [Building Custom Components](./overview#building-custom-components) guide. Once you have a Custom Component ready, you can use it as a Custom View.
```ts
import type { CollectionConfig } from 'payload'
export const MyCollectionConfig: CollectionConfig = {
// ...
admin: {
components: {
views: {
// highlight-start
edit: {
Component: '/path/to/MyCustomView', // highlight-line
},
// highlight-end
},
},
},
}
```
### Default Props
Your Custom Views will be provided with the following props:
| Prop | Description |
| ---------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
| `initPageResult` | An object containing `req`, `payload`, `permissions`, etc. |
| `clientConfig` | The Client Config object. [More details](./overview#accessing-the-payload-config). |
| `importMap` | The import map object. |
| `params` | An object containing the [Dynamic Route Parameters](https://nextjs.org/docs/app/building-your-application/routing/dynamic-routes). |
| `searchParams` | An object containing the [Search Parameters](https://developer.mozilla.org/docs/Learn/Common_questions/What_is_a_URL#parameters). |
| `doc` | The document being edited. Only available in Document Views. [More details](./document-views). |
| `i18n` | The [i18n](../configuration/i18n) object. |
| `payload` | The [Payload](../local-api/overview) class. |
<Banner type="warning">
**Note:** Some views may receive additional props, such as [Collection
Views](#collection-views) and [Global Views](#global-views). See the relevant
section for more details.
</Banner>
Here is an example of a Custom View component:
```tsx
import type { AdminViewServerProps } from 'payload'
import { Gutter } from '@payloadcms/ui'
import React from 'react'
export function MyCustomView(props: AdminViewServerProps) {
return (
<Gutter>
<h1>Custom Default Root View</h1>
<p>This view uses the Default Template.</p>
</Gutter>
)
}
```
<Banner type="success">
**Tip:** For consistent layout and navigation, you may want to wrap your
Custom View with one of the built-in [Template](./overview#templates).
</Banner>
### View Templates
Your Custom Root Views can optionally use one of the templates that Payload provides. The most common of these is the Default Template which provides the basic layout and navigation.
Here is an example of how to use the Default Template in your Custom View:
```tsx
import type { AdminViewServerProps } from 'payload'
import { DefaultTemplate } from '@payloadcms/next/templates'
import { Gutter } from '@payloadcms/ui'
import React from 'react'
export function MyCustomView({
initPageResult,
params,
searchParams,
}: AdminViewServerProps) {
return (
<DefaultTemplate
i18n={initPageResult.req.i18n}
locale={initPageResult.locale}
params={params}
payload={initPageResult.req.payload}
permissions={initPageResult.permissions}
searchParams={searchParams}
user={initPageResult.req.user || undefined}
visibleEntities={initPageResult.visibleEntities}
>
<Gutter>
<h1>Custom Default Root View</h1>
<p>This view uses the Default Template.</p>
</Gutter>
</DefaultTemplate>
)
}
```
### Securing Custom Views
All Custom Views are public by default. It's up to you to secure your custom views. If your view requires a user to be logged in or to have certain access rights, you should handle that within your view component yourself.
Here is how you might secure a Custom View:
```tsx
import type { AdminViewServerProps } from 'payload'
import { Gutter } from '@payloadcms/ui'
import React from 'react'
export function MyCustomView({ initPageResult }: AdminViewServerProps) {
const {
req: { user },
} = initPageResult
if (!user) {
return <p>You must be logged in to view this page.</p>
}
return (
<Gutter>
<h1>Custom Default Root View</h1>
<p>This view uses the Default Template.</p>
</Gutter>
)
}
```
## Root Views
Root Views are the main views of the [Admin Panel](../admin/overview). These are views that are scoped directly under the `/admin` route, such as the Dashboard or Account views.
To [swap out](#replacing-views) Root Views with your own, or to [create entirely new ones](#adding-new-views), use the `admin.components.views` property at the root of your [Payload Config](../configuration/overview):
```ts
import { buildConfig } from 'payload'
const config = buildConfig({
// ...
admin: {
components: {
views: {
// highlight-start
dashboard: {
Component: '/path/to/Dashboard',
},
// highlight-end
// Other options include:
// - account
// - [key: string]
// See below for more details
},
},
},
})
```
_For details on how to build Custom Views, including all available props, see [Building Custom Views](#building-custom-views)._
The following options are available:
| Property | Description |
| ----------- | ----------------------------------------------------------------------------------------------- |
| `account` | The Account view is used to show the currently logged in user's Account page. |
| `dashboard` | The main landing page of the Admin Panel. |
| `[key]` | Any other key can be used to add a completely new Root View. [More details](#adding-new-views). |
## Collection Views
Collection Views are views that are scoped under the `/collections` route, such as the Collection List and Document Edit views.
To [swap out](#replacing-views) Collection Views with your own, or to [create entirely new ones](#adding-new-views), use the `admin.components.views` property of your [Collection Config](../configuration/collections):
```ts
import type { CollectionConfig } from 'payload'
export const MyCollectionConfig: CollectionConfig = {
// ...
admin: {
components: {
views: {
// highlight-start
edit: {
default: {
Component: '/path/to/MyCustomCollectionView',
},
},
// highlight-end
// Other options include:
// - list
// - [key: string]
// See below for more details
},
},
},
}
```
<Banner type="success">
**Reminder:** The `edit` key is comprised of various nested views, known as
Document Views, that relate to the same Collection Document. [More
details](./document-views).
</Banner>
The following options are available:
| Property | Description |
| -------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
| `edit` | The Edit View corresponds to a single Document for any given Collection and consists of various nested views. [More details](./document-views). |
| `list` | The List View is used to show a list of Documents for any given Collection. [More details](#list-view). |
| `[key]` | Any other key can be used to add a completely new Collection View. [More details](#adding-new-views). |
_For details on how to build Custom Views, including all available props, see [Building Custom Views](#building-custom-views)._
## Global Views
Global Views are views that are scoped under the `/globals` route, such as the Edit View.
To [swap out](#replacing-views) Global Views with your own or [create entirely new ones](#adding-new-views), use the `admin.components.views` property in your [Global Config](../configuration/globals):
```ts
import type { SanitizedGlobalConfig } from 'payload'
export const MyGlobalConfig: SanitizedGlobalConfig = {
// ...
admin: {
components: {
views: {
// highlight-start
edit: {
default: {
Component: '/path/to/MyCustomGlobalView',
},
},
// highlight-end
// Other options include:
// - [key: string]
// See below for more details
},
},
},
}
```
<Banner type="success">
**Reminder:** The `edit` key is comprised of various nested views, known as
Document Views, that relate to the same Global Document. [More
details](./document-views).
</Banner>
The following options are available:
| Property | Description |
| -------- | --------------------------------------------------------------------------------------------------------------------------------------- |
| `edit` | The Edit View represents a single Document for any given Global and consists of various nested views. [More details](./document-views). |
| `[key]` | Any other key can be used to add a completely new Global View. [More details](#adding-new-views). |
_For details on how to build Custom Views, including all available props, see [Building Custom Views](#building-custom-views)._

View File

@@ -1,182 +0,0 @@
---
title: Document Views
label: Document Views
order: 50
desc:
keywords:
---
Document Views consist of multiple, individual views that together represent any single [Collection](../configuration/collections) or [Global](../configuration/globals) Document. All Document Views and are scoped under the `/collections/:collectionSlug/:id` or the `/globals/:globalSlug` route, respectively.
There are a number of default Document Views, such as the [Edit View](./edit-view) and API View, but you can also create [entirely new views](./custom-views#adding-new-views) as needed. All Document Views share a layout and can be given their own tab-based navigation, if desired.
To customize Document Views, use the `admin.components.views.edit[key]` property in your [Collection Config](../configuration/collections) or [Global Config](../configuration/globals):
```ts
import type { CollectionConfig } from 'payload'
export const MyCollectionOrGlobalConfig: CollectionConfig = {
// ...
admin: {
components: {
views: {
// highlight-start
edit: {
default: {
Component: '/path/to/MyCustomEditView',
},
// Other options include:
// - root
// - api
// - versions
// - version
// - [key: string]
// See below for more details
},
// highlight-end
},
},
},
}
```
## Config Options
The following options are available:
| Property | Description |
| ------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `root` | The Root View overrides all other nested views and routes. No document controls or tabs are rendered when this key is set. [More details](#document-root). |
| `default` | The Default View is the primary view in which your document is edited. It is rendered within the "Edit" tab. [More details](./edit-view). |
| `versions` | The Versions View is used to navigate the version history of a single document. It is rendered within the "Versions" tab. [More details](../versions/overview). |
| `version` | The Version View is used to edit a single version of a document. It is rendered within the "Version" tab. [More details](../versions/overview). |
| `api` | The API View is used to display the REST API JSON response for a given document. It is rendered within the "API" tab. |
| `livePreview` | The LivePreview view is used to display the Live Preview interface. It is rendered within the "Live Preview" tab. [More details](../live-preview/overview). |
| `[key]` | Any other key can be used to add a completely new Document View. |
_For details on how to build Custom Views, including all available props, see [Building Custom Views](./custom-views#building-custom-views)._
### Document Root
The Document Root is mounted on the top-level route for a Document. Setting this property will completely take over the entire Document View layout, including the title, [Document Tabs](#document-tabs), _and all other nested Document Views_ including the [Edit View](./edit-view), API View, etc.
When setting a Document Root, you are responsible for rendering all necessary components and controls, as no document controls or tabs would be rendered. To replace only the Edit View precisely, use the `edit.default` key instead.
To override the Document Root, use the `views.edit.root` property in your [Collection Config](../configuration/collections) or [Global Config](../configuration/globals):
```ts
import type { CollectionConfig } from 'payload'
export const MyCollection: CollectionConfig = {
slug: 'my-collection',
admin: {
components: {
views: {
edit: {
// highlight-start
root: {
Component: '/path/to/MyCustomRootComponent', // highlight-line
},
// highlight-end
},
},
},
},
}
```
### Edit View
The Edit View is where users interact with individual Collection and Global Documents. This is where they can view, edit, and save their content. The Edit View is keyed under the `default` property in the `views.edit` object.
For more information on customizing the Edit View, see the [Edit View](./edit-view) documentation.
## Document Tabs
Each Document View can be given a tab for navigation, if desired. Tabs are highly configurable, from as simple as changing the label to swapping out the entire component, they can be modified in any way.
To add or customize tabs in the Document View, use the `views.edit.[key].tab` property in your [Collection Config](../configuration/collections) or [Global Config](../configuration/globals):
```ts
import type { CollectionConfig } from 'payload'
export const MyCollection: CollectionConfig = {
slug: 'my-collection',
admin: {
components: {
views: {
edit: {
myCustomView: {
Component: '/path/to/MyCustomView',
path: '/my-custom-tab',
// highlight-start
tab: {
Component: '/path/to/MyCustomTabComponent',
},
// highlight-end
},
anotherCustomView: {
Component: '/path/to/AnotherCustomView',
path: '/another-custom-view',
// highlight-start
tab: {
label: 'Another Custom View',
href: '/another-custom-view',
order: '100',
},
// highlight-end
},
},
},
},
},
}
```
<Banner type="warning">
**Note:** This applies to _both_ Collections _and_ Globals.
</Banner>
The following options are available for tabs:
| Property | Description |
| ----------- | ------------------------------------------------------------------------------------------------------------- |
| `label` | The label to display in the tab. |
| `href` | The URL to navigate to when the tab is clicked. This is optional and defaults to the tab's `path`. |
| `order` | The order in which the tab appears in the navigation. Can be set on default and custom tabs. |
| `Component` | The component to render in the tab. This can be a Server or Client component. [More details](#tab-components) |
### Tab Components
If changing the label or href is not enough, you can also replace the entire tab component with your own custom component. This can be done by setting the `tab.Component` property to the path of your custom component.
Here is an example of how to scaffold a custom Document Tab:
#### Server Component
```tsx
import React from 'react'
import type { DocumentTabServerProps } from 'payload'
import { Link } from '@payloadcms/ui'
export function MyCustomTabComponent(props: DocumentTabServerProps) {
return (
<Link href="/my-custom-tab">This is a custom Document Tab (Server)</Link>
)
}
```
#### Client Component
```tsx
'use client'
import React from 'react'
import type { DocumentTabClientProps } from 'payload'
import { Link } from '@payloadcms/ui'
export function MyCustomTabComponent(props: DocumentTabClientProps) {
return (
<Link href="/my-custom-tab">This is a custom Document Tab (Client)</Link>
)
}
```

View File

@@ -1,587 +0,0 @@
---
title: Edit View
label: Edit View
order: 60
desc:
keywords: admin, components, custom, documentation, Content Management System, cms, headless, javascript, node, react, nextjs
---
The Edit View is where users interact with individual [Collection](../configuration/collections) and [Global](../configuration/globals) Documents within the [Admin Panel](../admin/overview). The Edit View contains the actual form in which submits the data to the server. This is where they can view, edit, and save their content. It contains controls for saving, publishing, and previewing the document, all of which can be customized to a high degree.
The Edit View can be swapped out in its entirety for a Custom View, or it can be injected with a number of Custom Components to add additional functionality or presentational elements without replacing the entire view.
<Banner type="warning">
**Note:** The Edit View is one of many [Document Views](./document-views) in
the Payload Admin Panel. Each Document View is responsible for a different
aspect of the interacting with a single Document.
</Banner>
## Custom Edit View
To swap out the entire Edit View with a [Custom View](./custom-views), use the `views.edit.default` property in your [Collection Config](../configuration/collections) or [Global Config](../configuration/globals):
```tsx
import { buildConfig } from 'payload'
const config = buildConfig({
// ...
admin: {
components: {
views: {
edit: {
// highlight-start
default: {
Component: '/path/to/MyCustomEditViewComponent',
},
// highlight-end
},
},
},
},
})
```
Here is an example of a custom Edit View:
#### Server Component
```tsx
import React from 'react'
import type { DocumentViewServerProps } from 'payload'
export function MyCustomServerEditView(props: DocumentViewServerProps) {
return <div>This is a custom Edit View (Server)</div>
}
```
#### Client Component
```tsx
'use client'
import React from 'react'
import type { DocumentViewClientProps } from 'payload'
export function MyCustomClientEditView(props: DocumentViewClientProps) {
return <div>This is a custom Edit View (Client)</div>
}
```
_For details on how to build Custom Views, including all available props, see [Building Custom Views](./custom-views#building-custom-views)._
## Custom Components
In addition to swapping out the entire Edit View with a [Custom View](./custom-views), you can also override individual components. This allows you to customize specific parts of the Edit View without swapping out the entire view.
<Banner type="warning">
**Important:** Collection and Globals are keyed to a different property in the
`admin.components` object have slightly different options. Be sure to use the
correct key for the entity you are working with.
</Banner>
#### Collections
To override Edit View components for a Collection, use the `admin.components.edit` property in your [Collection Config](../configuration/collections):
```ts
import type { CollectionConfig } from 'payload'
export const MyCollection: CollectionConfig = {
// ...
admin: {
components: {
// highlight-start
edit: {
// ...
},
// highlight-end
},
},
}
```
The following options are available:
| Path | Description |
| ------------------------ | ---------------------------------------------------------------------------------------------------------------------------- |
| `beforeDocumentControls` | Inject custom components before the Save / Publish buttons. [More details](#beforedocumentcontrols). |
| `editMenuItems` | Inject custom components within the 3-dot menu dropdown located in the document control bar. [More details](#editmenuitems). |
| `SaveButton` | A button that saves the current document. [More details](#savebutton). |
| `SaveDraftButton` | A button that saves the current document as a draft. [More details](#savedraftbutton). |
| `PublishButton` | A button that publishes the current document. [More details](#publishbutton). |
| `PreviewButton` | A button that previews the current document. [More details](#previewbutton). |
| `Description` | A description of the Collection. [More details](#description). |
| `Upload` | A file upload component. [More details](#upload). |
#### Globals
To override Edit View components for Globals, use the `admin.components.elements` property in your [Global Config](../configuration/globals):
```ts
import type { GlobalConfig } from 'payload'
export const MyGlobal: GlobalConfig = {
// ...
admin: {
components: {
// highlight-start
elements: {
// ...
},
// highlight-end
},
},
}
```
The following options are available:
| Path | Description |
| ------------------------ | ---------------------------------------------------------------------------------------------------------------------------- |
| `beforeDocumentControls` | Inject custom components before the Save / Publish buttons. [More details](#beforedocumentcontrols). |
| `editMenuItems` | Inject custom components within the 3-dot menu dropdown located in the document control bar. [More details](#editmenuitems). |
| `SaveButton` | A button that saves the current document. [More details](#savebutton). |
| `SaveDraftButton` | A button that saves the current document as a draft. [More details](#savedraftbutton). |
| `PublishButton` | A button that publishes the current document. [More details](#publishbutton). |
| `PreviewButton` | A button that previews the current document. [More details](#previewbutton). |
| `Description` | A description of the Global. [More details](#description). |
### SaveButton
The `SaveButton` property allows you to render a custom Save Button in the Edit View.
To add a `SaveButton` component, use the `components.edit.SaveButton` property in your [Collection Config](../configuration/collections) or `components.elements.SaveButton` in your [Global Config](../configuration/globals):
```ts
import type { CollectionConfig } from 'payload'
export const MyCollection: CollectionConfig = {
// ...
admin: {
components: {
edit: {
// highlight-start
SaveButton: '/path/to/MySaveButton',
// highlight-end
},
},
},
}
```
Here's an example of a custom `SaveButton` component:
#### Server Component
```tsx
import React from 'react'
import { SaveButton } from '@payloadcms/ui'
import type { SaveButtonServerProps } from 'payload'
export function MySaveButton(props: SaveButtonServerProps) {
return <SaveButton label="Save" />
}
```
#### Client Component
```tsx
'use client'
import React from 'react'
import { SaveButton } from '@payloadcms/ui'
import type { SaveButtonClientProps } from 'payload'
export function MySaveButton(props: SaveButtonClientProps) {
return <SaveButton label="Save" />
}
```
### beforeDocumentControls
The `beforeDocumentControls` property allows you to render custom components just before the default document action buttons (like Save, Publish, or Preview). This is useful for injecting custom buttons, status indicators, or any other UI elements before the built-in controls.
To add `beforeDocumentControls` components, use the `components.edit.beforeDocumentControls` property in you [Collection Config](../configuration/collections) or `components.elements.beforeDocumentControls` in your [Global Config](../configuration/globals):
#### Collections
```
export const MyCollection: CollectionConfig = {
admin: {
components: {
edit: {
// highlight-start
beforeDocumentControls: ['/path/to/CustomComponent'],
// highlight-end
},
},
},
}
```
#### Globals
```
export const MyGlobal: GlobalConfig = {
admin: {
components: {
elements: {
// highlight-start
beforeDocumentControls: ['/path/to/CustomComponent'],
// highlight-end
},
},
},
}
```
Here's an example of a custom `beforeDocumentControls` component:
#### Server Component
```tsx
import React from 'react'
import type { BeforeDocumentControlsServerProps } from 'payload'
export function MyCustomDocumentControlButton(
props: BeforeDocumentControlsServerProps,
) {
return <div>This is a custom beforeDocumentControl button (Server)</div>
}
```
#### Client Component
```tsx
'use client'
import React from 'react'
import type { BeforeDocumentControlsClientProps } from 'payload'
export function MyCustomDocumentControlButton(
props: BeforeDocumentControlsClientProps,
) {
return <div>This is a custom beforeDocumentControl button (Client)</div>
}
```
### editMenuItems
The `editMenuItems` property allows you to inject custom components into the 3-dot menu dropdown located in the document controls bar. This dropdown contains default options including `Create New`, `Duplicate`, `Delete`, and other options when additional features are enabled. Any custom components you add will appear below these default items.
To add `editMenuItems`, use the `components.edit.editMenuItems` property in your [Collection Config](../configuration/collections):
#### Config Example
```ts
import type { CollectionConfig } from 'payload'
export const Pages: CollectionConfig = {
slug: 'pages',
admin: {
components: {
edit: {
// highlight-start
editMenuItems: ['/path/to/CustomEditMenuItem'],
// highlight-end
},
},
},
}
```
Here's an example of a custom `editMenuItems` component:
#### Server Component
```tsx
import React from 'react'
import { PopupList } from '@payloadcms/ui'
import type { EditMenuItemsServerProps } from 'payload'
export const EditMenuItems = async (props: EditMenuItemsServerProps) => {
const href = `/custom-action?id=${props.id}`
return (
<PopupList.ButtonGroup>
<PopupList.Button href={href}>Custom Edit Menu Item</PopupList.Button>
<PopupList.Button href={href}>
Another Custom Edit Menu Item - add as many as you need!
</PopupList.Button>
</PopupList.ButtonGroup>
)
}
```
#### Client Component
```tsx
'use client'
import React from 'react'
import { PopupList } from '@payloadcms/ui'
import type { EditViewMenuItemClientProps } from 'payload'
export const EditMenuItems = (props: EditViewMenuItemClientProps) => {
const handleClick = () => {
console.log('Custom button clicked!')
}
return (
<PopupList.ButtonGroup>
<PopupList.Button onClick={handleClick}>
Custom Edit Menu Item
</PopupList.Button>
<PopupList.Button onClick={handleClick}>
Another Custom Edit Menu Item - add as many as you need!
</PopupList.Button>
</PopupList.ButtonGroup>
)
}
```
<Banner type="info">
**Styling:** Use Payload's built-in `PopupList.Button` to ensure your menu
items automatically match the default dropdown styles. If you want a different
look, you can customize the appearance by passing your own `className` to
`PopupList.Button`, or use a completely custom button built with a standard
HTML `button` element or any other component that fits your design
preferences.
</Banner>
### SaveDraftButton
The `SaveDraftButton` property allows you to render a custom Save Draft Button in the Edit View.
To add a `SaveDraftButton` component, use the `components.edit.SaveDraftButton` property in your [Collection Config](../configuration/collections) or `components.elements.SaveDraftButton` in your [Global Config](../configuration/globals):
```ts
import type { CollectionConfig } from 'payload'
export const MyCollection: CollectionConfig = {
// ...
admin: {
components: {
edit: {
// highlight-start
SaveDraftButton: '/path/to/MySaveDraftButton',
// highlight-end
},
},
},
}
```
Here's an example of a custom `SaveDraftButton` component:
#### Server Component
```tsx
import React from 'react'
import { SaveDraftButton } from '@payloadcms/ui'
import type { SaveDraftButtonServerProps } from 'payload'
export function MySaveDraftButton(props: SaveDraftButtonServerProps) {
return <SaveDraftButton />
}
```
#### Client Component
```tsx
'use client'
import React from 'react'
import { SaveDraftButton } from '@payloadcms/ui'
import type { SaveDraftButtonClientProps } from 'payload'
export function MySaveDraftButton(props: SaveDraftButtonClientProps) {
return <SaveDraftButton />
}
```
### PublishButton
The `PublishButton` property allows you to render a custom Publish Button in the Edit View.
To add a `PublishButton` component, use the `components.edit.PublishButton` property in your [Collection Config](../configuration/collections) or `components.elements.PublishButton` in your [Global Config](../configuration/globals):
```ts
import type { CollectionConfig } from 'payload'
export const MyCollection: CollectionConfig = {
// ...
admin: {
components: {
edit: {
// highlight-start
PublishButton: '/path/to/MyPublishButton',
// highlight-end
},
},
},
}
```
Here's an example of a custom `PublishButton` component:
#### Server Component
```tsx
import React from 'react'
import { PublishButton } from '@payloadcms/ui'
import type { PublishButtonClientProps } from 'payload'
export function MyPublishButton(props: PublishButtonServerProps) {
return <PublishButton label="Publish" />
}
```
#### Client Component
```tsx
'use client'
import React from 'react'
import { PublishButton } from '@payloadcms/ui'
import type { PublishButtonClientProps } from 'payload'
export function MyPublishButton(props: PublishButtonClientProps) {
return <PublishButton label="Publish" />
}
```
### PreviewButton
The `PreviewButton` property allows you to render a custom Preview Button in the Edit View.
To add a `PreviewButton` component, use the `components.edit.PreviewButton` property in your [Collection Config](../configuration/collections) or `components.elements.PreviewButton` in your [Global Config](../configuration/globals):
```ts
import type { CollectionConfig } from 'payload'
export const MyCollection: CollectionConfig = {
// ...
admin: {
components: {
edit: {
// highlight-start
PreviewButton: '/path/to/MyPreviewButton',
// highlight-end
},
},
},
}
```
Here's an example of a custom `PreviewButton` component:
#### Server Component
```tsx
import React from 'react'
import { PreviewButton } from '@payloadcms/ui'
import type { PreviewButtonServerProps } from 'payload'
export function MyPreviewButton(props: PreviewButtonServerProps) {
return <PreviewButton />
}
```
#### Client Component
```tsx
'use client'
import React from 'react'
import { PreviewButton } from '@payloadcms/ui'
import type { PreviewButtonClientProps } from 'payload'
export function MyPreviewButton(props: PreviewButtonClientProps) {
return <PreviewButton />
}
```
### Description
The `Description` property allows you to render a custom description of the Collection or Global in the Edit View.
To add a `Description` component, use the `components.edit.Description` property in your [Collection Config](../configuration/collections) or `components.elements.Description` in your [Global Config](../configuration/globals):
```ts
import type { CollectionConfig } from 'payload'
export const MyCollection: CollectionConfig = {
// ...
admin: {
components: {
// highlight-start
Description: '/path/to/MyDescriptionComponent',
// highlight-end
},
},
}
```
<Banner type="warning">
**Note:** The `Description` component is shared between the Edit View and the
[List View](./list-view).
</Banner>
Here's an example of a custom `Description` component:
#### Server Component
```tsx
import React from 'react'
import type { ViewDescriptionServerProps } from 'payload'
export function MyDescriptionComponent(props: ViewDescriptionServerProps) {
return <div>This is a custom description component (Server)</div>
}
```
#### Client Component
```tsx
'use client'
import React from 'react'
import type { ViewDescriptionClientProps } from 'payload'
export function MyDescriptionComponent(props: ViewDescriptionClientProps) {
return <div>This is a custom description component (Client)</div>
}
```
### Upload
The `Upload` property allows you to render a custom file upload component in the Edit View.
To add an `Upload` component, use the `components.edit.Upload` property in your [Collection Config](../configuration/collections):
```ts
import type { CollectionConfig } from 'payload'
export const MyCollection: CollectionConfig = {
// ...
admin: {
components: {
edit: {
// highlight-start
Upload: '/path/to/MyUploadComponent',
// highlight-end
},
},
},
}
```
<Banner type="warning">
**Note:** The Upload component is only available for Collections.
</Banner>
Here's an example of a custom `Upload` component:
```tsx
import React from 'react'
export function MyUploadComponent() {
return <input type="file" />
}
```

View File

@@ -1,333 +0,0 @@
---
title: List View
label: List View
order: 70
desc:
keywords: admin, components, custom, documentation, Content Management System, cms, headless, javascript, node, react, nextjs
---
The List View is where users interact with a list of [Collection](../configuration/collections) Documents within the [Admin Panel](../admin/overview). This is where they can view, sort, filter, and paginate their documents to find exactly what they're looking for. This is also where users can perform bulk operations on multiple documents at once, such as deleting, editing, or publishing many.
The List View can be swapped out in its entirety for a Custom View, or it can be injected with a number of Custom Components to add additional functionality or presentational elements without replacing the entire view.
<Banner type="info">
**Note:** Only [Collections](../configuration/collections) have a List View.
[Globals](../configuration/globals) do not have a List View as they are single
documents.
</Banner>
## Custom List View
To swap out the entire List View with a [Custom View](./custom-views), use the `admin.components.views.list` property in your [Payload Config](../configuration/overview):
```tsx
import { buildConfig } from 'payload'
const config = buildConfig({
// ...
admin: {
components: {
views: {
// highlight-start
list: '/path/to/MyCustomListView',
// highlight-end
},
},
},
})
```
Here is an example of a custom List View:
#### Server Component
```tsx
import React from 'react'
import type { ListViewServerProps } from 'payload'
import { DefaultListView } from '@payloadcms/ui'
export function MyCustomServerListView(props: ListViewServerProps) {
return <div>This is a custom List View (Server)</div>
}
```
#### Client Component
```tsx
'use client'
import React from 'react'
import type { ListViewClientProps } from 'payload'
export function MyCustomClientListView(props: ListViewClientProps) {
return <div>This is a custom List View (Client)</div>
}
```
_For details on how to build Custom Views, including all available props, see [Building Custom Views](./custom-views#building-custom-views)._
## Custom Components
In addition to swapping out the entire List View with a [Custom View](./custom-views), you can also override individual components. This allows you to customize specific parts of the List View without swapping out the entire view for your own.
To override List View components for a Collection, use the `admin.components` property in your [Collection Config](../configuration/collections):
```ts
import type { CollectionConfig } from 'payload'
export const MyCollection: CollectionConfig = {
// ...
admin: {
// highlight-start
components: {
// ...
},
// highlight-end
},
}
```
The following options are available:
| Path | Description |
| ----------------- | ------------------------------------------------------------------------------------------------------------------------- |
| `beforeList` | An array of custom components to inject before the list of documents in the List View. [More details](#beforelist). |
| `beforeListTable` | An array of custom components to inject before the table of documents in the List View. [More details](#beforelisttable). |
| `afterList` | An array of custom components to inject after the list of documents in the List View. [More details](#afterlist). |
| `afterListTable` | An array of custom components to inject after the table of documents in the List View. [More details](#afterlisttable). |
| `listMenuItems` | An array of components to render within a menu next to the List Controls (after the Columns and Filters options) |
| `Description` | A component to render a description of the Collection. [More details](#description). |
### beforeList
The `beforeList` property allows you to inject custom components before the list of documents in the List View.
To add `beforeList` components, use the `components.beforeList` property in your [Collection Config](../configuration/collections):
```ts
import type { CollectionConfig } from 'payload'
export const MyCollection: CollectionConfig = {
// ...
admin: {
components: {
// highlight-start
beforeList: ['/path/to/MyBeforeListComponent'],
// highlight-end
},
},
}
```
Here's an example of a custom `beforeList` component:
#### Server Component
```tsx
import React from 'react'
import type { BeforeListServerProps } from 'payload'
export function MyBeforeListComponent(props: BeforeListServerProps) {
return <div>This is a custom beforeList component (Server)</div>
}
```
#### Client Component
```tsx
'use client'
import React from 'react'
import type { BeforeListClientProps } from 'payload'
export function MyBeforeListComponent(props: BeforeListClientProps) {
return <div>This is a custom beforeList component (Client)</div>
}
```
### beforeListTable
The `beforeListTable` property allows you to inject custom components before the table of documents in the List View.
To add `beforeListTable` components, use the `components.beforeListTable` property in your [Collection Config](../configuration/collections):
```ts
import type { CollectionConfig } from 'payload'
export const MyCollection: CollectionConfig = {
// ...
admin: {
components: {
// highlight-start
beforeListTable: ['/path/to/MyBeforeListTableComponent'],
// highlight-end
},
},
}
```
Here's an example of a custom `beforeListTable` component:
#### Server Component
```tsx
import React from 'react'
import type { BeforeListTableServerProps } from 'payload'
export function MyBeforeListTableComponent(props: BeforeListTableServerProps) {
return <div>This is a custom beforeListTable component (Server)</div>
}
```
#### Client Component
```tsx
'use client'
import React from 'react'
import type { BeforeListTableClientProps } from 'payload'
export function MyBeforeListTableComponent(props: BeforeListTableClientProps) {
return <div>This is a custom beforeListTable component (Client)</div>
}
```
### afterList
The `afterList` property allows you to inject custom components after the list of documents in the List View.
To add `afterList` components, use the `components.afterList` property in your [Collection Config](../configuration/collections):
```ts
import type { CollectionConfig } from 'payload'
export const MyCollection: CollectionConfig = {
// ...
admin: {
components: {
// highlight-start
afterList: ['/path/to/MyAfterListComponent'],
// highlight-end
},
},
}
```
Here's an example of a custom `afterList` component:
#### Server Component
```tsx
import React from 'react'
import type { AfterListServerProps } from 'payload'
export function MyAfterListComponent(props: AfterListServerProps) {
return <div>This is a custom afterList component (Server)</div>
}
```
#### Client Component
```tsx
'use client'
import React from 'react'
import type { AfterListClientProps } from 'payload'
export function MyAfterListComponent(props: AfterListClientProps) {
return <div>This is a custom afterList component (Client)</div>
}
```
### afterListTable
The `afterListTable` property allows you to inject custom components after the table of documents in the List View.
To add `afterListTable` components, use the `components.afterListTable` property in your [Collection Config](../configuration/collections):
```ts
import type { CollectionConfig } from 'payload'
export const MyCollection: CollectionConfig = {
// ...
admin: {
components: {
// highlight-start
afterListTable: ['/path/to/MyAfterListTableComponent'],
// highlight-end
},
},
}
```
Here's an example of a custom `afterListTable` component:
#### Server Component
```tsx
import React from 'react'
import type { AfterListTableServerProps } from 'payload'
export function MyAfterListTableComponent(props: AfterListTableServerProps) {
return <div>This is a custom afterListTable component (Server)</div>
}
```
#### Client Component
```tsx
'use client'
import React from 'react'
import type { AfterListTableClientProps } from 'payload'
export function MyAfterListTableComponent(props: AfterListTableClientProps) {
return <div>This is a custom afterListTable component (Client)</div>
}
```
### Description
The `Description` property allows you to render a custom description of the Collection in the List View.
To add a `Description` component, use the `components.Description` property in your [Collection Config](../configuration/collections):
```ts
import type { CollectionConfig } from 'payload'
export const MyCollection: CollectionConfig = {
// ...
admin: {
components: {
// highlight-start
Description: '/path/to/MyDescriptionComponent',
// highlight-end
},
},
}
```
<Banner type="warning">
**Note:** The `Description` component is shared between the List View and the
[Edit View](./edit-view).
</Banner>
Here's an example of a custom `Description` component:
#### Server Component
```tsx
import React from 'react'
import type { ViewDescriptionServerProps } from 'payload'
export function MyDescriptionComponent(props: ViewDescriptionServerProps) {
return <div>This is a custom Collection description component (Server)</div>
}
```
#### Client Component
```tsx
'use client'
import React from 'react'
import type { ViewDescriptionClientProps } from 'payload'
export function MyDescriptionComponent(props: ViewDescriptionClientProps) {
return <div>This is a custom Collection description component (Client)</div>
}
```

View File

@@ -1,555 +0,0 @@
---
title: Swap in your own React components
label: Overview
order: 10
desc: Fully customize your Admin Panel by swapping in your own React components. Add fields, remove views, update routes and change functions to sculpt your perfect Dashboard.
keywords: admin, components, custom, documentation, Content Management System, cms, headless, javascript, node, react, nextjs
---
The Payload [Admin Panel](../admin/overview) is designed to be as minimal and straightforward as possible to allow for easy customization and full control over the UI. In order for Payload to support this level of customization, Payload provides a pattern for you to supply your own React components through your [Payload Config](../configuration/overview).
All Custom Components in Payload are [React Server Components](https://react.dev/reference/rsc/server-components) by default. This enables the use of the [Local API](../local-api/overview) directly on the front-end. Custom Components are available for nearly every part of the Admin Panel for extreme granularity and control.
<Banner type="success">
**Note:** Client Components continue to be fully supported. To use Client
Components in your app, simply include the `'use client'` directive. Payload
will automatically detect and remove all
[non-serializable](https://react.dev/reference/rsc/use-client#serializable-types)
default props before rendering your component. [More
details](#client-components).
</Banner>
There are four main types of Custom Components in Payload:
- [Root Components](./root-components)
- [Collection Components](../configuration/collections#custom-components)
- [Global Components](../configuration/globals#custom-components)
- [Field Components](../fields/overview#custom-components)
To swap in your own Custom Component, first determine the scope that corresponds to what you are trying to accomplish, consult the list of available components, then [author your React component(s)](#building-custom-components) accordingly.
## Defining Custom Components
As Payload compiles the Admin Panel, it checks your config for Custom Components. When detected, Payload either replaces its own default component with yours, or if none exists by default, renders yours outright. While there are many places where Custom Components are supported in Payload, each is defined in the same way using [Component Paths](#component-paths).
To add a Custom Component, point to its file path in your Payload Config:
```ts
import { buildConfig } from 'payload'
const config = buildConfig({
// ...
admin: {
components: {
logout: {
Button: '/src/components/Logout#MyComponent', // highlight-line
},
},
},
})
```
<Banner type="success">
**Note:** All Custom Components can be either Server Components or Client
Components, depending on the presence of the `'use client'` directive at the
top of the file.
</Banner>
### Component Paths
In order to ensure the Payload Config is fully Node.js compatible and as lightweight as possible, components are not directly imported into your config. Instead, they are identified by their file path for the Admin Panel to resolve on its own.
Component Paths, by default, are relative to your project's base directory. This is either your current working directory, or the directory specified in `config.admin.importMap.baseDir`.
Components using named exports are identified either by appending `#` followed by the export name, or using the `exportName` property. If the component is the default export, this can be omitted.
```ts
import { buildConfig } from 'payload'
import { fileURLToPath } from 'node:url'
import path from 'path'
const filename = fileURLToPath(import.meta.url)
const dirname = path.dirname(filename)
const config = buildConfig({
// ...
admin: {
importMap: {
baseDir: path.resolve(dirname, 'src'), // highlight-line
},
components: {
logout: {
Button: '/components/Logout#MyComponent', // highlight-line
},
},
},
})
```
In this example, we set the base directory to the `src` directory, and omit the `/src/` part of our component path string.
### Component Config
While Custom Components are usually defined as a string, you can also pass in an object with additional options:
```ts
import { buildConfig } from 'payload'
const config = buildConfig({
// ...
admin: {
components: {
logout: {
// highlight-start
Button: {
path: '/src/components/Logout',
exportName: 'MyComponent',
},
// highlight-end
},
},
},
})
```
The following options are available:
| Property | Description |
| ------------- | ----------------------------------------------------------------------------------------------------------------------------- |
| `clientProps` | Props to be passed to the Custom Components if it's a Client Component. [More details](#custom-props). |
| `exportName` | Instead of declaring named exports using `#` in the component path, you can also omit them from `path` and pass them in here. |
| `path` | File path to the Custom Component. Named exports can be appended to the end of the path, separated by a `#`. |
| `serverProps` | Props to be passed to the Custom Component if it's a Server Component. [More details](#custom-props). |
For details on how to build Custom Components, see [Building Custom Components](#building-custom-components).
### Import Map
In order for Payload to make use of [Component Paths](#component-paths), an "Import Map" is automatically generated at either `src/app/(payload)/admin/importMap.js` or `app/(payload)/admin/importMap.js`. This file contains every Custom Component in your config, keyed to their respective paths. When Payload needs to lookup a component, it uses this file to find the correct import.
The Import Map is automatically regenerated at startup and whenever Hot Module Replacement (HMR) runs, or you can run `payload generate:importmap` to manually regenerate it.
#### Overriding Import Map Location
Using the `config.admin.importMap.importMapFile` property, you can override the location of the import map. This is useful if you want to place the import map in a different location, or if you want to use a custom file name.
```ts
import { buildConfig } from 'payload'
import { fileURLToPath } from 'node:url'
import path from 'path'
const filename = fileURLToPath(import.meta.url)
const dirname = path.dirname(filename)
const config = buildConfig({
// ...
admin: {
importMap: {
baseDir: path.resolve(dirname, 'src'),
importMapFile: path.resolve(
dirname,
'app',
'(payload)',
'custom-import-map.js',
), // highlight-line
},
},
})
```
#### Custom Imports
If needed, custom items can be appended onto the Import Map. This is mostly only relevant for plugin authors who need to add a custom import that is not referenced in a known location.
To add a custom import to the Import Map, use the `admin.dependencies` property in your [Payload Config](../configuration/overview):
```ts
import { buildConfig } from 'payload'
export default buildConfig({
// ...
admin: {
// ...
dependencies: {
myTestComponent: {
// myTestComponent is the key - can be anything
path: '/components/TestComponent.js#TestComponent',
type: 'component',
clientProps: {
test: 'hello',
},
},
},
},
})
```
## Building Custom Components
All Custom Components in Payload are [React Server Components](https://react.dev/reference/rsc/server-components) by default. This enables the use of the [Local API](../local-api/overview) directly on the front-end, among other things.
### Default Props
To make building Custom Components as easy as possible, Payload automatically provides common props, such as the [`payload`](../local-api/overview) class and the [`i18n`](../configuration/i18n) object. This means that when building Custom Components within the Admin Panel, you do not have to get these yourself.
Here is an example:
```tsx
import React from 'react'
import type { Payload } from 'payload'
async function MyServerComponent({
payload, // highlight-line
}: {
payload: Payload
}) {
const page = await payload.findByID({
collection: 'pages',
id: '123',
})
return <p>{page.title}</p>
}
```
Each Custom Component receives the following props by default:
| Prop | Description |
| --------- | ------------------------------------------- |
| `payload` | The [Payload](../local-api/overview) class. |
| `i18n` | The [i18n](../configuration/i18n) object. |
<Banner type="warning">
**Reminder:** All Custom Components also receive various other props that are
specific to the component being rendered. See [Root
Components](#root-components), [Collection
Components](../configuration/collections#custom-components), [Global
Components](../configuration/globals#custom-components), or [Field
Components](../fields/overview#custom-components) for a complete list of all
default props per component.
</Banner>
### Custom Props
It is also possible to pass custom props to your Custom Components. To do this, you can use either the `clientProps` or `serverProps` properties depending on whether your prop is [serializable](https://react.dev/reference/rsc/use-client#serializable-types), and whether your component is a Server or Client Component.
```ts
import { buildConfig } from 'payload'
const config = buildConfig({
// ...
admin: {
// highlight-line
components: {
logout: {
Button: {
path: '/src/components/Logout#MyComponent',
clientProps: {
myCustomProp: 'Hello, World!', // highlight-line
},
},
},
},
},
})
```
Here is how your component might receive this prop:
```tsx
import React from 'react'
import { Link } from '@payloadcms/ui'
export function MyComponent({ myCustomProp }: { myCustomProp: string }) {
return <Link href="/admin/logout">{myCustomProp}</Link>
}
```
### Client Components
All Custom Components in Payload are [React Server Components](https://react.dev/reference/rsc/server-components) by default, however, it is possible to use [Client Components](https://react.dev/reference/rsc/use-client) by simply adding the `'use client'` directive at the top of your file. Payload will automatically detect and remove all [non-serializable](https://react.dev/reference/rsc/use-client#serializable-types) default props before rendering your component.
```tsx
// highlight-start
'use client'
// highlight-end
import React, { useState } from 'react'
export function MyClientComponent() {
const [count, setCount] = useState(0)
return (
<button onClick={() => setCount(count + 1)}>Clicked {count} times</button>
)
}
```
<Banner type="warning">
**Reminder:** Client Components cannot be passed [non-serializable
props](https://react.dev/reference/rsc/use-client#serializable-types). If you
are rendering your Client Component _from within_ a Server Component, ensure
that its props are serializable.
</Banner>
### Accessing the Payload Config
From any Server Component, the [Payload Config](../configuration/overview) can be accessed directly from the `payload` prop:
```tsx
import React from 'react'
export default async function MyServerComponent({
payload: {
config, // highlight-line
},
}) {
return <Link href={config.serverURL}>Go Home</Link>
}
```
But, the Payload Config is [non-serializable](https://react.dev/reference/rsc/use-client#serializable-types) by design. It is full of custom validation functions and more. This means that the Payload Config, in its entirety, cannot be passed directly to Client Components.
For this reason, Payload creates a Client Config and passes it into the Config Provider. This is a serializable version of the Payload Config that can be accessed from any Client Component via the [`useConfig`](../admin/react-hooks#useconfig) hook:
```tsx
'use client'
import React from 'react'
import { useConfig } from '@payloadcms/ui'
export function MyClientComponent() {
// highlight-start
const {
config: { serverURL },
} = useConfig()
// highlight-end
return <Link href={serverURL}>Go Home</Link>
}
```
<Banner type="success">
See [Using Hooks](#using-hooks) for more details.
</Banner>
Similarly, all [Field Components](../fields/overview#custom-components) automatically receive their respective Field Config through props.
Within Server Components, this prop is named `field`:
```tsx
import React from 'react'
import type { TextFieldServerComponent } from 'payload'
export const MyClientFieldComponent: TextFieldServerComponent = ({
field: { name },
}) => {
return <p>{`This field's name is ${name}`}</p>
}
```
Within Client Components, this prop is named `clientField` because its non-serializable props have been removed:
```tsx
'use client'
import React from 'react'
import type { TextFieldClientComponent } from 'payload'
export const MyClientFieldComponent: TextFieldClientComponent = ({
clientField: { name },
}) => {
return <p>{`This field's name is ${name}`}</p>
}
```
### Getting the Current Language
All Custom Components can support language translations to be consistent with Payload's [I18n](../configuration/i18n). This will allow your Custom Components to display the correct language based on the user's preferences.
To do this, first add your translation resources to the [I18n Config](../configuration/i18n). Then from any Server Component, you can translate resources using the `getTranslation` function from `@payloadcms/translations`.
All Server Components automatically receive the `i18n` object as a prop by default:
```tsx
import React from 'react'
import { getTranslation } from '@payloadcms/translations'
export default async function MyServerComponent({ i18n }) {
const translatedTitle = getTranslation(myTranslation, i18n) // highlight-line
return <p>{translatedTitle}</p>
}
```
The best way to do this within a Client Component is to import the `useTranslation` hook from `@payloadcms/ui`:
```tsx
'use client'
import React from 'react'
import { useTranslation } from '@payloadcms/ui'
export function MyClientComponent() {
const { t, i18n } = useTranslation() // highlight-line
return (
<ul>
<li>{t('namespace1:key', { variable: 'value' })}</li>
<li>{t('namespace2:key', { variable: 'value' })}</li>
<li>{i18n.language}</li>
</ul>
)
}
```
<Banner type="success">
See the [Hooks](../admin/react-hooks) documentation for a full list of
available hooks.
</Banner>
### Getting the Current Locale
All [Custom Views](./custom-views) can support multiple locales to be consistent with Payload's [Localization](../configuration/localization) feature. This can be used to scope API requests, etc.
All Server Components automatically receive the `locale` object as a prop by default:
```tsx
import React from 'react'
export default async function MyServerComponent({ payload, locale }) {
const localizedPage = await payload.findByID({
collection: 'pages',
id: '123',
locale,
})
return <p>{localizedPage.title}</p>
}
```
The best way to do this within a Client Component is to import the `useLocale` hook from `@payloadcms/ui`:
```tsx
'use client'
import React from 'react'
import { useLocale } from '@payloadcms/ui'
function Greeting() {
const locale = useLocale() // highlight-line
const trans = {
en: 'Hello',
es: 'Hola',
}
return <span>{trans[locale.code]}</span>
}
```
<Banner type="success">
See the [Hooks](../admin/react-hooks) documentation for a full list of
available hooks.
</Banner>
### Using Hooks
To make it easier to [build your Custom Components](#building-custom-components), you can use [Payload's built-in React Hooks](../admin/react-hooks) in any Client Component. For example, you might want to interact with one of Payload's many React Contexts. To do this, you can use one of the many hooks available depending on your needs.
```tsx
'use client'
import React from 'react'
import { useDocumentInfo } from '@payloadcms/ui'
export function MyClientComponent() {
const { slug } = useDocumentInfo() // highlight-line
return <p>{`Entity slug: ${slug}`}</p>
}
```
<Banner type="success">
See the [Hooks](../admin/react-hooks) documentation for a full list of
available hooks.
</Banner>
### Adding Styles
Payload has a robust [CSS Library](../admin/customizing-css) that you can use to style your Custom Components to match to Payload's built-in styling. This will ensure that your Custom Components integrate well into the existing design system. This will make it so they automatically adapt to any theme changes that might occur.
To apply custom styles, simply import your own `.css` or `.scss` file into your Custom Component:
```tsx
import './index.scss'
export function MyComponent() {
return <div className="my-component">My Custom Component</div>
}
```
Then to colorize your Custom Component's background, for example, you can use the following CSS:
```scss
.my-component {
background-color: var(--theme-elevation-500);
}
```
Payload also exports its [SCSS](https://sass-lang.com) library for reuse which includes mixins, etc. To use this, simply import it as follows into your `.scss` file:
```scss
@import '~@payloadcms/ui/scss';
.my-component {
@include mid-break {
background-color: var(--theme-elevation-900);
}
}
```
<Banner type="success">
**Note:** You can also drill into Payload's own component styles, or easily
apply global, app-wide CSS. More on that [here](../admin/customizing-css).
</Banner>
## Performance
An often overlooked aspect of Custom Components is performance. If unchecked, Custom Components can lead to slow load times of the Admin Panel and ultimately a poor user experience.
This is different from front-end performance of your public-facing site.
<Banner type="success">
For more performance tips, see the [Performance
documentation](../performance/overview).
</Banner>
### Follow React and Next.js best practices
All Custom Components are built using [React](https://react.dev). For this reason, it is important to follow React best practices. This includes using memoization, streaming, caching, optimizing renders, using hooks appropriately, and more.
To learn more, see the [React documentation](https://react.dev/learn).
The Admin Panel itself is a [Next.js](https://nextjs.org) application. For this reason, it is _also_ important to follow Next.js best practices. This includes bundling, when to use layouts vs pages, where to place the server/client boundary, and more.
To learn more, see the [Next.js documentation](https://nextjs.org/docs).
### Reducing initial HTML size
With Server Components, be aware of what is being sent to through the server/client boundary. All props are serialized and sent through the network. This can lead to large HTML sizes and slow initial load times if too much data is being sent to the client.
To minimize this, you must be explicit about what props are sent to the client. Prefer server components and only send the necessary props to the client. This will also offset some of the JS execution to the server.
<Banner type="success">
**Tip:** Use [React Suspense](https://react.dev/reference/react/Suspense) to
progressively load components and improve perceived performance.
</Banner>
### Prevent unnecessary re-renders
If subscribing your component to form state, it may be re-rendering more often than necessary.
To do this, use the [`useFormFields`](../admin/react-hooks) hook instead of `useFields` when you only need to access specific fields.
```ts
'use client'
import { useFormFields } from '@payloadcms/ui'
const MyComponent: TextFieldClientComponent = ({ path }) => {
const value = useFormFields(([fields, dispatch]) => fields[path])
// ...
}
```

View File

@@ -1,439 +0,0 @@
---
title: Root Components
label: Root Components
order: 20
desc:
keywords: admin, components, custom, documentation, Content Management System, cms, headless, javascript, node, react, nextjs
---
Root Components are those that affect the [Admin Panel](../admin/overview) at a high-level, such as the logo or the main nav. You can swap out these components with your own [Custom Components](./overview) to create a completely custom look and feel.
When combined with [Custom CSS](../admin/customizing-css), you can create a truly unique experience for your users, such as white-labeling the Admin Panel to match your brand.
To override Root Components, use the `admin.components` property at the root of your [Payload Config](../configuration/overview):
```ts
import { buildConfig } from 'payload'
export default buildConfig({
// ...
admin: {
// highlight-start
components: {
// ...
},
// highlight-end
},
})
```
## Config Options
The following options are available:
| Path | Description |
| ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `actions` | An array of Custom Components to be rendered _within_ the header of the Admin Panel, providing additional interactivity and functionality. [More details](#actions). |
| `afterDashboard` | An array of Custom Components to inject into the built-in Dashboard, _after_ the default dashboard contents. [More details](#afterdashboard). |
| `afterLogin` | An array of Custom Components to inject into the built-in Login, _after_ the default login form. [More details](#afterlogin). |
| `afterNavLinks` | An array of Custom Components to inject into the built-in Nav, _after_ the links. [More details](#afternavlinks). |
| `beforeDashboard` | An array of Custom Components to inject into the built-in Dashboard, _before_ the default dashboard contents. [More details](#beforedashboard). |
| `beforeLogin` | An array of Custom Components to inject into the built-in Login, _before_ the default login form. [More details](#beforelogin). |
| `beforeNavLinks` | An array of Custom Components to inject into the built-in Nav, _before_ the links themselves. [More details](#beforenavlinks). |
| `graphics.Icon` | The simplified logo used in contexts like the `Nav` component. [More details](#graphicsicon). |
| `graphics.Logo` | The full logo used in contexts like the `Login` view. [More details](#graphicslogo). |
| `header` | An array of Custom Components to be injected above the Payload header. [More details](#header). |
| `logout.Button` | The button displayed in the sidebar that logs the user out. [More details](#logoutbutton). |
| `Nav` | Contains the sidebar / mobile menu in its entirety. [More details](#nav). |
| `providers` | Custom [React Context](https://react.dev/learn/scaling-up-with-reducer-and-context) providers that will wrap the entire Admin Panel. [More details](./custom-providers). |
| `views` | Override or create new views within the Admin Panel. [More details](./custom-views). |
_For details on how to build Custom Components, see [Building Custom Components](./overview#building-custom-components)._
<Banner type="success">
**Note:** You can also use set [Collection
Components](../configuration/collections#custom-components) and [Global
Components](../configuration/globals#custom-components) in their respective
configs.
</Banner>
## Components
### actions
Actions are rendered within the header of the Admin Panel. Actions are typically used to display buttons that add additional interactivity and functionality, although they can be anything you'd like.
To add an action, use the `actions` property in your `admin.components` config:
```ts
import { buildConfig } from 'payload'
export default buildConfig({
// ...
admin: {
// highlight-start
components: {
actions: ['/path/to/your/component'],
},
// highlight-end
},
})
```
Here is an example of a simple Action component:
```tsx
export default function MyCustomAction() {
return (
<button onClick={() => alert('Hello, world!')}>
This is a custom action component
</button>
)
}
```
<Banner type="success">
**Note:** You can also use add Actions to the [Edit View](./edit-view) and
[List View](./list-view) in their respective configs.
</Banner>
### beforeDashboard
The `beforeDashboard` property allows you to inject Custom Components into the built-in Dashboard, before the default dashboard contents.
To add `beforeDashboard` components, use the `admin.components.beforeDashboard` property in your Payload Config:
```ts
import { buildConfig } from 'payload'
export default buildConfig({
// ...
admin: {
// highlight-start
components: {
beforeDashboard: ['/path/to/your/component'],
},
// highlight-end
},
})
```
Here is an example of a simple `beforeDashboard` component:
```tsx
export default function MyBeforeDashboardComponent() {
return <div>This is a custom component injected before the Dashboard.</div>
}
```
### afterDashboard
Similar to `beforeDashboard`, the `afterDashboard` property allows you to inject Custom Components into the built-in Dashboard, _after_ the default dashboard contents.
To add `afterDashboard` components, use the `admin.components.afterDashboard` property in your Payload Config:
```ts
import { buildConfig } from 'payload'
export default buildConfig({
// ...
admin: {
// highlight-start
components: {
afterDashboard: ['/path/to/your/component'],
},
// highlight-end
},
})
```
Here is an example of a simple `afterDashboard` component:
```tsx
export default function MyAfterDashboardComponent() {
return <div>This is a custom component injected after the Dashboard.</div>
}
```
### beforeLogin
The `beforeLogin` property allows you to inject Custom Components into the built-in Login view, _before_ the default login form.
To add `beforeLogin` components, use the `admin.components.beforeLogin` property in your Payload Config:
```ts
import { buildConfig } from 'payload'
export default buildConfig({
// ...
admin: {
// highlight-start
components: {
beforeLogin: ['/path/to/your/component'],
},
// highlight-end
},
})
```
Here is an example of a simple `beforeLogin` component:
```tsx
export default function MyBeforeLoginComponent() {
return <div>This is a custom component injected before the Login form.</div>
}
```
### afterLogin
Similar to `beforeLogin`, the `afterLogin` property allows you to inject Custom Components into the built-in Login view, _after_ the default login form.
To add `afterLogin` components, use the `admin.components.afterLogin` property in your Payload Config:
```ts
import { buildConfig } from 'payload'
export default buildConfig({
// ...
admin: {
// highlight-start
components: {
afterLogin: ['/path/to/your/component'],
},
// highlight-end
},
})
```
Here is an example of a simple `afterLogin` component:
```tsx
export default function MyAfterLoginComponent() {
return <div>This is a custom component injected after the Login form.</div>
}
```
### beforeNavLinks
The `beforeNavLinks` property allows you to inject Custom Components into the built-in [Nav Component](#nav), _before_ the nav links themselves.
To add `beforeNavLinks` components, use the `admin.components.beforeNavLinks` property in your Payload Config:
```ts
import { buildConfig } from 'payload'
export default buildConfig({
// ...
admin: {
// highlight-start
components: {
beforeNavLinks: ['/path/to/your/component'],
},
// highlight-end
},
})
```
Here is an example of a simple `beforeNavLinks` component:
```tsx
export default function MyBeforeNavLinksComponent() {
return <div>This is a custom component injected before the Nav links.</div>
}
```
### afterNavLinks
Similar to `beforeNavLinks`, the `afterNavLinks` property allows you to inject Custom Components into the built-in Nav, _after_ the nav links.
To add `afterNavLinks` components, use the `admin.components.afterNavLinks` property in your Payload Config:
```ts
import { buildConfig } from 'payload'
export default buildConfig({
// ...
admin: {
// highlight-start
components: {
afterNavLinks: ['/path/to/your/component'],
},
// highlight-end
},
})
```
Here is an example of a simple `afterNavLinks` component:
```tsx
export default function MyAfterNavLinksComponent() {
return <p>This is a custom component injected after the Nav links.</p>
}
```
### Nav
The `Nav` property contains the sidebar / mobile menu in its entirety. Use this property to completely replace the built-in Nav with your own custom navigation.
To add a custom nav, use the `admin.components.Nav` property in your Payload Config:
```ts
import { buildConfig } from 'payload'
export default buildConfig({
// ...
admin: {
// highlight-start
components: {
Nav: '/path/to/your/component',
},
// highlight-end
},
})
```
Here is an example of a simple `Nav` component:
```tsx
import { Link } from '@payloadcms/ui'
export default function MyCustomNav() {
return (
<nav>
<ul>
<li>
<Link href="/dashboard">Dashboard</Link>
</li>
</ul>
</nav>
)
}
```
### graphics.Icon
The `Icon` property is the simplified logo used in contexts like the `Nav` component. This is typically a small, square icon that represents your brand.
To add a custom icon, use the `admin.components.graphics.Icon` property in your Payload Config:
```ts
import { buildConfig } from 'payload'
export default buildConfig({
// ...
admin: {
// highlight-start
components: {
graphics: {
Icon: '/path/to/your/component',
},
},
// highlight-end
},
})
```
Here is an example of a simple `Icon` component:
```tsx
export default function MyCustomIcon() {
return <img src="/path/to/your/icon.png" alt="My Custom Icon" />
}
```
### graphics.Logo
The `Logo` property is the full logo used in contexts like the `Login` view. This is typically a larger, more detailed representation of your brand.
To add a custom logo, use the `admin.components.graphics.Logo` property in your Payload Config:
```ts
import { buildConfig } from 'payload'
export default buildConfig({
// ...
admin: {
// highlight-start
components: {
graphics: {
Logo: '/path/to/your/component',
},
},
// highlight-end
},
})
```
Here is an example of a simple `Logo` component:
```tsx
export default function MyCustomLogo() {
return <img src="/path/to/your/logo.png" alt="My Custom Logo" />
}
```
### header
The `header` property allows you to inject Custom Components above the Payload header.
Examples of a custom header components might include an announcements banner, a notifications bar, or anything else you'd like to display at the top of the Admin Panel in a prominent location.
To add `header` components, use the `admin.components.header` property in your Payload Config:
```ts
import { buildConfig } from 'payload'
export default buildConfig({
// ...
admin: {
// highlight-start
components: {
header: ['/path/to/your/component'],
},
// highlight-end
},
})
```
Here is an example of a simple `header` component:
```tsx
export default function MyCustomHeader() {
return (
<header>
<h1>My Custom Header</h1>
</header>
)
}
```
### logout.Button
The `logout.Button` property is the button displayed in the sidebar that should log the user out when clicked.
To add a custom logout button, use the `admin.components.logout.Button` property in your Payload Config:
```ts
import { buildConfig } from 'payload'
export default buildConfig({
// ...
admin: {
// highlight-start
components: {
logout: {
Button: '/path/to/your/component',
},
},
// highlight-end
},
})
```
Here is an example of a simple `logout.Button` component:
```tsx
export default function MyCustomLogoutButton() {
return <button onClick={() => alert('Logging out!')}>Log Out</button>
}
```

View File

@@ -1,65 +0,0 @@
---
title: Indexes
label: Indexes
order: 40
keywords: database, indexes
desc: Index fields to produce faster queries.
---
Database indexes are a way to optimize the performance of your database by allowing it to quickly locate and retrieve data. If you have a field that you frequently query or sort by, adding an index to that field can significantly improve the speed of those operations.
When your query runs, the database will not scan the entire document to find that one field, but will instead use the index to quickly locate the data.
To index a field, set the `index` option to `true` in your field's config:
```ts
import type { CollectionConfig } from 'payload'
export MyCollection: CollectionConfig = {
// ...
fields: [
// ...
{
name: 'title',
type: 'text',
// highlight-start
index: true,
// highlight-end
},
]
}
```
<Banner type="info">
**Note:** The `id`, `createdAt`, and `updatedAt` fields are indexed by
default.
</Banner>
<Banner type="success">
**Tip:** If you're using MongoDB, you can use [MongoDB
Compass](https://www.mongodb.com/products/compass) to visualize and manage
your indexes.
</Banner>
## Compound Indexes
In addition to indexing single fields, you can also create compound indexes that index multiple fields together. This can be useful for optimizing queries that filter or sort by multiple fields.
To create a compound index, use the `indexes` option in your [Collection Config](../configuration/collections):
```ts
import type { CollectionConfig } from 'payload'
export const MyCollection: CollectionConfig = {
// ...
fields: [
// ...
],
indexes: [
{
fields: ['title', 'createdAt'],
unique: true, // Optional, if you want the combination of fields to be unique
},
],
}
```

View File

@@ -1,312 +0,0 @@
---
title: Migrations
label: Migrations
order: 20
keywords: database, migrations, ddl, sql, mongodb, postgres, documentation, Content Management System, cms, headless, typescript, node, react, nextjs
desc: Payload features first-party database migrations all done in TypeScript.
---
Payload exposes a full suite of migration controls available for your use. Migration commands are accessible via
the `npm run payload` command in your project directory.
Ensure you have an npm script called "payload" in your `package.json` file.
```json
{
"scripts": {
"payload": "cross-env PAYLOAD_CONFIG_PATH=src/payload.config.ts payload"
}
}
```
<Banner>
Note that you need to run Payload migrations through the package manager that
you are using, because Payload should not be globally installed on your
system.
</Banner>
## Migration file contents
Payload stores all created migrations in a folder that you can specify. By default, migrations are stored
in `./src/migrations`.
A migration file has two exports - an `up` function, which is called when a migration is executed, and a `down` function
that will be called if for some reason the migration fails to complete successfully. The `up` function should contain
all changes that you attempt to make within the migration, and the `down` should ideally revert any changes you make.
Here is an example migration file:
```ts
import { MigrateUpArgs, MigrateDownArgs } from '@payloadcms/your-db-adapter'
export async function up({ payload, req }: MigrateUpArgs): Promise<void> {
// Perform changes to your database here.
// You have access to `payload` as an argument, and
// everything is done in TypeScript.
}
export async function down({ payload, req }: MigrateDownArgs): Promise<void> {
// Do whatever you need to revert changes if the `up` function fails
}
```
## Using Transactions
When migrations are run, each migration is performed in a new [transaction](/docs/database/transactions) for you. All
you need to do is pass the `req` object to any [Local API](/docs/local-api/overview) or direct database calls, such as
`payload.db.updateMany()`, to make database changes inside the transaction. Assuming no errors were thrown, the transaction is committed
after your `up` or `down` function runs. If the migration errors at any point or fails to commit, it is caught and the
transaction gets aborted. This way no change is made to the database if the migration fails.
### Using database directly with the transaction
Additionally, you can bypass Payload's layer entirely and perform operations directly on your underlying database within the active transaction:
### MongoDB:
```ts
import { type MigrateUpArgs } from '@payloadcms/db-mongodb'
export async function up({
session,
payload,
req,
}: MigrateUpArgs): Promise<void> {
const posts = await payload.db.collections.posts.collection
.find({ session })
.toArray()
}
```
### Postgres:
```ts
import { type MigrateUpArgs, sql } from '@payloadcms/db-postgres'
export async function up({ db, payload, req }: MigrateUpArgs): Promise<void> {
const { rows: posts } = await db.execute(sql`SELECT * from posts`)
}
```
### SQLite:
In SQLite, transactions are disabled by default. [More](./transactions).
```ts
import { type MigrateUpArgs, sql } from '@payloadcms/db-sqlite'
export async function up({ db, payload, req }: MigrateUpArgs): Promise<void> {
const { rows: posts } = await db.run(sql`SELECT * from posts`)
}
```
## Migrations Directory
Each DB adapter has an optional property `migrationDir` where you can override where you want your migrations to be
stored/read. If this is not specified, Payload will check the default and possibly make a best effort to find your
migrations directory by searching in common locations ie. `./src/migrations`, `./dist/migrations`, `./migrations`, etc.
All database adapters should implement similar migration patterns, but there will be small differences based on the
adapter and its specific needs. Below is a list of all migration commands that should be supported by your database
adapter.
## Commands
### Migrate
The `migrate` command will run any migrations that have not yet been run.
```text
npm run payload migrate
```
### Create
Create a new migration file in the migrations directory. You can optionally name the migration that will be created. By
default, migrations will be named using a timestamp.
```text
npm run payload migrate:create optional-name-here
```
Flags:
- `--skip-empty`: with Postgres, it skips the "no schema changes detected. Would you like to create a blank migration file?" prompt which can be useful for generating migration in CI.
- `--force-accept-warning`: accepts any command prompts, creates a blank migration even if there weren't any changes to the schema.
### Status
The `migrate:status` command will check the status of migrations and output a table of which migrations have been run,
and which migrations have not yet run.
`payload migrate:status`
```text
npm run payload migrate:status
```
### Down
Roll back the last batch of migrations.
```text
npm run payload migrate:down
```
### Refresh
Roll back all migrations that have been run, and run them again.
```text
npm run payload migrate:refresh
```
### Reset
Roll back all migrations.
```text
npm run payload migrate:reset
```
### Fresh
Drops all entities from the database and re-runs all migrations from scratch.
```text
npm run payload migrate:fresh
```
## When to run migrations
Depending on which Database Adapter you use, your migration workflow might differ subtly.
In relational databases, migrations will be **required** for non-development database environments. But with MongoDB, you might only need to run migrations once in a while (or never even need them).
#### MongoDB#mongodb-migrations
In MongoDB, you'll only ever really need to run migrations for times where you change your database shape, and you have lots of existing data that you'd like to transform from Shape A to Shape B.
In this case, you can create a migration by running `pnpm payload migrate:create`, and then write the logic that you need to perform to migrate your documents to their new shape. You can then either run your migrations in CI before you build / deploy, or you can run them locally, against your production database, by using your production database connection string on your local computer and running the `pnpm payload migrate` command.
#### Postgres#postgres-migrations
In relational databases like Postgres, migrations are a bit more important, because each time you add a new field or a new collection, you'll need to update the shape of your database to match your Payload Config (otherwise you'll see errors upon trying to read / write your data).
That means that Postgres users of Payload should become familiar with the entire migration workflow from top to bottom.
Here is an overview of a common workflow for working locally against a development database, creating migrations, and then running migrations against your production database before deploying.
**1 - work locally using push mode**
Payload uses Drizzle ORM's powerful `push` mode to automatically sync data changes to your database for you while in development mode. By default, this is enabled and is the suggested workflow to using Postgres and Payload while doing local development.
You can disable this setting and solely use migrations to manage your local development database (pass `push: false` to your Postgres adapter), but if you do disable it, you may see frequent errors while running development mode. This is because Payload will have updated to your new data shape, but your local database will not have updated.
For this reason, we suggest that you leave `push` as its default setting and treat your local dev database as a sandbox.
For more information about push mode and prototyping in development, [click here](./postgres#prototyping-in-development-mode).
The typical workflow in Payload is to build out your Payload configs, install plugins, and make progress in development mode - allowing Drizzle to push your changes to your local database for you. Once you're finished, you can create a migration.
But importantly, you do not need to run migrations against your development database, because Drizzle will have already pushed your changes to your database for you.
<Banner type="warning">
Warning: do not mix "push" and migrations with your local development
database. If you use "push" locally, and then try to migrate, Payload will
throw a warning, telling you that these two methods are not meant to be used
interchangeably.
</Banner>
**2 - create a migration**
Once you're done with working in your Payload Config, you can create a migration. It's best practice to try and complete a specific task or fully build out a feature before you create a migration.
But once you're ready, you can run `pnpm payload migrate:create`, which will perform the following steps for you:
- We will look for any existing migrations, and automatically generate SQL changes necessary to convert your schema from its prior state to the new state of your Payload Config
- We will then create a new migration file in your `/migrations` folder that contains all the SQL necessary to be run
We won't immediately run this migration for you, however.
<Banner type="success">
Tip: migrations created by Payload are relatively programmatic in nature, so
there should not be any surprises, but before you check in the created
migration it's a good idea to always double-check the contents of the
migration files.
</Banner>
**3 - set up your build process to run migrations**
Generally, you want to run migrations before you build Payload for production. This typically happens in your CI pipeline and can usually be configured on platforms like Payload Cloud, Vercel, or Netlify by specifying your build script.
A common set of scripts in a `package.json`, set up to run migrations in CI, might look like this:
```js
"scripts": {
// For running in dev mode
"dev": "next dev --turbo",
// To build your Next + Payload app for production
"build": "next build",
// A "tie-in" to Payload's CLI for convenience
// this helps you run `pnpm payload migrate:create` and similar
"payload": "cross-env NODE_OPTIONS=--no-deprecation payload",
// This command is what you'd set your `build script` to.
// Notice how it runs `payload migrate` and then `pnpm build`?
// This will run all migrations for you before building, in your CI,
// against your production database
"ci": "payload migrate && pnpm build",
},
```
In the example above, we've specified a `ci` script which we can use as our "build script" in the platform that we are deploying to production with.
This will require that your build pipeline can connect to your database, and it will simply run the `payload migrate` command prior to starting the build process. By calling `payload migrate`, Payload will automatically execute any migrations in your `/migrations` folder that have not yet been executed against your production database, in the order that they were created.
If it fails, the deployment will be rejected. But now, with your build script set up to run your migrations, you will be all set! Next time you deploy, your CI will execute the required migrations for you, and your database will be caught up with the shape that your Payload Config requires.
## Running migrations in production
In certain cases, you might want to run migrations at runtime when the server starts. Running them during build time may be impossible due to not having access to your database connection while building or similar reasoning.
If you're using a long-running server or container where your Node server starts up one time and then stays initialized, you might prefer to run migrations on server startup instead of within your CI.
In order to run migrations at runtime, on initialization, you can pass your migrations to your database adapter under the `prodMigrations` key as follows:
```ts
// Import your migrations from the `index.ts` file
// that Payload generates for you
import { migrations } from './migrations'
import { buildConfig } from 'payload'
export default buildConfig({
// your config here
db: postgresAdapter({
// your adapter config here
prodMigrations: migrations,
}),
})
```
Passing your migrations as shown above will tell Payload, in production only, to execute any migrations that need to be run prior to completing the initialization of Payload. This is ideal for long-running services where Payload will only be initialized at startup.
<Banner type="warning">
**Warning:** if Payload is instructed to run migrations in production, this
may slow down serverless cold starts on platforms such as Vercel. Generally,
this option should only be used for long-running servers / containers.
</Banner>
## Environment-Specific Configurations and Migrations
Your configuration may include environment-specific settings (e.g., enabling a plugin only in production). If you generate migrations without considering the environment, it can lead to discrepancies and issues. When running migrations locally, Payload uses the development environment, which might miss production-specific configurations. Similarly, running migrations in production could miss development-specific entities.
This is an easy oversight, so be mindful of any environment-specific logic in your config when handling migrations.
**Ways to address this:**
- Manually update your migration file after it is generated to include any environment-specific configurations.
- Temporarily enable any required production environment variables in your local setup when generating the migration to capture the necessary updates.
- Use separate migration files for each environment to ensure the correct migration is executed in the corresponding environment.

View File

@@ -1,64 +0,0 @@
---
title: MongoDB
label: MongoDB
order: 50
desc: Payload has supported MongoDB natively since we started. The flexible nature of MongoDB lends itself well to Payload's powerful fields.
keywords: MongoDB, documentation, typescript, Content Management System, cms, headless, javascript, node, react, nextjs
---
To use Payload with MongoDB, install the package `@payloadcms/db-mongodb`. It will come with everything you need to
store your Payload data in MongoDB.
Then from there, pass it to your Payload Config as follows:
```ts
import { mongooseAdapter } from '@payloadcms/db-mongodb'
export default buildConfig({
// Your config goes here
collections: [
// Collections go here
],
// Configure the Mongoose adapter here
db: mongooseAdapter({
// Mongoose-specific arguments go here.
// URL is required.
url: process.env.DATABASE_URI,
}),
})
```
## Options
| Option | Description |
| -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `autoPluralization` | Tell Mongoose to auto-pluralize any collection names if it encounters any singular words used as collection `slug`s. |
| `connectOptions` | Customize MongoDB connection options. Payload will connect to your MongoDB database using default options which you can override and extend to include all the [options](https://mongoosejs.com/docs/connections.html#options) available to mongoose. |
| `collectionsSchemaOptions` | Customize Mongoose schema options for collections. |
| `disableIndexHints` | Set to true to disable hinting to MongoDB to use 'id' as index. This is currently done when counting documents for pagination, as it increases the speed of the count function used in that query. Disabling this optimization might fix some problems with AWS DocumentDB. Defaults to false |
| `migrationDir` | Customize the directory that migrations are stored. |
| `transactionOptions` | An object with configuration properties used in [transactions](https://www.mongodb.com/docs/manual/core/transactions/) or `false` which will disable the use of transactions. |
| `collation` | Enable language-specific string comparison with customizable options. Available on MongoDB 3.4+. Defaults locale to "en". Example: `{ strength: 3 }`. For a full list of collation options and their definitions, see the [MongoDB documentation](https://www.mongodb.com/docs/manual/reference/collation/). |
| `allowAdditionalKeys` | By default, Payload strips all additional keys from MongoDB data that don't exist in the Payload schema. If you have some data that you want to include to the result but it doesn't exist in Payload, you can set this to `true`. Be careful as Payload access control _won't_ work for this data. |
| `allowIDOnCreate` | Set to `true` to use the `id` passed in data on the create API operations without using a custom ID field. |
| `disableFallbackSort` | Set to `true` to disable the adapter adding a fallback sort when sorting by non-unique fields, this can affect performance in some cases but it ensures a consistent order of results. |
## Access to Mongoose models
After Payload is initialized, this adapter exposes all of your Mongoose models and they are available for you to work
with directly.
You can access Mongoose models as follows:
- Collection models - `payload.db.collections[myCollectionSlug]`
- Globals model - `payload.db.globals`
- Versions model (both collections and globals) - `payload.db.versions[myEntitySlug]`
## Using other MongoDB implementations
Limitations with [DocumentDB](https://aws.amazon.com/documentdb/) and [Azure Cosmos DB](https://azure.microsoft.com/en-us/products/cosmos-db):
- For Azure Cosmos DB you must pass `transactionOptions: false` to the adapter options. Azure Cosmos DB does not support transactions that update two and more documents in different collections, which is a common case when using Payload (via hooks).
- For Azure Cosmos DB the root config property `indexSortableFields` must be set to `true`.
- The [Join Field](../fields/join) is not supported in DocumentDB and Azure Cosmos DB, as we internally use MongoDB aggregations to query data for that field, which are limited there. This can be changed in the future.
- For DocumentDB pass `disableIndexHints: true` to disable hinting to the DB to use `id` as index which can cause problems with DocumentDB.

View File

@@ -1,74 +0,0 @@
---
title: Database
label: Overview
order: 10
keywords: database, mongodb, postgres, documentation, Content Management System, cms, headless, typescript, node, react, nextjs
desc: With Payload, you bring your own database and own your data. You have full control.
---
Payload is database agnostic, meaning you can use any type of database behind Payload's familiar APIs. Payload is designed to interact with your database through a Database Adapter, which is a thin layer that translates Payload's internal data structures into your database's native data structures.
Currently, Payload officially supports the following Database Adapters:
- [MongoDB](/docs/database/mongodb) with [Mongoose](https://mongoosejs.com/)
- [Postgres](/docs/database/postgres) with [Drizzle](https://drizzle.team/)
- [SQLite](/docs/database/sqlite) with [Drizzle](https://drizzle.team/)
To configure a Database Adapter, use the `db` property in your [Payload Config](../configuration/overview):
```ts
import { buildConfig } from 'payload'
import { mongooseAdapter } from '@payloadcms/db-mongodb'
export default buildConfig({
// ...
// highlight-start
db: mongooseAdapter({
url: process.env.DATABASE_URI,
}),
// highlight-end
})
```
<Banner type="warning">
**Reminder:** The Database Adapter is an external dependency and must be
installed in your project separately from Payload. You can find the
installation instructions for each Database Adapter in their respective
documentation.
</Banner>
## Selecting a Database
There are several factors to consider when choosing which database technology and hosting option is right for your project and workload. Payload can theoretically support any database, but it's up to you to decide which database to use.
There are two main categories of databases to choose from:
- [Non-Relational Databases](#non-relational-databases)
- [Relational Databases](#relational-databases)
### Non-Relational Databases
If your project has a lot of dynamic fields, and you are comfortable with allowing Payload to enforce data integrity across your documents, MongoDB is a great choice. With it, your Payload documents are stored as _one_ document in your database—no matter if you have localization enabled, how many block or array fields you have, etc. This means that the shape of your data in your database will very closely reflect your field schema, and there is minimal complexity involved in storing or retrieving your data.
You should prefer MongoDB if:
- You prefer simplicity within your database
- You don't want to deal with keeping production / staging databases in sync via [DDL changes](https://en.wikipedia.org/wiki/Data_definition_language)
- Most (or everything) in your project is [Localized](../configuration/localization)
- You leverage a lot of [Arrays](../fields/array), [Blocks](../fields/blocks), or `hasMany` [Select](../fields/select) fields
### Relational Databases
Many projects might call for more rigid database architecture where the shape of your data is strongly enforced at the database level. For example, if you know the shape of your data and it's relatively "flat", and you don't anticipate it to change often, your workload might suit relational databases like Postgres very well.
You should prefer a relational DB like Postgres or SQLite if:
- You are comfortable with [Migrations](./migrations)
- You require enforced data consistency at the database level
- You have a lot of relationships between collections and require relationships to be enforced
## Payload Differences
It's important to note that nearly every Payload feature is available in all of our officially supported Database Adapters, including [Localization](../configuration/localization), [Arrays](../fields/array), [Blocks](../fields/blocks), etc. The only thing that is not supported in SQLite yet is the [Point Field](/docs/fields/point), but that should be added soon.
It's up to you to choose which database you would like to use based on the requirements of your project. Payload has no opinion on which database you should ultimately choose.

View File

@@ -1,316 +0,0 @@
---
title: Postgres
label: Postgres
order: 60
desc: Payload supports Postgres through an officially supported Drizzle Database Adapter.
keywords: Postgres, documentation, typescript, Content Management System, cms, headless, javascript, node, react, nextjs
---
To use Payload with Postgres, install the package `@payloadcms/db-postgres`. It leverages Drizzle ORM and `node-postgres` to interact with a Postgres database that you provide.
Alternatively, the `@payloadcms/db-vercel-postgres` package is also available and is optimized for use with Vercel.
It automatically manages changes to your database for you in development mode, and exposes a full suite of migration controls for you to leverage in order to keep other database environments in sync with your schema. DDL transformations are automatically generated.
To configure Payload to use Postgres, pass the `postgresAdapter` to your Payload Config as follows:
### Usage
`@payloadcms/db-postgres`:
```ts
import { postgresAdapter } from '@payloadcms/db-postgres'
export default buildConfig({
// Configure the Postgres adapter here
db: postgresAdapter({
// Postgres-specific arguments go here.
// `pool` is required.
pool: {
connectionString: process.env.DATABASE_URI,
},
}),
})
```
`@payloadcms/db-vercel-postgres`:
```ts
import { vercelPostgresAdapter } from '@payloadcms/db-vercel-postgres'
export default buildConfig({
// Automatically uses process.env.POSTGRES_URL if no options are provided.
db: vercelPostgresAdapter(),
// Optionally, can accept the same options as the @vercel/postgres package.
db: vercelPostgresAdapter({
pool: {
connectionString: process.env.DATABASE_URL,
},
}),
})
```
<Banner type="info">
**Note:** If you're using `vercelPostgresAdapter` your
`process.env.POSTGRES_URL` or `pool.connectionString` points to a local
database (e.g hostname has `localhost` or `127.0.0.1`) we use the `pg` module
for pooling instead of `@vercel/postgres`. This is because `@vercel/postgres`
doesn't work with local databases, if you want to disable that behavior, you
can pass `forceUseVercelPostgres: true` to the adapter's args and follow
[Vercel
guide](https://vercel.com/docs/storage/vercel-postgres/local-development#option-2:-local-postgres-instance-with-docker)
for a Docker Neon DB setup.
</Banner>
## Options
| Option | Description |
| --------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `pool` \* | [Pool connection options](https://orm.drizzle.team/docs/quick-postgresql/node-postgres) that will be passed to Drizzle and `node-postgres` or to `@vercel/postgres` |
| `push` | Disable Drizzle's [`db push`](https://orm.drizzle.team/kit-docs/overview#prototyping-with-db-push) in development mode. By default, `push` is enabled for development mode only. |
| `migrationDir` | Customize the directory that migrations are stored. |
| `schemaName` (experimental) | A string for the postgres schema to use, defaults to 'public'. |
| `idType` | A string of 'serial', or 'uuid' that is used for the data type given to id columns. |
| `transactionOptions` | A PgTransactionConfig object for transactions, or set to `false` to disable using transactions. [More details](https://orm.drizzle.team/docs/transactions) |
| `disableCreateDatabase` | Pass `true` to disable auto database creation if it doesn't exist. Defaults to `false`. |
| `localesSuffix` | A string appended to the end of table names for storing localized fields. Default is '\_locales'. |
| `relationshipsSuffix` | A string appended to the end of table names for storing relationships. Default is '\_rels'. |
| `versionsSuffix` | A string appended to the end of table names for storing versions. Defaults to '\_v'. |
| `beforeSchemaInit` | Drizzle schema hook. Runs before the schema is built. [More Details](#beforeschemainit) |
| `afterSchemaInit` | Drizzle schema hook. Runs after the schema is built. [More Details](#afterschemainit) |
| `generateSchemaOutputFile` | Override generated schema from `payload generate:db-schema` file path. Defaults to `{CWD}/src/payload-generated.schema.ts` |
| `allowIDOnCreate` | Set to `true` to use the `id` passed in data on the create API operations without using a custom ID field. |
| `readReplicas` | An array of DB read replicas connection strings, can be used to offload read-heavy traffic. |
| `blocksAsJSON` | Store blocks as a JSON column instead of using the relational structure which can improve performance with a large amount of blocks |
## Access to Drizzle
After Payload is initialized, this adapter will expose the full power of Drizzle to you for use if you need it.
To ensure type-safety, you need to generate Drizzle schema first with:
```sh
npx payload generate:db-schema
```
Then, you can access Drizzle as follows:
```ts
import { posts } from './payload-generated-schema'
// To avoid installing Drizzle, you can import everything that drizzle has from our re-export path.
import { eq, sql, and } from '@payloadcms/db-postgres/drizzle'
// Drizzle's Querying API: https://orm.drizzle.team/docs/rqb
const posts = await payload.db.drizzle.query.posts.findMany()
// Drizzle's Select API https://orm.drizzle.team/docs/select
const result = await payload.db.drizzle
.select()
.from(posts)
.where(
and(eq(posts.id, 50), sql`lower(${posts.title}) = 'example post title'`),
)
```
## Tables, relations, and enums
In addition to exposing Drizzle directly, all of the tables, Drizzle relations, and enum configs are exposed for you via the `payload.db` property as well.
- Tables - `payload.db.tables`
- Enums - `payload.db.enums`
- Relations - `payload.db.relations`
## Prototyping in development mode
Drizzle exposes two ways to work locally in development mode.
The first is [`db push`](https://orm.drizzle.team/kit-docs/overview#prototyping-with-db-push), which automatically pushes changes you make to your Payload Config (and therefore, Drizzle schema) to your database so you don't have to manually migrate every time you change your Payload Config. This only works in development mode, and should not be mixed with manually running [`migrate`](/docs/database/migrations) commands.
You will be warned if any changes that you make will entail data loss while in development mode. Push is enabled by default, but you can opt out if you'd like.
Alternatively, you can disable `push` and rely solely on migrations to keep your local database in sync with your Payload Config.
## Migration workflows
In Postgres, migrations are a fundamental aspect of working with Payload and you should become familiar with how they work.
For more information about migrations, [click here](./migrations#when-to-run-migrations).
## Drizzle schema hooks
### beforeSchemaInit
Runs before the schema is built. You can use this hook to extend your database structure with tables that won't be managed by Payload.
```ts
import { postgresAdapter } from '@payloadcms/db-postgres'
import {
integer,
pgTable,
serial,
} from '@payloadcms/db-postgres/drizzle/pg-core'
postgresAdapter({
beforeSchemaInit: [
({ schema, adapter }) => {
return {
...schema,
tables: {
...schema.tables,
addedTable: pgTable('added_table', {
id: serial('id').notNull(),
}),
},
}
},
],
})
```
One use case is preserving your existing database structure when migrating to Payload. By default, Payload drops the current database schema, which may not be desirable in this scenario.
To quickly generate the Drizzle schema from your database you can use [Drizzle Introspection](https://orm.drizzle.team/kit-docs/commands#introspect--pull)
You should get the `schema.ts` file which may look like this:
```ts
import {
pgTable,
uniqueIndex,
serial,
varchar,
text,
} from 'drizzle-orm/pg-core'
export const users = pgTable('users', {
id: serial('id').primaryKey(),
fullName: text('full_name'),
phone: varchar('phone', { length: 256 }),
})
export const countries = pgTable(
'countries',
{
id: serial('id').primaryKey(),
name: varchar('name', { length: 256 }),
},
(countries) => {
return {
nameIndex: uniqueIndex('name_idx').on(countries.name),
}
},
)
```
You can import them into your config and append to the schema with the `beforeSchemaInit` hook like this:
```ts
import { postgresAdapter } from '@payloadcms/db-postgres'
import { users, countries } from '../drizzle/schema'
postgresAdapter({
beforeSchemaInit: [
({ schema, adapter }) => {
return {
...schema,
tables: {
...schema.tables,
users,
countries,
},
}
},
],
})
```
Make sure Payload doesn't overlap table names with its collections. For example, if you already have a collection with slug "users", you should either change the slug or `dbName` to change the table name for this collection.
### afterSchemaInit
Runs after the Drizzle schema is built. You can use this hook to modify the schema with features that aren't supported by Payload, or if you want to add a column that you don't want to be in the Payload config.
To extend a table, Payload exposes `extendTable` utility to the args. You can refer to the [Drizzle documentation](https://orm.drizzle.team/docs/sql-schema-declaration).
The following example adds the `extra_integer_column` column and a composite index on `country` and `city` columns.
```ts
import { postgresAdapter } from '@payloadcms/db-postgres'
import { index, integer } from '@payloadcms/db-postgres/drizzle/pg-core'
import { buildConfig } from 'payload'
export default buildConfig({
collections: [
{
slug: 'places',
fields: [
{
name: 'country',
type: 'text',
},
{
name: 'city',
type: 'text',
},
],
},
],
db: postgresAdapter({
afterSchemaInit: [
({ schema, extendTable, adapter }) => {
extendTable({
table: schema.tables.places,
columns: {
extraIntegerColumn: integer('extra_integer_column'),
},
extraConfig: (table) => ({
country_city_composite_index: index(
'country_city_composite_index',
).on(table.country, table.city),
}),
})
return schema
},
],
}),
})
```
### Note for generated schema:
Columns and tables, added in schema hooks won't be added to the generated via `payload generate:db-schema` Drizzle schema.
If you want them to be there, you either have to edit this file manually or mutate the internal Payload "raw" SQL schema in the `beforeSchemaInit`:
```ts
import { postgresAdapter } from '@payloadcms/db-postgres'
postgresAdapter({
beforeSchemaInit: [
({ schema, adapter }) => {
// Add a new table
adapter.rawTables.myTable = {
name: 'my_table',
columns: {
my_id: {
name: 'my_id',
type: 'serial',
primaryKey: true,
},
},
}
// Add a new column to generated by Payload table:
adapter.rawTables.posts.columns.customColumn = {
name: 'custom_column',
// Note that Payload SQL doesn't support everything that Drizzle does.
type: 'integer',
notNull: true,
}
// Add a new index to generated by Payload table:
adapter.rawTables.posts.indexes.customColumnIdx = {
name: 'custom_column_idx',
unique: true,
on: ['custom_column'],
}
return schema
},
],
})
```

View File

@@ -1,280 +0,0 @@
---
title: SQLite
label: SQLite
order: 70
desc: Payload supports SQLite through an officially supported Drizzle Database Adapter.
keywords: SQLite, documentation, typescript, Content Management System, cms, headless, javascript, node, react, nextjs
---
To use Payload with SQLite, install the package `@payloadcms/db-sqlite`. It leverages Drizzle ORM and `libSQL` to interact with a SQLite database that you provide.
It automatically manages changes to your database for you in development mode, and exposes a full suite of migration controls for you to leverage in order to keep other database environments in sync with your schema. DDL transformations are automatically generated.
To configure Payload to use SQLite, pass the `sqliteAdapter` to your Payload Config as follows:
```ts
import { sqliteAdapter } from '@payloadcms/db-sqlite'
export default buildConfig({
// Your config goes here
collections: [
// Collections go here
],
// Configure the SQLite adapter here
db: sqliteAdapter({
// SQLite-specific arguments go here.
// `client.url` is required.
client: {
url: process.env.DATABASE_URL,
authToken: process.env.DATABASE_AUTH_TOKEN,
},
}),
})
```
## Options
| Option | Description |
| -------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `client` \* | [Client connection options](https://orm.drizzle.team/docs/get-started-sqlite#turso) that will be passed to `createClient` from `@libsql/client`. |
| `push` | Disable Drizzle's [`db push`](https://orm.drizzle.team/kit-docs/overview#prototyping-with-db-push) in development mode. By default, `push` is enabled for development mode only. |
| `migrationDir` | Customize the directory that migrations are stored. |
| `logger` | The instance of the logger to be passed to drizzle. By default Payload's will be used. |
| `idType` | A string of 'number', or 'uuid' that is used for the data type given to id columns. |
| `transactionOptions` | A SQLiteTransactionConfig object for transactions, or set to `false` to disable using transactions. [More details](https://orm.drizzle.team/docs/transactions) |
| `localesSuffix` | A string appended to the end of table names for storing localized fields. Default is '\_locales'. |
| `relationshipsSuffix` | A string appended to the end of table names for storing relationships. Default is '\_rels'. |
| `versionsSuffix` | A string appended to the end of table names for storing versions. Defaults to '\_v'. |
| `beforeSchemaInit` | Drizzle schema hook. Runs before the schema is built. [More Details](#beforeschemainit) |
| `afterSchemaInit` | Drizzle schema hook. Runs after the schema is built. [More Details](#afterschemainit) |
| `generateSchemaOutputFile` | Override generated schema from `payload generate:db-schema` file path. Defaults to `{CWD}/src/payload-generated.schema.ts` |
| `autoIncrement` | Pass `true` to enable SQLite [AUTOINCREMENT](https://www.sqlite.org/autoinc.html) for primary keys to ensure the same ID cannot be reused from deleted rows |
| `allowIDOnCreate` | Set to `true` to use the `id` passed in data on the create API operations without using a custom ID field. |
| `blocksAsJSON` | Store blocks as a JSON column instead of using the relational structure which can improve performance with a large amount of blocks |
## Access to Drizzle
After Payload is initialized, this adapter will expose the full power of Drizzle to you for use if you need it.
To ensure type-safety, you need to generate Drizzle schema first with:
```sh
npx payload generate:db-schema
```
Then, you can access Drizzle as follows:
```ts
// Import table from the generated file
import { posts } from './payload-generated-schema'
// To avoid installing Drizzle, you can import everything that drizzle has from our re-export path.
import { eq, sql, and } from '@payloadcms/db-sqlite/drizzle'
// Drizzle's Querying API: https://orm.drizzle.team/docs/rqb
const posts = await payload.db.drizzle.query.posts.findMany()
// Drizzle's Select API https://orm.drizzle.team/docs/select
const result = await payload.db.drizzle
.select()
.from(posts)
.where(
and(eq(posts.id, 50), sql`lower(${posts.title}) = 'example post title'`),
)
```
## Tables and relations
In addition to exposing Drizzle directly, all of the tables and Drizzle relations are exposed for you via the `payload.db` property as well.
- Tables - `payload.db.tables`
- Relations - `payload.db.relations`
## Prototyping in development mode
Drizzle exposes two ways to work locally in development mode.
The first is [`db push`](https://orm.drizzle.team/kit-docs/overview#prototyping-with-db-push), which automatically pushes changes you make to your Payload Config (and therefore, Drizzle schema) to your database so you don't have to manually migrate every time you change your Payload Config. This only works in development mode, and should not be mixed with manually running [`migrate`](/docs/database/migrations) commands.
You will be warned if any changes that you make will entail data loss while in development mode. Push is enabled by default, but you can opt out if you'd like.
Alternatively, you can disable `push` and rely solely on migrations to keep your local database in sync with your Payload Config.
## Migration workflows
In SQLite, migrations are a fundamental aspect of working with Payload and you should become familiar with how they work.
For more information about migrations, [click here](./migrations#when-to-run-migrations).
## Drizzle schema hooks
### beforeSchemaInit
Runs before the schema is built. You can use this hook to extend your database structure with tables that won't be managed by Payload.
```ts
import { sqliteAdapter } from '@payloadcms/db-sqlite'
import { integer, sqliteTable } from '@payloadcms/db-sqlite/drizzle/sqlite-core'
sqliteAdapter({
beforeSchemaInit: [
({ schema, adapter }) => {
return {
...schema,
tables: {
...schema.tables,
addedTable: sqliteTable('added_table', {
id: integer('id').primaryKey({ autoIncrement: true }),
}),
},
}
},
],
})
```
One use case is preserving your existing database structure when migrating to Payload. By default, Payload drops the current database schema, which may not be desirable in this scenario.
To quickly generate the Drizzle schema from your database you can use [Drizzle Introspection](https://orm.drizzle.team/kit-docs/commands#introspect--pull)
You should get the `schema.ts` file which may look like this:
```ts
import {
sqliteTable,
text,
uniqueIndex,
integer,
} from 'drizzle-orm/sqlite-core'
export const users = sqliteTable('users', {
id: integer('id').primaryKey({ autoIncrement: true }),
fullName: text('full_name'),
phone: text('phone', { length: 256 }),
})
export const countries = sqliteTable(
'countries',
{
id: integer('id').primaryKey({ autoIncrement: true }),
name: text('name', { length: 256 }),
},
(countries) => {
return {
nameIndex: uniqueIndex('name_idx').on(countries.name),
}
},
)
```
You can import them into your config and append to the schema with the `beforeSchemaInit` hook like this:
```ts
import { sqliteAdapter } from '@payloadcms/db-sqlite'
import { users, countries } from '../drizzle/schema'
sqliteAdapter({
beforeSchemaInit: [
({ schema, adapter }) => {
return {
...schema,
tables: {
...schema.tables,
users,
countries,
},
}
},
],
})
```
Make sure Payload doesn't overlap table names with its collections. For example, if you already have a collection with slug "users", you should either change the slug or `dbName` to change the table name for this collection.
### afterSchemaInit
Runs after the Drizzle schema is built. You can use this hook to modify the schema with features that aren't supported by Payload, or if you want to add a column that you don't want to be in the Payload config.
To extend a table, Payload exposes `extendTable` utility to the args. You can refer to the [Drizzle documentation](https://orm.drizzle.team/docs/sql-schema-declaration).
The following example adds the `extra_integer_column` column and a composite index on `country` and `city` columns.
```ts
import { sqliteAdapter } from '@payloadcms/db-sqlite'
import { index, integer } from '@payloadcms/db-sqlite/drizzle/sqlite-core'
import { buildConfig } from 'payload'
export default buildConfig({
collections: [
{
slug: 'places',
fields: [
{
name: 'country',
type: 'text',
},
{
name: 'city',
type: 'text',
},
],
},
],
db: sqliteAdapter({
afterSchemaInit: [
({ schema, extendTable, adapter }) => {
extendTable({
table: schema.tables.places,
columns: {
extraIntegerColumn: integer('extra_integer_column'),
},
extraConfig: (table) => ({
country_city_composite_index: index(
'country_city_composite_index',
).on(table.country, table.city),
}),
})
return schema
},
],
}),
})
```
### Note for generated schema:
Columns and tables, added in schema hooks won't be added to the generated via `payload generate:db-schema` Drizzle schema.
If you want them to be there, you either have to edit this file manually or mutate the internal Payload "raw" SQL schema in the `beforeSchemaInit`:
```ts
import { sqliteAdapter } from '@payloadcms/db-sqlite'
sqliteAdapter({
beforeSchemaInit: [
({ schema, adapter }) => {
// Add a new table
adapter.rawTables.myTable = {
name: 'my_table',
columns: {
my_id: {
name: 'my_id',
type: 'integer',
primaryKey: true,
},
},
}
// Add a new column to generated by Payload table:
adapter.rawTables.posts.columns.customColumn = {
name: 'custom_column',
// Note that Payload SQL doesn't support everything that Drizzle does.
type: 'integer',
notNull: true,
}
// Add a new index to generated by Payload table:
adapter.rawTables.posts.indexes.customColumnIdx = {
name: 'custom_column_idx',
unique: true,
on: ['custom_column'],
}
return schema
},
],
})
```

View File

@@ -1,139 +0,0 @@
---
title: Transactions
label: Transactions
order: 30
keywords: database, transactions, sql, mongodb, postgres, documentation, Content Management System, cms, headless, typescript, node, react, nextjs
desc: Database transactions are fully supported within Payload.
---
Database transactions allow your application to make a series of database changes in an all-or-nothing commit. Consider an HTTP request that creates a new **Order** and has an `afterChange` hook to update the stock count of related **Items**. If an error occurs when updating an **Item** and an HTTP error is returned to the user, you would not want the new **Order** to be persisted or any other items to be changed either. This kind of interaction with the database is handled seamlessly with transactions.
By default, Payload will use transactions for all data changing operations, as long as it is supported by the configured database. Database changes are contained within all Payload operations and any errors thrown will result in all changes being rolled back without being committed. When transactions are not supported by the database, Payload will continue to operate as expected without them.
<Banner type="info">
**Note:**
MongoDB requires a connection to a replicaset in order to make use of transactions.
</Banner>
<Banner type="info">
**Note:**
Transactions in SQLite are disabled by default. You need to pass `transactionOptions: {}` to enable them.
</Banner>
The initial request made to Payload will begin a new transaction and attach it to the `req.transactionID`. If you have a `hook` that interacts with the database, you can opt in to using the same transaction by passing the `req` in the arguments. For example:
```ts
const afterChange: CollectionAfterChangeHook = async ({ req }) => {
// because req.transactionID is assigned from Payload and passed through,
// my-slug will only persist if the entire request is successful
await req.payload.create({
req,
collection: 'my-slug',
data: {
some: 'data',
},
})
}
```
## Async Hooks with Transactions
Since Payload hooks can be async and be written to not await the result, it is possible to have an incorrect success response returned on a request that is rolled back. If you have a hook where you do not `await` the result, then you should **not** pass the `req.transactionID`.
```ts
const afterChange: CollectionAfterChangeHook = async ({ req }) => {
// WARNING: an async call made with the same req, but NOT awaited,
// may fail resulting in an OK response being returned with response data that is not committed
const dangerouslyIgnoreAsync = req.payload.create({
req,
collection: 'my-slug',
data: {
some: 'other data',
},
})
// Should this call fail, it will not rollback other changes
// because the req (and its transactionID) is not passed through
const safelyIgnoredAsync = req.payload.create({
collection: 'my-slug',
data: {
some: 'other data',
},
})
}
```
## Direct Transaction Access
When writing your own scripts or custom endpoints, you may wish to have direct control over transactions. This is useful for interacting with your database outside of Payload's Local API.
The following functions can be used for managing transactions:
- `payload.db.beginTransaction` - Starts a new session and returns a transaction ID for use in other Payload Local API calls.
- `payload.db.commitTransaction` - Takes the identifier for the transaction, finalizes any changes.
- `payload.db.rollbackTransaction` - Takes the identifier for the transaction, discards any changes.
Payload uses the `req` object to pass the transaction ID through to the database adapter. If you are not using the `req` object, you can make a new object to pass the transaction ID directly to database adapter methods and Local API calls.
Example:
```ts
import payload from 'payload'
import config from './payload.config'
const standalonePayloadScript = async () => {
// initialize Payload
await payload.init({ config })
const transactionID = await payload.db.beginTransaction()
try {
// Make an update using the Local API
await payload.update({
collection: 'posts',
data: {
some: 'data',
},
where: {
slug: { equals: 'my-slug' },
},
req: { transactionID },
})
/*
You can make additional db changes or run other functions
that need to be committed on an all or nothing basis
*/
// Commit the transaction
await payload.db.commitTransaction(transactionID)
} catch (error) {
// Rollback the transaction
await payload.db.rollbackTransaction(transactionID)
}
}
standalonePayloadScript()
```
## Disabling Transactions
If you wish to disable transactions entirely, you can do so by passing `false` as the `transactionOptions` in your database adapter configuration. All the official Payload database adapters support this option.
In addition to allowing database transactions to be disabled at the adapter level. You can prevent Payload from using a transaction in direct calls to the Local API by adding `disableTransaction: true` to the args. For example:
```ts
await payload.update({
collection: 'posts',
data: {
some: 'data',
},
where: {
slug: { equals: 'my-slug' },
},
disableTransaction: true,
})
```

View File

@@ -1,163 +0,0 @@
---
title: Email Functionality
label: Overview
order: 10
desc: Payload uses an adapter pattern to enable email functionality. Set up email functions such as password resets, order confirmations and more.
keywords: email, overview, config, configuration, documentation, Content Management System, cms, headless, javascript, node, react, nextjs
---
## Introduction
Payload has a few email adapters that can be imported to enable email functionality. The [@payloadcms/email-nodemailer](https://www.npmjs.com/package/@payloadcms/email-nodemailer) package will be the package most will want to install. This package provides an easy way to use [Nodemailer](https://nodemailer.com) for email and won't get in your way for those already familiar.
The email adapter should be passed into the `email` property of the Payload Config. This will allow Payload to send [auth-related emails](../authentication/email) for things like password resets, new user verification, and any other email sending needs you may have.
## Configuration
### Default Configuration
When email is not needed or desired, Payload will log a warning on startup notifying that email is not configured. A warning message will also be logged on any attempt to send an email.
### Email Adapter
An email adapter will require at least the following fields:
| Option | Description |
| --------------------------- | -------------------------------------------------------------------------------- |
| **`defaultFromName`** \* | The name part of the From field that will be seen on the delivered email |
| **`defaultFromAddress`** \* | The email address part of the From field that will be used when delivering email |
### Official Email Adapters
| Name | Package | Description |
| ---------- | ------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Nodemailer | [@payloadcms/email-nodemailer](https://www.npmjs.com/package/@payloadcms/email-nodemailer) | Use any [Nodemailer transport](https://nodemailer.com/transports), including SMTP, Resend, SendGrid, and more. This was provided by default in Payload 2.x. This is the easiest migration path. |
| Resend | [@payloadcms/email-resend](https://www.npmjs.com/package/@payloadcms/email-resend) | Resend email via their REST API. This is preferred for serverless platforms such as Vercel because it is much more lightweight than the nodemailer adapter. |
## Nodemailer Configuration
| Option | Description |
| ---------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **`transport`** | The Nodemailer transport object for when you want to do it yourself, not needed when transportOptions is set |
| **`transportOptions`** | An object that configures the transporter that Payload will create. For all the available options see the [Nodemailer documentation](https://nodemailer.com) or see the examples below |
## Use SMTP
Simple Mail Transfer Protocol (SMTP) options can be passed in using the `transportOptions` object on the `email` options. See the [Nodemailer SMTP documentation](https://nodemailer.com/smtp/) for more information, including details on when `secure` should and should not be set to `true`.
**Example email options using SMTP:**
```ts
import { buildConfig } from 'payload'
import { nodemailerAdapter } from '@payloadcms/email-nodemailer'
export default buildConfig({
email: nodemailerAdapter({
defaultFromAddress: 'info@payloadcms.com',
defaultFromName: 'Payload',
// Nodemailer transportOptions
transportOptions: {
host: process.env.SMTP_HOST,
port: 587,
auth: {
user: process.env.SMTP_USER,
pass: process.env.SMTP_PASS,
},
},
}),
})
```
**Example email options using nodemailer.createTransport:**
```ts
import { buildConfig } from 'payload'
import { nodemailerAdapter } from '@payloadcms/email-nodemailer'
import nodemailer from 'nodemailer'
export default buildConfig({
email: nodemailerAdapter({
defaultFromAddress: 'info@payloadcms.com',
defaultFromName: 'Payload',
// Any Nodemailer transport can be used
transport: nodemailer.createTransport({
host: process.env.SMTP_HOST,
port: 587,
auth: {
user: process.env.SMTP_USER,
pass: process.env.SMTP_PASS,
},
}),
}),
})
```
**Custom Transport:**
You also have the ability to bring your own nodemailer transport. This is an example of using the SendGrid nodemailer transport.
```ts
import { buildConfig } from 'payload'
import { nodemailerAdapter } from '@payloadcms/email-nodemailer'
import nodemailerSendgrid from 'nodemailer-sendgrid'
export default buildConfig({
email: nodemailerAdapter({
defaultFromAddress: 'info@payloadcms.com',
defaultFromName: 'Payload',
transportOptions: nodemailerSendgrid({
apiKey: process.env.SENDGRID_API_KEY,
}),
}),
})
```
During development, if you pass nothing to `nodemailerAdapter`, it will use the [ethereal.email](https://ethereal.email) service.
This will log the ethereal.email details to console on startup.
```ts
import { nodemailerAdapter } from '@payloadcms/email-nodemailer'
export default buildConfig({
email: nodemailerAdapter(),
})
```
## Resend Configuration
The Resend adapter requires an API key to be passed in the options. This can be found in the Resend dashboard. This is the preferred package if you are deploying on Vercel because this is much more lightweight than the Nodemailer adapter.
| Option | Description |
| ------ | ----------------------------------- |
| apiKey | The API key for the Resend service. |
```ts
import { buildConfig } from 'payload'
import { resendAdapter } from '@payloadcms/email-resend'
export default buildConfig({
email: resendAdapter({
defaultFromAddress: 'dev@payloadcms.com',
defaultFromName: 'Payload CMS',
apiKey: process.env.RESEND_API_KEY || '',
}),
})
```
## Sending Mail
With a working transport you can call it anywhere you have access to Payload by calling `payload.sendEmail(message)`. The `message` will contain the `to`, `subject` and `html` or `text` for the email being sent. Other options are also available and can be seen in the sendEmail args. Support for these will depend on the adapter being used.
```ts
// Example of sending an email
const email = await payload.sendEmail({
to: 'test@example.com',
subject: 'This is a test email',
text: 'This is my message body',
})
```
## Using multiple mail providers
Payload supports the use of a single transporter of email, but there is nothing stopping you from having more. Consider a use case where sending bulk email is handled differently than transactional email and could be done using a [hook](/docs/hooks/overview).

View File

@@ -1,27 +0,0 @@
---
title: Examples
label: Overview
order: 10
desc:
keywords: example, examples, starter, boilerplate, template, templates
---
Payload provides a vast array of examples to help you get started with your project no matter what you are working on. These examples are designed to be easy to get up and running, and to be easy to understand. They showcase nothing more than the specific features being demonstrated so you can easily decipher precisely what is going on.
- [Auth](https://github.com/payloadcms/payload/tree/main/examples/auth)
- [Custom Components](https://github.com/payloadcms/payload/tree/main/examples/custom-components)
- [Draft Preview](https://github.com/payloadcms/payload/tree/main/examples/draft-preview)
- [Email](https://github.com/payloadcms/payload/tree/main/examples/email)
- [Form Builder](https://github.com/payloadcms/payload/tree/main/examples/form-builder)
- [Live Preview](https://github.com/payloadcms/payload/tree/main/examples/live-preview)
- [Multi-tenant](https://github.com/payloadcms/payload/tree/main/examples/multi-tenant)
- [Tailwind / Shadcn-ui](https://github.com/payloadcms/payload/tree/main/examples/tailwind-shadcn-ui)
- [White-label Admin UI](https://github.com/payloadcms/payload/tree/main/examples/whitelabel)
If you'd like to run the examples, you can use `create-payload-app` to create a project from one:
```sh
npx create-payload-app --example example_name
```
We are adding new examples every day, so if your particular use case is not demonstrated in any existing example, please feel free to start a new [Discussion](https://github.com/payloadcms/payload/discussions) or open a new [PR](https://github.com/payloadcms/payload/pulls) to add it yourself.

View File

@@ -1,235 +0,0 @@
---
title: Array Field
label: Array
order: 20
desc: Array Fields are intended for sets of repeating fields, that you define. Learn how to use Array Fields, see examples and options.
keywords: array, fields, config, configuration, documentation, Content Management System, cms, headless, javascript, node, react, nextjs
---
The Array Field is used when you need to have a set of "repeating" [Fields](./overview). It stores an array of objects containing fields that you define. These fields can be of any type, including other arrays, to achieve infinitely nested data structures.
Arrays are useful for many different types of content from simple to complex, such as:
- A "slider" with an image ([upload field](/docs/fields/upload)) and a caption ([text field](/docs/fields/text))
- Navigational structures where editors can specify nav items containing pages ([relationship field](/docs/fields/relationship)), an "open in new tab" [checkbox field](/docs/fields/checkbox)
- Event agenda "timeslots" where you need to specify start & end time ([date field](/docs/fields/date)), label ([text field](/docs/fields/text)), and Learn More page [relationship](/docs/fields/relationship)
<LightDarkImage
srcLight="https://payloadcms.com/images/docs/fields/array.png"
srcDark="https://payloadcms.com/images/docs/fields/array-dark.png"
alt="Array field with two Rows in Payload Admin Panel"
caption="Admin Panel screenshot of an Array field with two Rows"
/>
To create an Array Field, set the `type` to `array` in your [Field Config](./overview):
```ts
import type { Field } from 'payload'
export const MyArrayField: Field = {
// ...
// highlight-start
type: 'array',
fields: [
// ...
],
// highlight-end
}
```
## Config Options
| Option | Description |
| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **`name`** \* | To be used as the property name when stored and retrieved from the database. [More details](/docs/fields/overview#field-names). |
| **`label`** | Text used as the heading in the [Admin Panel](../admin/overview) or an object with keys for each language. Auto-generated from name if not defined. |
| **`fields`** \* | Array of field types to correspond to each row of the Array. |
| **`validate`** | Provide a custom validation function that will be executed on both the [Admin Panel](../admin/overview) and the backend. [More details](/docs/fields/overview#validation). |
| **`minRows`** | A number for the fewest allowed items during validation when a value is present. |
| **`maxRows`** | A number for the most allowed items during validation when a value is present. |
| **`saveToJWT`** | If this field is top-level and nested in a config supporting [Authentication](/docs/authentication/overview), include its data in the user JWT. |
| **`hooks`** | Provide Field Hooks to control logic for this field. [More details](../hooks/fields). |
| **`access`** | Provide Field Access Control to denote what users can see and do with this field's data. [More details](../access-control/fields). |
| **`hidden`** | Restrict this field's visibility from all APIs entirely. Will still be saved to the database, but will not appear in any API or the Admin Panel. |
| **`defaultValue`** | Provide an array of row data to be used for this field's default value. [More details](/docs/fields/overview#default-values). |
| **`localized`** | Enable localization for this field. Requires [localization to be enabled](/docs/configuration/localization) in the Base config. If enabled, a separate, localized set of all data within this Array will be kept, so there is no need to specify each nested field as `localized`. |
| **`required`** | Require this field to have a value. |
| **`labels`** | Customize the row labels appearing in the Admin dashboard. |
| **`admin`** | Admin-specific configuration. [More details](#admin-options). |
| **`custom`** | Extension point for adding custom data (e.g. for plugins) |
| **`interfaceName`** | Create a top level, reusable [Typescript interface](/docs/typescript/generating-types#custom-field-interfaces) & [GraphQL type](/docs/graphql/graphql-schema#custom-field-schemas). |
| **`dbName`** | Custom table name for the field when using SQL Database Adapter ([Postgres](/docs/database/postgres)). Auto-generated from name if not defined. |
| **`typescriptSchema`** | Override field type generation with providing a JSON schema |
| **`virtual`** | Provide `true` to disable field in the database, or provide a string path to [link the field with a relationship](/docs/fields/relationship#linking-virtual-fields-with-relationships). See [Virtual Fields](https://payloadcms.com/blog/learn-how-virtual-fields-can-help-solve-common-cms-challenges) |
_\* An asterisk denotes that a property is required._
## Admin Options
To customize the appearance and behavior of the Array Field in the [Admin Panel](../admin/overview), you can use the `admin` option:
```ts
import type { Field } from 'payload'
export const MyArrayField: Field = {
// ...
admin: {
// highlight-line
// ...
},
}
```
The Array Field inherits all of the default admin options from the base [Field Admin Config](./overview#admin-options), plus the following additional options:
| Option | Description |
| ------------------------- | ----------------------------------------------------------------------------------- |
| **`initCollapsed`** | Set the initial collapsed state |
| **`components.RowLabel`** | React component to be rendered as the label on the array row. [Example](#row-label) |
| **`isSortable`** | Disable order sorting by setting this value to `false` |
## Example
In this example, we have an Array Field called `slider` that contains a set of fields for a simple image slider. Each row in the array has a `title`, `image`, and `caption`. We also customize the row label to display the title if it exists, or a default label if it doesn't.
```ts
import type { CollectionConfig } from 'payload'
export const ExampleCollection: CollectionConfig = {
slug: 'example-collection',
fields: [
{
name: 'slider', // required
type: 'array', // required
label: 'Image Slider',
minRows: 2,
maxRows: 10,
interfaceName: 'CardSlider', // optional
labels: {
singular: 'Slide',
plural: 'Slides',
},
fields: [
// required
{
name: 'title',
type: 'text',
},
{
name: 'image',
type: 'upload',
relationTo: 'media',
required: true,
},
{
name: 'caption',
type: 'text',
},
],
},
],
}
```
## Custom Components
### Field
#### Server Component
```tsx
import type React from 'react'
import { ArrayField } from '@payloadcms/ui'
import type { ArrayFieldServerComponent } from 'payload'
export const CustomArrayFieldServer: ArrayFieldServerComponent = ({
clientField,
path,
schemaPath,
permissions,
}) => {
return (
<ArrayField
field={clientField}
path={path}
schemaPath={schemaPath}
permissions={permissions}
/>
)
}
```
#### Client Component
```tsx
'use client'
import React from 'react'
import { ArrayField } from '@payloadcms/ui'
import type { ArrayFieldClientComponent } from 'payload'
export const CustomArrayFieldClient: ArrayFieldClientComponent = (props) => {
return <ArrayField {...props} />
}
```
### Label
#### Server Component
```tsx
import React from 'react'
import { FieldLabel } from '@payloadcms/ui'
import type { ArrayFieldLabelServerComponent } from 'payload'
export const CustomArrayFieldLabelServer: ArrayFieldLabelServerComponent = ({
clientField,
path,
}) => {
return (
<FieldLabel
label={clientField?.label || clientField?.name}
path={path}
required={clientField?.required}
/>
)
}
```
#### Client Component
```tsx
'use client'
import type { ArrayFieldLabelClientComponent } from 'payload'
import { FieldLabel } from '@payloadcms/ui'
import React from 'react'
export const CustomArrayFieldLabelClient: ArrayFieldLabelClientComponent = ({
field,
path,
}) => {
return (
<FieldLabel
label={field?.label || field?.name}
path={path}
required={field?.required}
/>
)
}
```
### Row Label
```tsx
'use client'
import { useRowLabel } from '@payloadcms/ui'
export const ArrayRowLabel = () => {
const { data, rowNumber } = useRowLabel<{ title?: string }>()
const customLabel = `${data.title || 'Slide'} ${String(rowNumber).padStart(2, '0')} `
return <div>Custom Label: {customLabel}</div>
}
```

View File

@@ -1,390 +0,0 @@
---
title: Blocks Field
label: Blocks
order: 30
desc: The Blocks Field is a great layout build and can be used to construct any flexible content model. Learn how to use Block Fields, see examples and options.
keywords: blocks, fields, config, configuration, documentation, Content Management System, cms, headless, javascript, node, react, nextjs
---
The Blocks Field is **incredibly powerful**, storing an array of objects based on the fields that you define, where each item in the array is a "block" with its own unique schema.
Blocks are a great way to create a flexible content model that can be used to build a wide variety of content types, including:
- A layout builder tool that grants editors to design highly customizable page or post layouts. Blocks could include configs such as `Quote`, `CallToAction`, `Slider`, `Content`, `Gallery`, or others.
- A form builder tool where available block configs might be `Text`, `Select`, or `Checkbox`.
- Virtual event agenda "timeslots" where a timeslot could either be a `Break`, a `Presentation`, or a `BreakoutSession`.
<LightDarkImage
srcLight="https://payloadcms.com/images/docs/fields/blocks.png"
srcDark="https://payloadcms.com/images/docs/fields/blocks-dark.png"
alt="Admin Panel screenshot of add Blocks drawer view"
caption="Admin Panel screenshot of add Blocks drawer view"
/>
To add a Blocks Field, set the `type` to `blocks` in your [Field Config](./overview):
```ts
import type { Field } from 'payload'
export const MyBlocksField: Field = {
// ...
// highlight-start
type: 'blocks',
blocks: [
// ...
],
// highlight-end
}
```
## Config Options
| Option | Description |
| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **`name`** \* | To be used as the property name when stored and retrieved from the database. [More details](/docs/fields/overview#field-names). |
| **`label`** | Text used as the heading in the Admin Panel or an object with keys for each language. Auto-generated from name if not defined. |
| **`blocks`** \* | Array of [block configs](/docs/fields/blocks#block-configs) to be made available to this field. |
| **`validate`** | Provide a custom validation function that will be executed on both the Admin Panel and the backend. [More details](/docs/fields/overview#validation). |
| **`minRows`** | A number for the fewest allowed items during validation when a value is present. |
| **`maxRows`** | A number for the most allowed items during validation when a value is present. |
| **`saveToJWT`** | If this field is top-level and nested in a config supporting [Authentication](/docs/authentication/overview), include its data in the user JWT. |
| **`hooks`** | Provide Field Hooks to control logic for this field. [More details](../hooks/fields). |
| **`access`** | Provide Field Access Control to denote what users can see and do with this field's data. [More details](../access-control/fields). |
| **`hidden`** | Restrict this field's visibility from all APIs entirely. Will still be saved to the database, but will not appear in any API response or the Admin Panel. |
| **`defaultValue`** | Provide an array of block data to be used for this field's default value. [More details](/docs/fields/overview#default-values). |
| **`localized`** | Enable localization for this field. Requires [localization to be enabled](/docs/configuration/localization) in the Base config. If enabled, a separate, localized set of all data within this field will be kept, so there is no need to specify each nested field as `localized`. |
| **`unique`** | Enforce that each entry in the Collection has a unique value for this field. |
| **`labels`** | Customize the block row labels appearing in the Admin dashboard. |
| **`admin`** | Admin-specific configuration. [More details](#admin-options). |
| **`custom`** | Extension point for adding custom data (e.g. for plugins) |
| **`typescriptSchema`** | Override field type generation with providing a JSON schema |
| **`virtual`** | Provide `true` to disable field in the database, or provide a string path to [link the field with a relationship](/docs/fields/relationship#linking-virtual-fields-with-relationships). See [Virtual Fields](https://payloadcms.com/blog/learn-how-virtual-fields-can-help-solve-common-cms-challenges) |
_\* An asterisk denotes that a property is required._
## Admin Options
To customize the appearance and behavior of the Blocks Field in the [Admin Panel](../admin/overview), you can use the `admin` option:
```ts
import type { Field } from 'payload'
export const MyBlocksField: Field = {
// ...
admin: {
// highlight-line
// ...
},
}
```
The Blocks Field inherits all of the default admin options from the base [Field Admin Config](./overview#admin-options), plus the following additional options:
| Option | Description |
| ---------------------- | -------------------------------------------------------------------------- |
| **`group`** | Text or localization object used to group this Block in the Blocks Drawer. |
| **`initCollapsed`** | Set the initial collapsed state |
| **`isSortable`** | Disable order sorting by setting this value to `false` |
| **`disableBlockName`** | Hide the blockName field by setting this value to `true` |
#### Customizing the way your block is rendered in Lexical
If you're using this block within the [Lexical editor](/docs/rich-text/overview), you can also customize how the block is rendered in the Lexical editor itself by specifying custom components.
- `admin.components.Label` - pass a custom React component here to customize the way that the label is rendered for this block
- `admin.components.Block` - pass a component here to completely override the way the block is rendered in Lexical with your own component
This is super handy if you'd like to present your editors with a very deliberate and nicely designed block "preview" right in your rich text.
For example, if you have a `gallery` block, you might want to actually render the gallery of images directly in your Lexical block. With the `admin.components.Block` property, you can do exactly that!
<Banner type="success">
**Tip:** If you customize the way your block is rendered in Lexical, you can
import utility components to easily edit / remove your block - so that you
don't have to build all of this yourself.
</Banner>
To import these utility components for one of your custom blocks, you can import the following:
```ts
import {
// Edit block buttons (choose the one that corresponds to your usage)
// When clicked, this will open a drawer with your block's fields
// so your editors can edit them
InlineBlockEditButton,
BlockEditButton,
// Buttons that will remove this block from Lexical
// (choose the one that corresponds to your usage)
InlineBlockRemoveButton,
BlockRemoveButton,
// The label that should be rendered for an inline block
InlineBlockLabel,
// The default "container" that is rendered for an inline block
// if you want to re-use it
InlineBlockContainer,
// The default "collapsible" UI that is rendered for a regular block
// if you want to re-use it
BlockCollapsible,
} from '@payloadcms/richtext-lexical/client'
```
## Block Configs
Blocks are defined as separate configs of their own.
<Banner type="success">
**Tip:** Best practice is to define each block config in its own file, and
then import them into your Blocks field as necessary. This way each block
config can be easily shared between fields. For instance, using the "layout
builder" example, you might want to feature a few of the same blocks in a Post
collection as well as a Page collection. Abstracting into their own files
trivializes their reusability.
</Banner>
| Option | Description |
| -------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **`slug`** \* | Identifier for this block type. Will be saved on each block as the `blockType` property. |
| **`fields`** \* | Array of fields to be stored in this block. |
| **`labels`** | Customize the block labels that appear in the Admin dashboard. Auto-generated from slug if not defined. |
| **`imageURL`** | Provide a custom image thumbnail to help editors identify this block in the Admin UI. |
| **`imageAltText`** | Customize this block's image thumbnail alt text. |
| **`interfaceName`** | Create a top level, reusable [Typescript interface](/docs/typescript/generating-types#custom-field-interfaces) & [GraphQL type](/docs/graphql/graphql-schema#custom-field-schemas). |
| **`graphQL.singularName`** | Text to use for the GraphQL schema name. Auto-generated from slug if not defined. NOTE: this is set for deprecation, prefer `interfaceName`. |
| **`dbName`** | Custom table name for this block type when using SQL Database Adapter ([Postgres](/docs/database/postgres)). Auto-generated from slug if not defined. |
| **`custom`** | Extension point for adding custom data (e.g. for plugins) |
### Auto-generated data per block
In addition to the field data that you define on each block, Payload will store two additional properties on each block:
**`blockType`**
The `blockType` is saved as the slug of the block that has been selected.
**`blockName`**
The Admin Panel provides each block with a `blockName` field which optionally allows editors to label their blocks for better editability and readability. This can be visually hidden via `admin.disableBlockName`.
## Example
`collections/ExampleCollection.js`
```ts
import { Block, CollectionConfig } from 'payload'
const QuoteBlock: Block = {
slug: 'Quote', // required
imageURL: 'https://google.com/path/to/image.jpg',
imageAltText: 'A nice thumbnail image to show what this block looks like',
interfaceName: 'QuoteBlock', // optional
fields: [
// required
{
name: 'quoteHeader',
type: 'text',
required: true,
},
{
name: 'quoteText',
type: 'text',
},
],
}
export const ExampleCollection: CollectionConfig = {
slug: 'example-collection',
fields: [
{
name: 'layout', // required
type: 'blocks', // required
minRows: 1,
maxRows: 20,
blocks: [
// required
QuoteBlock,
],
},
],
}
```
## Custom Components
### Field
#### Server Component
```tsx
import type React from 'react'
import { BlocksField } from '@payloadcms/ui'
import type { BlocksFieldServerComponent } from 'payload'
export const CustomBlocksFieldServer: BlocksFieldServerComponent = ({
clientField,
path,
schemaPath,
permissions,
}) => {
return (
<BlocksField
field={clientField}
path={path}
schemaPath={schemaPath}
permissions={permissions}
/>
)
}
```
#### Client Component
```tsx
'use client'
import React from 'react'
import { BlocksField } from '@payloadcms/ui'
import type { BlocksFieldClientComponent } from 'payload'
export const CustomBlocksFieldClient: BlocksFieldClientComponent = (props) => {
return <BlocksField {...props} />
}
```
### Label
#### Server Component
```tsx
import React from 'react'
import { FieldLabel } from '@payloadcms/ui'
import type { BlocksFieldLabelServerComponent } from 'payload'
export const CustomBlocksFieldLabelServer: BlocksFieldLabelServerComponent = ({
clientField,
path,
}) => {
return (
<FieldLabel
label={clientField?.label || clientField?.name}
path={path}
required={clientField?.required}
/>
)
}
```
#### Client Component
```tsx
'use client'
import React from 'react'
import { FieldLabel } from '@payloadcms/ui'
import type { BlocksFieldLabelClientComponent } from 'payload'
export const CustomBlocksFieldLabelClient: BlocksFieldLabelClientComponent = ({
label,
path,
required,
}) => {
return (
<FieldLabel
label={field?.label || field?.name}
path={path}
required={field?.required}
/>
)
}
```
### Row Label
```tsx
'use client'
import { useRowLabel } from '@payloadcms/ui'
export const BlockRowLabel = () => {
const { data, rowNumber } = useRowLabel<{ title?: string }>()
const customLabel = `${data.type} ${String(rowNumber).padStart(2, '0')} `
return <div>Custom Label: {customLabel}</div>
}
```
## Block References
If you have multiple blocks used in multiple places, your Payload Config can grow in size, potentially sending more data to the client and requiring more processing on the server. However, you can optimize performance by defining each block **once** in your Payload Config and then referencing its slug wherever it's used instead of passing the entire block config.
To do this, define the block in the `blocks` array of the Payload Config. Then, in the Blocks Field, pass the block slug to the `blockReferences` array - leaving the `blocks` array empty for compatibility reasons.
```ts
import { buildConfig } from 'payload'
import { lexicalEditor, BlocksFeature } from '@payloadcms/richtext-lexical'
// Payload Config
const config = buildConfig({
// Define the block once
blocks: [
{
slug: 'TextBlock',
fields: [
{
name: 'text',
type: 'text',
},
],
},
],
collections: [
{
slug: 'collection1',
fields: [
{
name: 'content',
type: 'blocks',
// Reference the block by slug
blockReferences: ['TextBlock'],
blocks: [], // Required to be empty, for compatibility reasons
},
],
},
{
slug: 'collection2',
fields: [
{
name: 'editor',
type: 'richText',
editor: lexicalEditor({
features: [
BlocksFeature({
// Same reference can be reused anywhere, even in the lexical editor, without incurred performance hit
blocks: ['TextBlock'],
}),
],
}),
},
],
},
],
})
```
<Banner type="warning">
**Reminder:**
Blocks referenced in the `blockReferences` array are treated as isolated from the collection / global config. This has the following implications:
1. The block config cannot be modified or extended in the collection config. It will be identical everywhere it's referenced.
2. Access control for blocks referenced in the `blockReferences` are run only once - data from the collection will not be available in the block's access control.
</Banner>
## TypeScript
As you build your own Block configs, you might want to store them in separate files but retain typing accordingly. To do so, you can import and use Payload's `Block` type:
```ts
import type { Block } from 'payload'
```

View File

@@ -1,153 +0,0 @@
---
title: Checkbox Field
label: Checkbox
order: 40
desc: Checkbox field types allow the developer to save a boolean value in the database. Learn how to use Checkbox fields, see examples and options.
keywords: checkbox, fields, config, configuration, documentation, Content Management System, cms, headless, javascript, node, react, nextjs
---
The Checkbox Field saves a boolean in the database.
<LightDarkImage
srcLight="https://payloadcms.com/images/docs/fields/checkbox.png"
srcDark="https://payloadcms.com/images/docs/fields/checkbox-dark.png"
alt="Checkbox field with text field in Payload Admin Panel"
caption="Admin Panel screenshot of Checkbox field with Text field below"
/>
To add a Checkbox Field, set the `type` to `checkbox` in your [Field Config](./overview):
```ts
import type { Field } from 'payload'
export const MyCheckboxField: Field = {
// ...
type: 'checkbox', // highlight-line
}
```
## Config Options
| Option | Description |
| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **`name`** \* | To be used as the property name when stored and retrieved from the database. [More details](/docs/fields/overview#field-names). |
| **`label`** | Text used as a field label in the Admin Panel or an object with keys for each language. |
| **`validate`** | Provide a custom validation function that will be executed on both the Admin Panel and the backend. [More details](/docs/fields/overview#validation). |
| **`index`** | Build an [index](../database/indexes) for this field to produce faster queries. Set this field to `true` if your users will perform queries on this field's data often. |
| **`saveToJWT`** | If this field is top-level and nested in a config supporting [Authentication](/docs/authentication/overview), include its data in the user JWT. |
| **`hooks`** | Provide Field Hooks to control logic for this field. [More details](../hooks/fields). |
| **`access`** | Provide Field Access Control to denote what users can see and do with this field's data. [More details](../access-control/fields). |
| **`hidden`** | Restrict this field's visibility from all APIs entirely. Will still be saved to the database, but will not appear in any API or the Admin Panel. |
| **`defaultValue`** | Provide data to be used for this field's default value, will default to false if field is also `required`. [More details](/docs/fields/overview#default-values). |
| **`localized`** | Enable localization for this field. Requires [localization to be enabled](/docs/configuration/localization) in the Base config. |
| **`required`** | Require this field to have a value. |
| **`admin`** | Admin-specific configuration. [More details](./overview#admin-options). |
| **`custom`** | Extension point for adding custom data (e.g. for plugins) |
| **`typescriptSchema`** | Override field type generation with providing a JSON schema |
| **`virtual`** | Provide `true` to disable field in the database, or provide a string path to [link the field with a relationship](/docs/fields/relationship#linking-virtual-fields-with-relationships). See [Virtual Fields](https://payloadcms.com/blog/learn-how-virtual-fields-can-help-solve-common-cms-challenges) |
_\* An asterisk denotes that a property is required._
## Example
Here is an example of a Checkbox Field in a Collection:
```ts
import type { CollectionConfig } from 'payload'
export const ExampleCollection: CollectionConfig = {
slug: 'example-collection',
fields: [
{
name: 'enableCoolStuff', // required
type: 'checkbox', // required
label: 'Click me to see fanciness',
defaultValue: false,
},
],
}
```
## Custom Components
### Field
#### Server Component
```tsx
import type React from 'react'
import { CheckboxField } from '@payloadcms/ui'
import type { CheckboxFieldServerComponent } from 'payload'
export const CustomCheckboxFieldServer: CheckboxFieldServerComponent = ({
clientField,
path,
schemaPath,
permissions,
}) => {
return (
<CheckboxField
field={clientField}
path={path}
schemaPath={schemaPath}
permissions={permissions}
/>
)
}
```
#### Client Component
```tsx
'use client'
import React from 'react'
import { CheckboxField } from '@payloadcms/ui'
import type { CheckboxFieldClientComponent } from 'payload'
export const CustomCheckboxFieldClient: CheckboxFieldClientComponent = (
props,
) => {
return <CheckboxField {...props} />
}
```
### Label
#### Server Component
```tsx
import React from 'react'
import { FieldLabel } from '@payloadcms/ui'
import type { CheckboxFieldLabelServerComponent } from 'payload'
export const CustomCheckboxFieldLabelServer: CheckboxFieldLabelServerComponent =
({ clientField, path }) => {
return (
<FieldLabel
label={clientField?.label || clientField?.name}
path={path}
required={clientField?.required}
/>
)
}
```
#### Client Component
```tsx
'use client'
import React from 'react'
import { FieldLabel } from '@payloadcms/ui'
import type { CheckboxFieldLabelClientComponent } from 'payload'
export const CustomCheckboxFieldLabelClient: CheckboxFieldLabelClientComponent =
({ label, path, required }) => {
return (
<FieldLabel
label={field?.label || field?.name}
path={path}
required={field?.required}
/>
)
}
```

View File

@@ -1,184 +0,0 @@
---
title: Code Field
label: Code
order: 50
desc: The Code field type will store any string in the Database. Learn how to use Code fields, see examples and options.
keywords: code, fields, config, configuration, documentation, Content Management System, cms, headless, javascript, node, react, nextjs
---
The Code Field saves a string in the database, but provides the [Admin Panel](../admin/overview) with a code editor styled interface.
<LightDarkImage
srcLight="https://payloadcms.com/images/docs/fields/code.png"
srcDark="https://payloadcms.com/images/docs/fields/code-dark.png"
alt="Shows a Code field in the Payload Admin Panel"
caption="This field is using the `monaco-react` editor syntax highlighting."
/>
To add a Code Field, set the `type` to `code` in your [Field Config](./overview):
```ts
import type { Field } from 'payload'
export const MyBlocksField: Field = {
// ...
type: 'code', // highlight-line
}
```
## Config Options
| Option | Description |
| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **`name`** \* | To be used as the property name when stored and retrieved from the database. [More details](/docs/fields/overview#field-names). |
| **`label`** | Text used as a field label in the Admin Panel or an object with keys for each language. |
| **`unique`** | Enforce that each entry in the Collection has a unique value for this field. |
| **`index`** | Build an [index](../database/indexes) for this field to produce faster queries. Set this field to `true` if your users will perform queries on this field's data often. |
| **`minLength`** | Used by the default validation function to ensure values are of a minimum character length. |
| **`maxLength`** | Used by the default validation function to ensure values are of a maximum character length. |
| **`validate`** | Provide a custom validation function that will be executed on both the Admin Panel and the backend. [More details](/docs/fields/overview#validation). |
| **`saveToJWT`** | If this field is top-level and nested in a config supporting [Authentication](/docs/authentication/overview), include its data in the user JWT. |
| **`hooks`** | Provide Field Hooks to control logic for this field. [More details](../hooks/fields). |
| **`access`** | Provide Field Access Control to denote what users can see and do with this field's data. [More details](../access-control/fields). |
| **`hidden`** | Restrict this field's visibility from all APIs entirely. Will still be saved to the database, but will not appear in any API or the Admin Panel. |
| **`defaultValue`** | Provide data to be used for this field's default value. [More details](/docs/fields/overview#default-values). |
| **`localized`** | Enable localization for this field. Requires [localization to be enabled](/docs/configuration/localization) in the Base config. |
| **`required`** | Require this field to have a value. |
| **`admin`** | Admin-specific configuration. See below for [more detail](#admin-options). |
| **`custom`** | Extension point for adding custom data (e.g. for plugins) |
| **`typescriptSchema`** | Override field type generation with providing a JSON schema |
| **`virtual`** | Provide `true` to disable field in the database, or provide a string path to [link the field with a relationship](/docs/fields/relationship#linking-virtual-fields-with-relationships). See [Virtual Fields](https://payloadcms.com/blog/learn-how-virtual-fields-can-help-solve-common-cms-challenges) |
_\* An asterisk denotes that a property is required._
## Admin Options
To customize the appearance and behavior of the Code Field in the [Admin Panel](../admin/overview), you can use the `admin` option:
```ts
import type { Field } from 'payload'
export const MyCodeField: Field = {
// ...
admin: {
// highlight-line
// ...
},
}
```
The Code Field inherits all of the default admin options from the base [Field Admin Config](./overview#admin-options), plus the following additional options:
| Option | Description |
| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **`language`** | This property can be set to any language listed [here](https://github.com/microsoft/monaco-editor/tree/main/src/basic-languages). |
| **`editorOptions`** | Options that can be passed to the monaco editor, [view the full list](https://microsoft.github.io/monaco-editor/typedoc/interfaces/editor.IDiffEditorConstructionOptions.html). |
## Example
`collections/ExampleCollection.ts
```ts
import type { CollectionConfig } from 'payload'
export const ExampleCollection: CollectionConfig = {
slug: 'example-collection',
fields: [
{
name: 'trackingCode', // required
type: 'code', // required
required: true,
admin: {
language: 'javascript',
},
},
],
}
```
## Custom Components
### Field
#### Server Component
```tsx
import type React from 'react'
import { CodeField } from '@payloadcms/ui'
import type { CodeFieldServerComponent } from 'payload'
export const CustomCodeFieldServer: CodeFieldServerComponent = ({
clientField,
path,
schemaPath,
permissions,
}) => {
return (
<CodeField
field={clientField}
path={path}
schemaPath={schemaPath}
permissions={permissions}
/>
)
}
```
#### Client Component
```tsx
'use client'
import React from 'react'
import { CodeField } from '@payloadcms/ui'
import type { CodeFieldClientComponent } from 'payload'
export const CustomCodeFieldClient: CodeFieldClientComponent = (props) => {
return <CodeField {...props} />
}
```
### Label
#### Server Component
```tsx
import React from 'react'
import { FieldLabel } from '@payloadcms/ui'
import type { CodeFieldLabelServerComponent } from 'payload'
export const CustomCodeFieldLabelServer: CodeFieldLabelServerComponent = ({
clientField,
path,
}) => {
return (
<FieldLabel
label={clientField?.label || clientField?.name}
path={path}
required={clientField?.required}
/>
)
}
```
#### Client Component
```tsx
'use client'
import React from 'react'
import { FieldLabel } from '@payloadcms/ui'
import type { CodeFieldLabelClientComponent } from 'payload'
export const CustomCodeFieldLabelClient: CodeFieldLabelClientComponent = ({
field,
path,
}) => {
return (
<FieldLabel
label={field?.label || field?.name}
path={path}
required={field?.required}
/>
)
}
```

View File

@@ -1,96 +0,0 @@
---
title: Collapsible Field
label: Collapsible
order: 60
desc: With the Collapsible field, you can place fields within a collapsible layout component that can be collapsed / expanded.
keywords: row, fields, config, configuration, documentation, Content Management System, cms, headless, javascript, node, react, nextjs
---
The Collapsible Field is presentational-only and only affects the Admin Panel. By using it, you can place fields within a nice layout component that can be collapsed / expanded.
<LightDarkImage
srcLight="https://payloadcms.com/images/docs/fields/collapsible.png"
srcDark="https://payloadcms.com/images/docs/fields/collapsible-dark.png"
alt="Shows a Collapsible field in the Payload Admin Panel"
caption="Admin Panel screenshot of a Collapsible field"
/>
To add a Collapsible Field, set the `type` to `collapsible` in your [Field Config](./overview):
```ts
import type { Field } from 'payload'
export const MyCollapsibleField: Field = {
// ...
// highlight-start
type: 'collapsible',
fields: [
// ...
],
// highlight-end
}
```
## Config Options
| Option | Description |
| --------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **`label`** \* | A label to render within the header of the collapsible component. This can be a string, function or react component. Function/components receive `({ data, path })` as args. |
| **`fields`** \* | Array of field types to nest within this Collapsible. |
| **`admin`** | Admin-specific configuration. [More details](#admin-options). |
| **`custom`** | Extension point for adding custom data (e.g. for plugins) |
_\* An asterisk denotes that a property is required._
## Admin Options
To customize the appearance and behavior of the Collapsible Field in the [Admin Panel](../admin/overview), you can use the `admin` option:
```ts
import type { Field } from 'payload'
export const MyCollapsibleField: Field = {
// ...
admin: {
// highlight-line
// ...
},
}
```
The Collapsible Field inherits all of the default admin options from the base [Field Admin Config](./overview#admin-options), plus the following additional options:
| Option | Description |
| ------------------- | ------------------------------- |
| **`initCollapsed`** | Set the initial collapsed state |
## Example
`collections/ExampleCollection.ts`
```ts
import type { CollectionConfig } from 'payload'
export const ExampleCollection: CollectionConfig = {
slug: 'example-collection',
fields: [
{
label: ({ data }) => data?.title || 'Untitled',
type: 'collapsible', // required
fields: [
// required
{
name: 'title',
type: 'text',
required: true,
},
{
name: 'someTextField',
type: 'text',
required: true,
},
],
},
],
}
```

View File

@@ -1,249 +0,0 @@
---
title: Date Field
label: Date
order: 70
desc: The Date field type stores a Date in the database. Learn how to use and customize the Date field, see examples and options.
keywords: date, fields, config, configuration, documentation, Content Management System, cms, headless, javascript, node, react, nextjs
---
The Date Field saves a Date in the database and provides the [Admin Panel](../admin/overview) with a customizable time picker interface.
<LightDarkImage
srcLight="https://payloadcms.com/images/docs/fields/date.png"
srcDark="https://payloadcms.com/images/docs/fields/date-dark.png"
alt="Shows a Date field in the Payload Admin Panel"
caption="This field is using the `react-datepicker` component for UI."
/>
To add a Date Field, set the `type` to `date` in your [Field Config](./overview):
```ts
import type { Field } from 'payload'
export const MyDateField: Field = {
// ...
type: 'date', // highlight-line
}
```
## Config Options
| Option | Description |
| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **`name`** \* | To be used as the property name when stored and retrieved from the database. [More details](/docs/fields/overview#field-names). |
| **`label`** | Text used as a field label in the Admin Panel or an object with keys for each language. |
| **`index`** | Build an [index](../database/indexes) for this field to produce faster queries. Set this field to `true` if your users will perform queries on this field's data often. |
| **`validate`** | Provide a custom validation function that will be executed on both the Admin Panel and the backend. [More details](/docs/fields/overview#validation). |
| **`saveToJWT`** | If this field is top-level and nested in a config supporting [Authentication](/docs/authentication/overview), include its data in the user JWT. |
| **`hooks`** | Provide Field Hooks to control logic for this field. [More details](../hooks/fields). |
| **`access`** | Provide Field Access Control to denote what users can see and do with this field's data. [More details](../access-control/fields). |
| **`hidden`** | Restrict this field's visibility from all APIs entirely. Will still be saved to the database, but will not appear in any API or the Admin Panel. |
| **`defaultValue`** | Provide data to be used for this field's default value. [More details](/docs/fields/overview#default-values). |
| **`localized`** | Enable localization for this field. Requires [localization to be enabled](/docs/configuration/localization) in the Base config. |
| **`required`** | Require this field to have a value. |
| **`admin`** | Admin-specific configuration. [More details](#admin-options). |
| **`custom`** | Extension point for adding custom data (e.g. for plugins) |
| **`timezone`** \* | Set to `true` to enable timezone selection on this field. [More details](#timezones). |
| **`typescriptSchema`** | Override field type generation with providing a JSON schema |
| **`virtual`** | Provide `true` to disable field in the database, or provide a string path to [link the field with a relationship](/docs/fields/relationship#linking-virtual-fields-with-relationships). See [Virtual Fields](https://payloadcms.com/blog/learn-how-virtual-fields-can-help-solve-common-cms-challenges) |
_\* An asterisk denotes that a property is required._
## Admin Options
To customize the appearance and behavior of the Date Field in the [Admin Panel](../admin/overview), you can use the `admin` option:
```ts
import type { Field } from 'payload'
export const MyDateField: Field = {
// ...
admin: {
// highlight-line
// ...
},
}
```
The Date Field inherits all of the default admin options from the base [Field Admin Config](./overview#admin-options), plus the following additional options:
| Property | Description |
| ------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------- |
| **`placeholder`** | Placeholder text for the field. |
| **`date`** | Pass options to customize date field appearance. |
| **`date.displayFormat`** | Format date to be shown in field **cell**. |
| **`date.pickerAppearance`** \* | Determines the appearance of the datepicker: `dayAndTime` `timeOnly` `dayOnly` `monthOnly`. |
| **`date.monthsToShow`** \* | Number of months to display max is 2. Defaults to 1. |
| **`date.minDate`** \* | Min date value to allow. |
| **`date.maxDate`** \* | Max date value to allow. |
| **`date.minTime`** \* | Min time value to allow. |
| **`date.maxTime`** \* | Max date value to allow. |
| **`date.overrides`** \* | Pass any valid props directly to the [react-datepicker](https://github.com/Hacker0x01/react-datepicker/blob/master/docs/datepicker.md) |
| **`date.timeIntervals`** \* | Time intervals to display. Defaults to 30 minutes. |
| **`date.timeFormat`** \* | Determines time format. Defaults to `'h:mm aa'`. |
_\* This property is passed directly to [react-datepicker](https://github.com/Hacker0x01/react-datepicker/blob/master/docs/datepicker.md)._
### Display Format and Picker Appearance
These properties only affect how the date is displayed in the UI. The full date is always stored in the format `YYYY-MM-DDTHH:mm:ss.SSSZ` (e.g. `1999-01-01T8:00:00.000+05:00`).
`displayFormat` determines how the date is presented in the field **cell**, you can pass any valid [unicode date format](https://date-fns.org/v4.1.0/docs/format).
`pickerAppearance` sets the appearance of the **react datepicker**, the options available are `dayAndTime`, `dayOnly`, `timeOnly`, and `monthOnly`. By default, the datepicker will display `dayOnly`.
When only `pickerAppearance` is set, an equivalent format will be rendered in the date field cell. To overwrite this format, set `displayFormat`.
## Example
`collections/ExampleCollection.ts`
```ts
import type { CollectionConfig } from 'payload'
export const ExampleCollection: CollectionConfig = {
slug: 'example-collection',
fields: [
{
name: 'dateOnly',
type: 'date',
admin: {
date: {
pickerAppearance: 'dayOnly',
displayFormat: 'd MMM yyy',
},
},
},
{
name: 'timeOnly',
type: 'date',
admin: {
date: {
pickerAppearance: 'timeOnly',
displayFormat: 'h:mm:ss a',
},
},
},
{
name: 'monthOnly',
type: 'date',
admin: {
date: {
pickerAppearance: 'monthOnly',
displayFormat: 'MMMM yyyy',
},
},
},
],
}
```
## Custom Components
### Field
#### Server Component
```tsx
import type React from 'react'
import { DateTimeField } from '@payloadcms/ui'
import type { DateFieldServerComponent } from 'payload'
export const CustomDateFieldServer: DateFieldServerComponent = ({
clientField,
path,
schemaPath,
permissions,
}) => {
return (
<DateTimeField
field={clientField}
path={path}
schemaPath={schemaPath}
permissions={permissions}
/>
)
}
```
#### Client Component
```tsx
'use client'
import React from 'react'
import { DateTimeField } from '@payloadcms/ui'
import type { DateFieldClientComponent } from 'payload'
export const CustomDateFieldClient: DateFieldClientComponent = (props) => {
return <DateTimeField {...props} />
}
```
### Label
#### Server Component
```tsx
import React from 'react'
import { FieldLabel } from '@payloadcms/ui'
import type { DateFieldLabelServerComponent } from 'payload'
export const CustomDateFieldLabelServer: DateFieldLabelServerComponent = ({
clientField,
path,
}) => {
return (
<FieldLabel
label={clientField?.label || clientField?.name}
path={path}
required={clientField?.required}
/>
)
}
```
#### Client Component
```tsx
'use client'
import React from 'react'
import { FieldLabel } from '@payloadcms/ui'
import type { DateFieldLabelClientComponent } from 'payload'
export const CustomDateFieldLabelClient: DateFieldLabelClientComponent = ({
field,
path,
}) => {
return (
<FieldLabel
label={field?.label || field?.name}
path={path}
required={field?.required}
/>
)
}
```
## Timezones
To enable timezone selection on a Date field, set the `timezone` property to `true`:
```ts
{
name: 'date',
type: 'date',
timezone: true,
}
```
This will add a dropdown to the date picker that allows users to select a timezone. The selected timezone will be saved in the database along with the date in a new column named `date_tz`.
You can customise the available list of timezones in the [global admin config](../admin/overview#timezones).
<Banner type="info">
**Good to know:**
The date itself will be stored in UTC so it's up to you to handle the conversion to the user's timezone when displaying the date in your frontend.
Dates without a specific time are normalised to 12:00 in the selected timezone.
</Banner>

View File

@@ -1,179 +0,0 @@
---
title: Email Field
label: Email
order: 80
desc: The Email field enforces that the value provided is a valid email address. Learn how to use Email fields, see examples and options.
keywords: email, fields, config, configuration, documentation, Content Management System, cms, headless, javascript, node, react, nextjs
---
The Email Field enforces that the value provided is a valid email address.
<LightDarkImage
srcLight="https://payloadcms.com/images/docs/fields/email.png"
srcDark="https://payloadcms.com/images/docs/fields/email-dark.png"
alt="Shows an Email field in the Payload Admin Panel"
caption="Admin Panel screenshot of an Email field"
/>
To create an Email Field, set the `type` to `email` in your [Field Config](./overview):
```ts
import type { Field } from 'payload'
export const MyEmailField: Field = {
// ...
type: 'email', // highlight-line
}
```
## Config Options
| Option | Description |
| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **`name`** \* | To be used as the property name when stored and retrieved from the database. [More details](/docs/fields/overview#field-names). |
| **`label`** | Text used as a field label in the Admin Panel or an object with keys for each language. |
| **`unique`** | Enforce that each entry in the Collection has a unique value for this field. |
| **`index`** | Build an [index](../database/indexes) for this field to produce faster queries. Set this field to `true` if your users will perform queries on this field's data often. |
| **`validate`** | Provide a custom validation function that will be executed on both the Admin Panel and the backend. [More details](/docs/fields/overview#validation). |
| **`saveToJWT`** | If this field is top-level and nested in a config supporting [Authentication](/docs/authentication/overview), include its data in the user JWT. |
| **`hooks`** | Provide Field Hooks to control logic for this field. [More details](../hooks/fields). |
| **`access`** | Provide Field Access Control to denote what users can see and do with this field's data. [More details](../access-control/fields). |
| **`hidden`** | Restrict this field's visibility from all APIs entirely. Will still be saved to the database, but will not appear in any API or the Admin Panel. |
| **`defaultValue`** | Provide data to be used for this field's default value. [More details](/docs/fields/overview#default-values). |
| **`localized`** | Enable localization for this field. Requires [localization to be enabled](/docs/configuration/localization) in the Base config. |
| **`required`** | Require this field to have a value. |
| **`admin`** | Admin-specific configuration. [More details](#admin-options). |
| **`custom`** | Extension point for adding custom data (e.g. for plugins) |
| **`typescriptSchema`** | Override field type generation with providing a JSON schema |
| **`virtual`** | Provide `true` to disable field in the database, or provide a string path to [link the field with a relationship](/docs/fields/relationship#linking-virtual-fields-with-relationships). See [Virtual Fields](https://payloadcms.com/blog/learn-how-virtual-fields-can-help-solve-common-cms-challenges) |
_\* An asterisk denotes that a property is required._
## Admin Options
To customize the appearance and behavior of the Email Field in the [Admin Panel](../admin/overview), you can use the `admin` option:
```ts
import type { Field } from 'payload'
export const MyEmailField: Field = {
// ...
admin: {
// highlight-line
// ...
},
}
```
The Email Field inherits all of the default admin options from the base [Field Admin Config](./overview#admin-options), plus the following additional options:
| Property | Description |
| ------------------ | ------------------------------------------------------------------------- |
| **`placeholder`** | Set this property to define a placeholder string for the field. |
| **`autoComplete`** | Set this property to a string that will be used for browser autocomplete. |
## Example
`collections/ExampleCollection.ts`
```ts
import type { CollectionConfig } from 'payload'
export const ExampleCollection: CollectionConfig = {
slug: 'example-collection',
fields: [
{
name: 'contact', // required
type: 'email', // required
label: 'Contact Email Address',
required: true,
},
],
}
```
## Custom Components
### Field
#### Server Component
```tsx
import type React from 'react'
import { EmailField } from '@payloadcms/ui'
import type { EmailFieldServerComponent } from 'payload'
export const CustomEmailFieldServer: EmailFieldServerComponent = ({
clientField,
path,
schemaPath,
permissions,
}) => {
return (
<EmailField
field={clientField}
path={path}
schemaPath={schemaPath}
permissions={permissions}
/>
)
}
```
#### Client Component
```tsx
'use client'
import React from 'react'
import { EmailField } from '@payloadcms/ui'
import type { EmailFieldClientComponent } from 'payload'
export const CustomEmailFieldClient: EmailFieldClientComponent = (props) => {
return <EmailField {...props} />
}
```
### Label
#### Server Component
```tsx
import React from 'react'
import { FieldLabel } from '@payloadcms/ui'
import type { EmailFieldLabelServerComponent } from 'payload'
export const CustomEmailFieldLabelServer: EmailFieldLabelServerComponent = ({
clientField,
path,
}) => {
return (
<FieldLabel
label={clientField?.label || clientField?.name}
path={path}
required={clientField?.required}
/>
)
}
```
#### Client Component
```tsx
'use client'
import React from 'react'
import { FieldLabel } from '@payloadcms/ui'
import type { EmailFieldLabelClientComponent } from 'payload'
export const CustomEmailFieldLabelClient: EmailFieldLabelClientComponent = ({
field,
path,
}) => {
return (
<FieldLabel
label={field?.label || field?.name}
path={path}
required={field?.required}
/>
)
}
```

View File

@@ -1,146 +0,0 @@
---
title: Group Field
label: Group
order: 90
desc: The Group field allows other fields to be nested under a common property. Learn how to use Group fields, see examples and options.
keywords: group, fields, config, configuration, documentation, Content Management System, cms, headless, javascript, node, react, nextjs
---
The Group Field allows [Fields](./overview) to be nested under a common property name. It also groups fields together visually in the [Admin Panel](../admin/overview).
<LightDarkImage
srcLight="https://payloadcms.com/images/docs/fields/group.png"
srcDark="https://payloadcms.com/images/docs/fields/group-dark.png"
alt="Shows a Group field in the Payload Admin Panel"
caption="Admin Panel screenshot of a Group field"
/>
To add a Group Field, set the `type` to `group` in your [Field Config](./overview):
```ts
import type { Field } from 'payload'
export const MyGroupField: Field = {
// ...
// highlight-start
type: 'group',
fields: [
// ...
],
// highlight-end
}
```
## Config Options
| Option | Description |
| ---------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **`name`** | To be used as the property name when stored and retrieved from the database. [More details](/docs/fields/overview#field-names). |
| **`fields`** \* | Array of field types to nest within this Group. |
| **`label`** | Used as a heading in the Admin Panel and to name the generated GraphQL type. Defaults to the field name, if defined. |
| **`validate`** | Provide a custom validation function that will be executed on both the Admin Panel and the backend. [More details](/docs/fields/overview#validation). |
| **`saveToJWT`** | If this field is top-level and nested in a config supporting [Authentication](/docs/authentication/overview), include its data in the user JWT. |
| **`hooks`** | Provide Field Hooks to control logic for this field. [More details](../hooks/fields). |
| **`access`** | Provide Field Access Control to denote what users can see and do with this field's data. [More details](../access-control/fields). |
| **`hidden`** | Restrict this field's visibility from all APIs entirely. Will still be saved to the database, but will not appear in any API or the Admin Panel. |
| **`defaultValue`** | Provide an object of data to be used for this field's default value. [More details](/docs/fields/overview#default-values). |
| **`localized`** | Enable localization for this field. Requires [localization to be enabled](/docs/configuration/localization) in the Base config. If enabled, a separate, localized set of all data within this Group will be kept, so there is no need to specify each nested field as `localized`. |
| **`admin`** | Admin-specific configuration. [More details](#admin-options). |
| **`custom`** | Extension point for adding custom data (e.g. for plugins) |
| **`interfaceName`** | Create a top level, reusable [Typescript interface](/docs/typescript/generating-types#custom-field-interfaces) & [GraphQL type](/docs/graphql/graphql-schema#custom-field-schemas). |
| **`typescriptSchema`** | Override field type generation with providing a JSON schema |
| **`virtual`** | Provide `true` to disable field in the database. See [Virtual Fields](https://payloadcms.com/blog/learn-how-virtual-fields-can-help-solve-common-cms-challenges) |
_\* An asterisk denotes that a property is required._
## Admin Options
To customize the appearance and behavior of the Group Field in the [Admin Panel](../admin/overview), you can use the `admin` option:
```ts
import type { Field } from 'payload'
export const MyGroupField: Field = {
// ...
admin: {
// highlight-line
// ...
},
}
```
The Group Field inherits all of the default admin options from the base [Field Admin Config](./overview#admin-options), plus the following additional options:
| Option | Description |
| ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **`hideGutter`** | Set this property to `true` to hide this field's gutter within the Admin Panel. The field gutter is rendered as a vertical line and padding, but often if this field is nested within a Group, Block, or Array, you may want to hide the gutter. |
## Example
`collections/ExampleCollection.ts`
```ts
import type { CollectionConfig } from 'payload'
export const ExampleCollection: CollectionConfig = {
slug: 'example-collection',
fields: [
{
name: 'pageMeta',
type: 'group', // required
interfaceName: 'Meta', // optional
fields: [
// required
{
name: 'title',
type: 'text',
required: true,
minLength: 20,
maxLength: 100,
},
{
name: 'description',
type: 'textarea',
required: true,
minLength: 40,
maxLength: 160,
},
],
},
],
}
```
## Presentational group fields
You can also use the Group field to only visually group fields without affecting the data structure. Not defining a label will render just the grouped fields.
```ts
import type { CollectionConfig } from 'payload'
export const ExampleCollection: CollectionConfig = {
slug: 'example-collection',
fields: [
{
label: 'Page meta',
type: 'group', // required
fields: [
{
name: 'title',
type: 'text',
required: true,
minLength: 20,
maxLength: 100,
},
{
name: 'description',
type: 'textarea',
required: true,
minLength: 40,
maxLength: 160,
},
],
},
],
}
```

View File

@@ -1,308 +0,0 @@
---
title: Join Field
label: Join
order: 140
desc: The Join field provides the ability to work on related documents. Learn how to use Join field, see examples and options.
keywords: join, relationship, junction, fields, config, configuration, documentation, Content Management System, cms, headless, javascript, node, react, nextjs
---
The Join Field is used to make Relationship and Upload fields available in the opposite direction. With a Join you can
edit and view collections
having reference to a specific collection document. The field itself acts as a virtual field, in that no new data is
stored on the collection with a Join
field. Instead, the Admin UI surfaces the related documents for a better editing experience and is surfaced by Payload's
APIs.
The Join field is useful in scenarios including:
- To surface `Orders` for a given `Product`
- To view and edit `Posts` belonging to a `Category`
- To work with any bi-directional relationship data
- Displaying where a document or upload is used in other documents
<LightDarkImage
srcLight="https://payloadcms.com/images/docs/fields/join.png"
srcDark="https://payloadcms.com/images/docs/fields/join-dark.png"
alt="Shows Join field in the Payload Admin Panel"
caption="Admin Panel screenshot of Join field"
/>
For the Join field to work, you must have an existing [relationship](./relationship) or [upload](./upload) field in the
collection you are joining. This will reference the collection and path of the field of the related documents.
To add a Relationship Field, set the `type` to `join` in your [Field Config](./overview):
```ts
import type { Field } from 'payload'
export const MyJoinField: Field = {
// highlight-start
name: 'relatedPosts',
type: 'join',
collection: 'posts',
on: 'category',
// highlight-end
}
// relationship field in another collection:
export const MyRelationshipField: Field = {
name: 'category',
type: 'relationship',
relationTo: 'categories',
}
```
In this example, the field is defined to show the related `posts` when added to a `category` collection. The `on`
property is used to specify the relationship field name of the field that relates to the collection document.
With this example, if you navigate to a Category in the Admin UI or an API response, you'll now see that the Posts which
are related to the Category are populated for you. This is extremely powerful and can be used to define a wide variety
of relationship types in an easy manner.
<Banner type="success">
The Join field is extremely performant and does not add additional query
overhead to your API responses until you add depth of 1 or above. It works in
all database adapters. In MongoDB, we use **aggregations** to automatically
join in related documents, and in relational databases, we use joins.
</Banner>
<Banner type="warning">
The Join Field is not supported in
[DocumentDB](https://aws.amazon.com/documentdb/) and [Azure Cosmos
DB](https://azure.microsoft.com/en-us/products/cosmos-db), as we internally
use MongoDB aggregations to query data for that field, which are limited
there. This can be changed in the future.
</Banner>
### Schema advice
When modeling your database, you might come across many places where you'd like to feature bi-directional relationships.
But here's an important consideration—you generally only want to store information about a given relationship in _one_
place.
Let's take the Posts and Categories example. It makes sense to define which category a post belongs to while editing the
post.
It would generally not be necessary to have a list of post IDs stored directly on the category as well, for a few
reasons:
- You want to have a "single source of truth" for relationships, and not worry about keeping two sources in sync with
one another
- If you have hundreds, thousands, or even millions of posts, you would not want to store all of those post IDs on a
given category
- Etc.
This is where the `join` field is especially powerful. With it, you only need to store the `category_id` on the `post`,
and Payload will automatically join in related posts for you when you query for categories. The related category is only
stored on the post itself - and is not duplicated on both sides. However, the `join` field is what enables
bi-directional APIs and UI for you.
### Using the Join field to have full control of your database schema
For typical polymorphic / many relationships, if you're using Postgres or SQLite, Payload will automatically create
a `posts_rels` table, which acts as a junction table to store all of a given document's relationships.
However, this might not be appropriate for your use case if you'd like to have more control over your database
architecture. You might not want to have that `_rels` table, and would prefer to maintain / control your own junction
table design.
<Banner type="success">
With the Join field, you can control your own junction table design, and avoid
Payload's automatic _rels table creation.
</Banner>
The `join` field can be used in conjunction with _any_ collection - and if you wanted to define your own "junction"
collection, which, say, is called `categories_posts` and has a `post_id` and a `category_id` column, you can achieve
complete control over the shape of that junction table.
You could go a step further and leverage the `admin.hidden` property of the `categories_posts` collection to hide the
collection from appearing in the Admin UI navigation.
#### Specifying additional fields on relationships
Another very powerful use case of the `join` field is to be able to define "context" fields on your relationships. Let's
say that you have Posts and Categories, and use join fields on both your Posts and Categories collection to join in
related docs from a new pseudo-junction collection called `categories_posts`. Now, the relations are stored in this
third junction collection, and can be surfaced on both Posts and Categories. But, importantly, you could add
additional "context" fields to this shared junction collection.
For example, on this `categories_posts` collection, in addition to having the `category` and `post` fields, we could add
custom "context" fields like `featured` or `spotlight`,
which would allow you to store additional information directly on relationships.
The `join` field gives you complete control over any type of relational architecture in Payload, all wrapped up in a
powerful Admin UI.
## Config Options
| Option | Description |
| ---------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **`name`** \* | To be used as the property name when retrieved from the database. [More details](./overview#field-names). |
| **`collection`** \* | The `slug`s having the relationship field or an array of collection slugs. |
| **`on`** \* | The name of the relationship or upload field that relates to the collection document. Use dot notation for nested paths, like 'myGroup.relationName'. If `collection` is an array, this field must exist for all specified collections |
| **`orderable`** | If true, enables custom ordering and joined documents can be reordered via drag and drop. Uses [fractional indexing](https://observablehq.com/@dgreensp/implementing-fractional-indexing) for efficient reordering. |
| **`where`** | A `Where` query to hide related documents from appearing. Will be merged with any `where` specified in the request. |
| **`maxDepth`** | Default is 1, Sets a maximum population depth for this field, regardless of the remaining depth when this field is reached. [Max Depth](../queries/depth#max-depth). |
| **`label`** | Text used as a field label in the Admin Panel or an object with keys for each language. |
| **`hooks`** | Provide Field Hooks to control logic for this field. [More details](../hooks/fields). |
| **`access`** | Provide Field Access Control to denote what users can see and do with this field's data. [More details](../access-control/fields). |
| **`defaultLimit`** | The number of documents to return. Set to 0 to return all related documents. |
| **`defaultSort`** | The field name used to specify the order the joined documents are returned. |
| **`admin`** | Admin-specific configuration. [More details](#admin-config-options). |
| **`custom`** | Extension point for adding custom data (e.g. for plugins). |
| **`typescriptSchema`** | Override field type generation with providing a JSON schema. |
| **`graphQL`** | Custom graphQL configuration for the field. [More details](/docs/graphql/overview#field-complexity) |
_\* An asterisk denotes that a property is required._
## Admin Config Options
You can control the user experience of the join field using the `admin` config properties. The following options are supported:
| Option | Description |
| ---------------------- | -------------------------------------------------------------------------------------------------------------------------- |
| **`defaultColumns`** | Array of field names that correspond to which columns to show in the relationship table. Default is the collection config. |
| **`allowCreate`** | Set to `false` to remove the controls for making new related documents from this field. |
| **`components.Label`** | Override the default Label of the Field Component. [More details](./overview#label) |
## Join Field Data
When a document is returned that for a Join field is populated with related documents. The structure returned is an
object with:
- `docs` an array of related documents or only IDs if the depth is reached
- `hasNextPage` a boolean indicating if there are additional documents
- `totalDocs` a total number of documents, exists only if `count: true` is passed to the join query
```json
{
"id": "66e3431a3f23e684075aae9c",
"relatedPosts": {
"docs": [
{
"id": "66e3431a3f23e684075aaeb9",
// other fields...
"category": "66e3431a3f23e684075aae9c"
}
// { ... }
],
"hasNextPage": false,
"totalDocs": 10 // if count: true is passed
}
// other fields...
}
```
## Join Field Data (polymorphic)
When a document is returned that for a polymorphic Join field (with `collection` as an array) is populated with related documents. The structure returned is an
object with:
- `docs` an array of `relationTo` - the collection slug of the document and `value` - the document itself or the ID if the depth is reached
- `hasNextPage` a boolean indicating if there are additional documents
- `totalDocs` a total number of documents, exists only if `count: true` is passed to the join query
```json
{
"id": "66e3431a3f23e684075aae9c",
"relatedPosts": {
"docs": [
{
"relationTo": "posts",
"value": {
"id": "66e3431a3f23e684075aaeb9",
// other fields...
"category": "66e3431a3f23e684075aae9c"
}
}
// { ... }
],
"hasNextPage": false,
"totalDocs": 10 // if count: true is passed
}
// other fields...
}
```
## Query Options
The Join Field supports custom queries to filter, sort, and limit the related documents that will be returned. In
addition to the specific query options for each Join Field, you can pass `joins: false` to disable all Join Field from
returning. This is useful for performance reasons when you don't need the related documents.
The following query options are supported:
| Property | Description |
| ----------- | --------------------------------------------------------------------------------------------------- |
| **`limit`** | The maximum related documents to be returned, default is 10. |
| **`where`** | An optional `Where` query to filter joined documents. Will be merged with the field `where` object. |
| **`sort`** | A string used to order related results |
| **`count`** | Whether include the count of related documents or not. Not included by default |
These can be applied to the Local API, GraphQL, and REST API.
### Local API
By adding `joins` to the Local API you can customize the request for each join field by the `name` of the field.
```js
const result = await payload.find({
collection: 'categories',
where: {
title: {
equals: 'My Category',
},
},
joins: {
relatedPosts: {
limit: 5,
where: {
title: {
equals: 'My Post',
},
},
sort: 'title',
},
},
})
```
<Banner type="warning">
Currently, `Where` query support on joined documents for join fields with an
array of `collection` is limited and not supported for fields inside arrays
and blocks.
</Banner>
### Rest API
The REST API supports the same query options as the Local API. You can use the `joins` query parameter to customize the
request for each join field by the `name` of the field. For example, an API call to get a document with the related
posts limited to 5 and sorted by title:
`/api/categories/${id}?joins[relatedPosts][limit]=5&joins[relatedPosts][sort]=title`
You can specify as many `joins` parameters as needed for the same or different join fields for a single request.
### GraphQL
The GraphQL API supports the same query options as the local and REST APIs. You can specify the query options for each
join field in your query.
Example:
```graphql
query {
Categories {
docs {
relatedPosts(
sort: "createdAt"
limit: 5
where: { author: { equals: "66e3431a3f23e684075aaeb9" } }
) {
docs {
title
}
hasNextPage
}
}
}
}
```

View File

@@ -1,242 +0,0 @@
---
title: JSON Field
label: JSON
order: 50
desc: The JSON field type will store any string in the Database. Learn how to use JSON fields, see examples and options.
keywords: json, jsonSchema, schema, validation, fields, config, configuration, documentation, Content Management System, cms, headless, javascript, node, react, nextjs
---
The JSON Field saves raw JSON to the database and provides the [Admin Panel](../admin/overview) with a code editor styled interface. This is different from the [Code Field](./code) which saves the value as a string in the database.
<LightDarkImage
srcLight="https://payloadcms.com/images/docs/fields/json.png"
srcDark="https://payloadcms.com/images/docs/fields/json-dark.png"
alt="Shows a JSON field in the Payload Admin Panel"
caption="This field is using the `monaco-react` editor syntax highlighting."
/>
To add a JSON Field, set the `type` to `json` in your [Field Config](./overview):
```ts
import type { Field } from 'payload'
export const MyJSONField: Field = {
// ...
type: 'json', // highlight-line
}
```
## Config Options
| Option | Description |
| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **`name`** \* | To be used as the property name when stored and retrieved from the database. [More details](/docs/fields/overview#field-names). |
| **`label`** | Text used as a field label in the Admin Panel or an object with keys for each language. |
| **`unique`** | Enforce that each entry in the Collection has a unique value for this field. |
| **`index`** | Build an [index](../database/indexes) for this field to produce faster queries. Set this field to `true` if your users will perform queries on this field's data often. |
| **`validate`** | Provide a custom validation function that will be executed on both the Admin Panel and the backend. [More details](/docs/fields/overview#validation). |
| **`jsonSchema`** | Provide a JSON schema that will be used for validation. [JSON schemas](https://json-schema.org/learn/getting-started-step-by-step) |
| **`saveToJWT`** | If this field is top-level and nested in a config supporting [Authentication](/docs/authentication/overview), include its data in the user JWT. |
| **`hooks`** | Provide Field Hooks to control logic for this field. [More details](../hooks/fields). |
| **`access`** | Provide Field Access Control to denote what users can see and do with this field's data. [More details](../access-control/fields). |
| **`hidden`** | Restrict this field's visibility from all APIs entirely. Will still be saved to the database, but will not appear in any API or the Admin Panel. |
| **`defaultValue`** | Provide data to be used for this field's default value. [More details](/docs/fields/overview#default-values). |
| **`localized`** | Enable localization for this field. Requires [localization to be enabled](/docs/configuration/localization) in the Base config. |
| **`required`** | Require this field to have a value. |
| **`admin`** | Admin-specific configuration. [More details](#admin-options). |
| **`custom`** | Extension point for adding custom data (e.g. for plugins) |
| **`typescriptSchema`** | Override field type generation with providing a JSON schema |
| **`virtual`** | Provide `true` to disable field in the database, or provide a string path to [link the field with a relationship](/docs/fields/relationship#linking-virtual-fields-with-relationships). See [Virtual Fields](https://payloadcms.com/blog/learn-how-virtual-fields-can-help-solve-common-cms-challenges) |
_\* An asterisk denotes that a property is required._
## Admin Options
To customize the appearance and behavior of the JSON Field in the [Admin Panel](../admin/overview), you can use the `admin` option:
```ts
import type { Field } from 'payload'
export const MyJSONField: Field = {
// ...
admin: {
// highlight-line
// ...
},
}
```
The JSON Field inherits all of the default admin options from the base [Field Admin Config](./overview#admin-options), plus the following additional options:
| Option | Description |
| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **`editorOptions`** | Options that can be passed to the monaco editor, [view the full list](https://microsoft.github.io/monaco-editor/typedoc/variables/editor.EditorOptions.html). |
## Example
`collections/ExampleCollection.ts`
```ts
import type { CollectionConfig } from 'payload'
export const ExampleCollection: CollectionConfig = {
slug: 'example-collection',
fields: [
{
name: 'customerJSON', // required
type: 'json', // required
required: true,
},
],
}
```
## JSON Schema Validation
Payload JSON fields fully support the [JSON schema](https://json-schema.org/) standard. By providing a schema in your field config, the editor will be guided in the admin UI, getting typeahead for properties and their formats automatically. When the document is saved, the default validation will prevent saving any invalid data in the field according to the schema in your config.
If you only provide a URL to a schema, Payload will fetch the desired schema if it is publicly available. If not, it is recommended to add the schema directly to your config or import it from another file so that it can be implemented consistently in your project.
### Local JSON Schema
`collections/ExampleCollection.ts`
```ts
import type { CollectionConfig } from 'payload'
export const ExampleCollection: CollectionConfig = {
slug: 'example-collection',
fields: [
{
name: 'customerJSON', // required
type: 'json', // required
jsonSchema: {
uri: 'a://b/foo.json', // required
fileMatch: ['a://b/foo.json'], // required
schema: {
type: 'object',
properties: {
foo: {
enum: ['bar', 'foobar'],
},
},
},
},
},
],
}
// {"foo": "bar"} or {"foo": "foobar"} - ok
// Attempting to create {"foo": "not-bar"} will throw an error
```
### Remote JSON Schema
`collections/ExampleCollection.ts`
```ts
import type { CollectionConfig } from 'payload'
export const ExampleCollection: CollectionConfig = {
slug: 'example-collection',
fields: [
{
name: 'customerJSON', // required
type: 'json', // required
jsonSchema: {
uri: 'https://example.com/customer.schema.json', // required
fileMatch: ['https://example.com/customer.schema.json'], // required
},
},
],
}
// If 'https://example.com/customer.schema.json' has a JSON schema
// {"foo": "bar"} or {"foo": "foobar"} - ok
// Attempting to create {"foo": "not-bar"} will throw an error
```
## Custom Components
### Field
#### Server Component
```tsx
import type React from 'react'
import { JSONField } from '@payloadcms/ui'
import type { JSONFieldServerComponent } from 'payload'
export const CustomJSONFieldServer: JSONFieldServerComponent = ({
clientField,
path,
schemaPath,
permissions,
}) => {
return (
<JSONField
field={clientField}
path={path}
schemaPath={schemaPath}
permissions={permissions}
/>
)
}
```
#### Client Component
```tsx
'use client'
import React from 'react'
import { JSONField } from '@payloadcms/ui'
import type { JSONFieldClientComponent } from 'payload'
export const CustomJSONFieldClient: JSONFieldClientComponent = (props) => {
return <JSONField {...props} />
}
```
### Label
#### Server Component
```tsx
import React from 'react'
import { FieldLabel } from '@payloadcms/ui'
import type { JSONFieldLabelServerComponent } from 'payload'
export const CustomJSONFieldLabelServer: JSONFieldLabelServerComponent = ({
clientField,
path,
}) => {
return (
<FieldLabel
label={clientField?.label || clientField?.name}
path={path}
required={clientField?.required}
/>
)
}
```
#### Client Component
```tsx
'use client'
import React from 'react'
import { FieldLabel } from '@payloadcms/ui'
import type { JSONFieldLabelClientComponent } from 'payload'
export const CustomJSONFieldLabelClient: JSONFieldLabelClientComponent = ({
field,
path,
}) => {
return (
<FieldLabel
label={field?.label || field?.name}
path={path}
required={field?.required}
/>
)
}
```

View File

@@ -1,187 +0,0 @@
---
title: Number Field
label: Number
order: 100
desc: Number fields store and validate numeric data. Learn how to use and format Number fields, see examples and Number field options.
keywords: number, fields, config, configuration, documentation, Content Management System, cms, headless, javascript, node, react, nextjs
---
The Number Field stores and validates numeric entry and supports additional numerical validation and formatting features.
<LightDarkImage
srcLight="https://payloadcms.com/images/docs/fields/number.png"
srcDark="https://payloadcms.com/images/docs/fields/number-dark.png"
alt="Shows a Number field in the Payload Admin Panel"
caption="Admin Panel screenshot of a Number field"
/>
To add a Number Field, set the `type` to `number` in your [Field Config](./overview):
```ts
import type { Field } from 'payload'
export const MyNumberField: Field = {
// ...
type: 'number', // highlight-line
}
```
## Config Options
| Option | Description |
| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **`name`** \* | To be used as the property name when stored and retrieved from the database. [More details](/docs/fields/overview#field-names). |
| **`label`** | Text used as a field label in the Admin Panel or an object with keys for each language. |
| **`min`** | Minimum value accepted. Used in the default `validation` function. |
| **`max`** | Maximum value accepted. Used in the default `validation` function. |
| **`hasMany`** | Makes this field an ordered array of numbers instead of just a single number. |
| **`minRows`** | Minimum number of numbers in the numbers array, if `hasMany` is set to true. |
| **`maxRows`** | Maximum number of numbers in the numbers array, if `hasMany` is set to true. |
| **`unique`** | Enforce that each entry in the Collection has a unique value for this field. |
| **`index`** | Build an [index](../database/indexes) for this field to produce faster queries. Set this field to `true` if your users will perform queries on this field's data often. |
| **`validate`** | Provide a custom validation function that will be executed on both the Admin Panel and the backend. [More details](/docs/fields/overview#validation). |
| **`saveToJWT`** | If this field is top-level and nested in a config supporting [Authentication](/docs/authentication/overview), include its data in the user JWT. |
| **`hooks`** | Provide Field Hooks to control logic for this field. [More details](../hooks/fields). |
| **`access`** | Provide Field Access Control to denote what users can see and do with this field's data. [More details](../access-control/fields). |
| **`hidden`** | Restrict this field's visibility from all APIs entirely. Will still be saved to the database, but will not appear in any API or the Admin Panel. |
| **`defaultValue`** | Provide data to be used for this field's default value. [More details](/docs/fields/overview#default-values). |
| **`localized`** | Enable localization for this field. Requires [localization to be enabled](/docs/configuration/localization) in the Base config. |
| **`required`** | Require this field to have a value. |
| **`admin`** | Admin-specific configuration. [More details](#admin-options). |
| **`custom`** | Extension point for adding custom data (e.g. for plugins) |
| **`typescriptSchema`** | Override field type generation with providing a JSON schema |
| **`virtual`** | Provide `true` to disable field in the database, or provide a string path to [link the field with a relationship](/docs/fields/relationship#linking-virtual-fields-with-relationships). See [Virtual Fields](https://payloadcms.com/blog/learn-how-virtual-fields-can-help-solve-common-cms-challenges) |
_\* An asterisk denotes that a property is required._
## Admin Options
To customize the appearance and behavior of the Number Field in the [Admin Panel](../admin/overview), you can use the `admin` option:
```ts
import type { Field } from 'payload'
export const MyNumberField: Field = {
// ...
admin: {
// highlight-line
// ...
},
}
```
The Number Field inherits all of the default admin options from the base [Field Admin Config](./overview#admin-options), plus the following additional options:
| Property | Description |
| ------------------ | --------------------------------------------------------------------------------- |
| **`step`** | Set a value for the number field to increment / decrement using browser controls. |
| **`placeholder`** | Set this property to define a placeholder string for the field. |
| **`autoComplete`** | Set this property to a string that will be used for browser autocomplete. |
## Example
`collections/ExampleCollection.ts`
```ts
import type { CollectionConfig } from 'payload'
export const ExampleCollection: CollectionConfig = {
slug: 'example-collection',
fields: [
{
name: 'age', // required
type: 'number', // required
required: true,
admin: {
step: 1,
},
},
],
}
```
## Custom Components
### Field
#### Server Component
```tsx
import type React from 'react'
import { NumberField } from '@payloadcms/ui'
import type { NumberFieldServerComponent } from 'payload'
export const CustomNumberFieldServer: NumberFieldServerComponent = ({
clientField,
path,
schemaPath,
permissions,
}) => {
return (
<NumberField
field={clientField}
path={path}
schemaPath={schemaPath}
permissions={permissions}
/>
)
}
```
#### Client Component
```tsx
'use client'
import React from 'react'
import { NumberField } from '@payloadcms/ui'
import type { NumberFieldClientComponent } from 'payload'
export const CustomNumberFieldClient: NumberFieldClientComponent = (props) => {
return <NumberField {...props} />
}
```
### Label
#### Server Component
```tsx
import React from 'react'
import { FieldLabel } from '@payloadcms/ui'
import type { NumberFieldLabelServerComponent } from 'payload'
export const CustomNumberFieldLabelServer: NumberFieldLabelServerComponent = ({
clientField,
path,
}) => {
return (
<FieldLabel
label={clientField?.label || clientField?.name}
path={path}
required={clientField?.required}
/>
)
}
```
#### Client Component
```tsx
'use client'
import React from 'react'
import { FieldLabel } from '@payloadcms/ui'
import type { NumberFieldLabelClientComponent } from 'payload'
export const CustomNumberFieldLabelClient: NumberFieldLabelClientComponent = ({
field,
path,
}) => {
return (
<FieldLabel
label={field?.label || field?.name}
path={path}
required={field?.required}
/>
)
}
```

View File

@@ -1,990 +0,0 @@
---
description: Fields are the building blocks of Payload, find out how to add or remove a field, change field type, add hooks, define Access Control and Validation.
keywords: overview, fields, config, configuration, documentation, Content Management System, cms, headless, javascript, node, react, nextjs
label: Overview
order: 10
title: Fields Overview
---
Fields are the building blocks of Payload. They define the schema of the Documents that will be stored in the [Database](../database/overview), as well as automatically generate the corresponding UI within the [Admin Panel](../admin/overview).
There are many [Field Types](#field-types) to choose from, ranging anywhere from simple text strings to nested arrays and blocks. Most fields save data to the database, while others are strictly presentational. Fields can have [Custom Validations](#validation), [Conditional Logic](./overview#conditional-logic), [Access Control](#field-level-access-control), [Hooks](#field-level-hooks), and so much more.
Fields can be endlessly customized in their appearance and behavior without affecting their underlying data structure. Fields are designed to withstand heavy modification or even complete replacement through the use of [Custom Field Components](#custom-components).
To configure fields, use the `fields` property in your [Collection](../configuration/collections) or [Global](../configuration/globals) config:
```ts
import type { CollectionConfig } from 'payload'
export const Page: CollectionConfig = {
// ...
fields: [
// highlight-line
// ...
],
}
```
## Field Types
Payload provides a wide variety of built-in Field Types, each with its own unique properties and behaviors that determine which values it can accept, how it is presented in the API, and how it will be rendered in the [Admin Panel](../admin/overview).
To configure fields, use the `fields` property in your [Collection](../configuration/collections) or [Global](../configuration/globals) config:
```ts
import type { CollectionConfig } from 'payload'
export const Page: CollectionConfig = {
slug: 'pages',
// highlight-start
fields: [
{
name: 'field',
type: 'text',
},
],
// highlight-end
}
```
<Banner type="warning">
**Reminder:** Each field is an object with at least the `type` property. This
matches the field to its corresponding Field Type. [More
details](#field-options).
</Banner>
There are three main categories of fields in Payload:
- [Data Fields](#data-fields)
- [Presentational Fields](#presentational-fields)
- [Virtual Fields](#virtual-fields)
To begin writing fields, first determine which [Field Type](#field-types) best supports your application. Then author your field accordingly using the [Field Options](#field-options) for your chosen field type.
### Data Fields
Data Fields are used to store data in the [Database](../database/overview). All Data Fields have a `name` property. This is the key that will be used to store the field's value.
Here are the available Data Fields:
- [Array](./array) - for repeating content, supports nested fields
- [Blocks](./blocks) - for block-based content, supports nested fields
- [Checkbox](./checkbox) - saves boolean true / false values
- [Code](./code) - renders a code editor interface that saves a string
- [Date](./date) - renders a date picker and saves a timestamp
- [Email](./email) - ensures the value is a properly formatted email address
- [Group](./group) - nests fields within a keyed object
- [JSON](./json) - renders a JSON editor interface that saves a JSON object
- [Number](./number) - saves numeric values
- [Point](./point) - for location data, saves geometric coordinates
- [Radio](./radio) - renders a radio button group that allows only one value to be selected
- [Relationship](./relationship) - assign relationships to other collections
- [Rich Text](./rich-text) - renders a fully extensible rich text editor
- [Select](./select) - renders a dropdown / picklist style value selector
- [Tabs (Named)](./tabs) - similar to group, but renders nested fields within a tabbed layout
- [Text](./text) - simple text input that saves a string
- [Textarea](./textarea) - similar to text, but allows for multi-line input
- [Upload](./upload) - allows local file and image upload
### Presentational Fields
Presentational Fields do not store data in the database. Instead, they are used to organize and present other fields in the [Admin Panel](../admin/overview), or to add custom UI components.
Here are the available Presentational Fields:
- [Collapsible](../fields/collapsible) - nests fields within a collapsible component
- [Row](../fields/row) - aligns fields horizontally
- [Tabs (Unnamed)](../fields/tabs) - nests fields within a tabbed layout
- [UI](../fields/ui) - blank field for custom UI components
### Virtual Fields
Virtual fields are used to display data that is not stored in the database. They are useful for displaying computed values that populate within the API response through hooks, etc.
Here are the available Virtual Fields:
- [Join](../fields/join) - achieves two-way data binding between fields
<Banner type="success">
**Tip:** Don't see a built-in field type that you need? Build it! Using a
combination of [Field Validations](#validation) and [Custom
Components](../custom-components/overview), you can override the entirety of
how a component functions within the [Admin Panel](../admin/overview) to
effectively create your own field type.
</Banner>
## Field Options
All fields require at least the `type` property. This matches the field to its corresponding [Field Type](#field-types) to determine its appearance and behavior within the [Admin Panel](../admin/overview). Each Field Type has its own unique set of options based on its own type.
To set a field's type, use the `type` property in your Field Config:
```ts
import type { Field } from 'payload'
export const MyField: Field = {
type: 'text', // highlight-line
name: 'myField',
}
```
<Banner type="warning">
For a full list of configuration options, see the documentation for each
[Field Type](#field-types).
</Banner>
### Field Names
All [Data Fields](#data-fields) require a `name` property. This is the key that will be used to store and retrieve the field's value in the database. This property must be unique amongst this field's siblings.
To set a field's name, use the `name` property in your Field Config:
```ts
import type { Field } from 'payload'
export const MyField: Field = {
type: 'text',
name: 'myField', // highlight-line
}
```
Payload reserves various field names for internal use. Using reserved field names will result in your field being sanitized from the config.
The following field names are forbidden and cannot be used:
- `__v`
- `salt`
- `hash`
- `file`
### Field-level Hooks
In addition to being able to define [Hooks](../hooks/overview) on a document-level, you can define extremely granular logic field-by-field.
To define Field-level Hooks, use the `hooks` property in your Field Config:
```ts
import type { Field } from 'payload'
export const MyField: Field = {
type: 'text',
name: 'myField',
// highlight-start
hooks: {
// ...
},
// highlight-end
}
```
For full details on Field-level Hooks, see the [Field Hooks](../hooks/fields) documentation.
### Field-level Access Control
In addition to being able to define [Access Control](../access-control/overview) on a document-level, you can define extremely granular permissions field-by-field.
To define Field-level Access Control, use the `access` property in your Field Config:
```ts
import type { Field } from 'payload'
export const MyField: Field = {
type: 'text',
name: 'myField',
// highlight-start
access: {
// ...
},
// highlight-end
}
```
For full details on Field-level Access Control, see the [Field Access Control](../access-control/fields) documentation.
### Default Values
Fields can be optionally prefilled with initial values. This is used in both the [Admin Panel](../admin/overview) as well as API requests to populate missing or undefined field values during the `create` or `update` operations.
To set a field's default value, use the `defaultValue` property in your Field Config:
```ts
import type { Field } from 'payload'
export const MyField: Field = {
type: 'text',
name: 'myField',
defaultValue: 'Hello, World!', // highlight-line
}
```
Default values can be defined as a static value or a function that returns a value. When a `defaultValue` is defined statically, Payload's [Database Adapters](../database/overview) will apply it to the database schema or models.
Functions can be written to make use of the following argument properties:
- `user` - the authenticated user object
- `locale` - the currently selected locale string
- `req` - the `PayloadRequest` object
Here is an example of a `defaultValue` function:
```ts
import type { Field } from 'payload'
const translation: {
en: 'Written by'
es: 'Escrito por'
}
export const myField: Field = {
name: 'attribution',
type: 'text',
// highlight-start
defaultValue: ({ user, locale, req }) =>
`${translation[locale]} ${user.name}`,
// highlight-end
}
```
<Banner type="success">
**Tip:** You can use async `defaultValue` functions to fill fields with data
from API requests or Local API using `req.payload`.
</Banner>
### Validation
Fields are automatically validated based on their [Field Type](#field-types) and other [Field Options](#field-options) such as `required` or `min` and `max` value constraints. If needed, however, field validations can be customized or entirely replaced by providing your own custom validation functions.
To set a custom field validation function, use the `validate` property in your Field Config:
```ts
import type { Field } from 'payload'
export const MyField: Field = {
type: 'text',
name: 'myField',
validate: (value) => Boolean(value) || 'This field is required', // highlight-line
}
```
Custom validation functions should return either `true` or a `string` representing the error message to display in API responses.
The following arguments are provided to the `validate` function:
| Argument | Description |
| -------- | ------------------------------------------------------------------------------- |
| `value` | The value of the field being validated. |
| `ctx` | An object with additional data and context. [More details](#validation-context) |
#### Validation Context
The `ctx` argument contains full document data, sibling field data, the current operation, and other useful information such as currently authenticated user:
```ts
import type { Field } from 'payload'
export const MyField: Field = {
type: 'text',
name: 'myField',
// highlight-start
validate: (val, { user }) =>
Boolean(user) || 'You must be logged in to save this field',
// highlight-end
}
```
The following additional properties are provided in the `ctx` object:
| Property | Description |
| ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `data` | An object containing the full collection or global document currently being edited. |
| `siblingData` | An object containing document data that is scoped to only fields within the same parent of this field. |
| `operation` | Will be `create` or `update` depending on the UI action or API call. |
| `path` | The full path to the field in the schema, represented as an array of string segments, including array indexes. I.e `['group', 'myArray', '1', 'textField']`. |
| `id` | The `id` of the current document being edited. `id` is `undefined` during the `create` operation. |
| `req` | The current HTTP request object. Contains `payload`, `user`, etc. |
| `event` | Either `onChange` or `submit` depending on the current action. Used as a performance opt-in. [More details](#validation-performance). |
#### Localized and Built-in Error Messages
You can return localized error messages by utilizing the translation function provided in the `req` object:
```ts
import type { Field } from 'payload'
export const MyField: Field = {
type: 'text',
name: 'myField',
validate: (value, { req: { t } }) =>
Boolean(value) || t('validation:required'), // highlight-line
}
```
This way you can use [Custom Translations](https://payloadcms.com/docs/configuration/i18n#custom-translations) as well as Payload's built in error messages (like `validation:required` used in the example above). For a full list of available translation strings, see the [english translation file](https://github.com/payloadcms/payload/blob/main/packages/translations/src/languages/en.ts) of Payload.
#### Reusing Default Field Validations
When using custom validation functions, Payload will use yours in place of the default. However, you might want to simply augment the default validation with your own custom logic.
To reuse default field validations, call them from within your custom validation function:
```ts
import { text } from 'payload/shared'
const field: Field = {
name: 'notBad',
type: 'text',
validate: (val, args) => {
if (val === 'bad') return 'This cannot be "bad"'
return text(val, args) // highlight-line
},
}
```
Here is a list of all default field validation functions:
```ts
import {
array,
blocks,
checkbox,
code,
date,
email,
json,
number,
point,
radio,
relationship,
richText,
select,
tabs,
text,
textarea,
upload,
} from 'payload/shared'
```
#### Validation Performance
When writing async or computationally heavy validation functions, it is important to consider the performance implications. Within the Admin Panel, validations are executed on every change to the field, so they should be as lightweight as possible and only run when necessary.
If you need to perform expensive validations, such as querying the database, consider using the `event` property in the `ctx` object to only run that particular validation on form submission.
To write asynchronous validation functions, use the `async` keyword to define your function:
```ts
import type { CollectionConfig } from 'payload'
export const Orders: CollectionConfig = {
slug: 'orders',
fields: [
{
name: 'customerNumber',
type: 'text',
// highlight-start
validate: async (val, { event }) => {
if (event === 'onChange') {
return true
}
// only perform expensive validation when the form is submitted
const response = await fetch(`https://your-api.com/customers/${val}`)
if (response.ok) {
return true
}
return 'The customer number provided does not match any customers within our records.'
},
// highlight-end
},
],
}
```
<Banner type="success">
For more performance tips, see the [Performance
documentation](../performance/overview).
</Banner>
## Custom ID Fields
All [Collections](../configuration/collections) automatically generate their own ID field. If needed, you can override this behavior by providing an explicit ID field to your config. This field should either be required or have a hook to generate the ID dynamically.
To define a custom ID field, add a top-level field with the `name` property set to `id`:
```ts
import type { CollectionConfig } from 'payload'
export const MyCollection: CollectionConfig = {
// ...
fields: [
{
name: 'id', // highlight-line
required: true,
type: 'number',
},
],
}
```
<Banner type="warning">
**Reminder:** The Custom ID Fields can only be of type [`Number`](./number) or
[`Text`](./text). Custom ID fields with type `text` must not contain `/` or
`.` characters.
</Banner>
## Admin Options
You can customize the appearance and behavior of fields within the [Admin Panel](../admin/overview) through the `admin` property of any Field Config:
```ts
import type { CollectionConfig } from 'payload'
export const CollectionConfig: CollectionConfig = {
// ...
fields: [
// ...
{
name: 'myField',
type: 'text',
admin: {
// highlight-line
// ...
},
},
],
}
```
The following options are available:
| Option | Description |
| ----------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **`condition`** | Programmatically show / hide fields based on other fields. [More details](#conditional-logic). |
| **`components`** | All Field Components can be swapped out for [Custom Components](../custom-components/overview) that you define. |
| **`description`** | Helper text to display alongside the field to provide more information for the editor. [More details](#description). |
| **`position`** | Specify if the field should be rendered in the sidebar by defining `position: 'sidebar'`. |
| **`width`** | Restrict the width of a field. You can pass any string-based value here, be it pixels, percentages, etc. This property is especially useful when fields are nested within a `Row` type where they can be organized horizontally. |
| **`style`** | [CSS Properties](https://developer.mozilla.org/en-US/docs/Web/CSS) to inject into the root element of the field. |
| **`className`** | Attach a [CSS class attribute](https://developer.mozilla.org/en-US/docs/Web/CSS/Class_selectors) to the root DOM element of a field. |
| **`readOnly`** | Setting a field to `readOnly` has no effect on the API whatsoever but disables the admin component's editability to prevent editors from modifying the field's value. |
| **`disabled`** | If a field is `disabled`, it is completely omitted from the [Admin Panel](../admin/overview) entirely. |
| **`disableBulkEdit`** | Set `disableBulkEdit` to `true` to prevent fields from appearing in the select options when making edits for multiple documents. Defaults to `true` for UI fields. |
| **`disableListColumn`** | Set `disableListColumn` to `true` to prevent fields from appearing in the list view column selector. |
| **`disableListFilter`** | Set `disableListFilter` to `true` to prevent fields from appearing in the list view filter options. |
| **`hidden`** | Will transform the field into a `hidden` input type. Its value will still submit with requests in the Admin Panel, but the field itself will not be visible to editors. |
### Field Descriptions
Field Descriptions are used to provide additional information to the editor about a field, such as special instructions. Their placement varies from field to field, but typically are displayed with subtle style differences beneath the field inputs.
A description can be configured in three ways:
- As a string.
- As a function which returns a string. [More details](#description-functions).
- As a React component. [More details](#description).
To add a Custom Description to a field, use the `admin.description` property in your Field Config:
```ts
import type { CollectionConfig } from 'payload'
export const MyCollectionConfig: CollectionConfig = {
// ...
fields: [
// ...
{
name: 'myField',
type: 'text',
admin: {
description: 'Hello, world!', // highlight-line
},
},
],
}
```
<Banner type="warning">
**Reminder:** To replace the Field Description with a [Custom
Component](../custom-components/overview), use the
`admin.components.Description` property. [More details](#description).
</Banner>
#### Description Functions
Custom Descriptions can also be defined as a function. Description Functions are executed on the server and can be used to format simple descriptions based on the user's current [Locale](../configuration/localization).
To add a Description Function to a field, set the `admin.description` property to a _function_ in your Field Config:
```ts
import type { CollectionConfig } from 'payload'
export const MyCollectionConfig: CollectionConfig = {
// ...
fields: [
// ...
{
name: 'myField',
type: 'text',
admin: {
description: ({ t }) => `${t('Hello, world!')}`, // highlight-line
},
},
],
}
```
All Description Functions receive the following arguments:
| Argument | Description |
| -------- | ------------------------------------------------------------------------------------------------ |
| **`t`** | The `t` function used to internationalize the Admin Panel. [More details](../configuration/i18n) |
<Banner type="info">
**Note:** If you need to subscribe to live updates within your form, use a
Description Component instead. [More details](#description).
</Banner>
### Conditional Logic
You can show and hide fields based on what other fields are doing by utilizing conditional logic on a field by field basis. The `condition` property on a field's admin config accepts a function which takes the following arguments:
| Argument | Description |
| ----------------- | -------------------------------------------------------------------------------- |
| **`data`** | The entire document's data that is currently being edited. |
| **`siblingData`** | Only the fields that are direct siblings to the field with the condition. |
| **`ctx`** | An object containing additional information about the fields location and user. |
The `ctx` object:
| Property | Description |
| --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **`blockData`** | The nearest parent block's data. If the field is not inside a block, this will be `undefined`. |
| **`operation`** | A string relating to which operation the field type is currently executing within. |
| **`path`** | The full path to the field in the schema, represented as an array of string segments, including array indexes. I.e `['group', 'myArray', '1', 'textField']`. |
| **`user`** | The currently authenticated user object. |
The `condition` function should return a boolean that will control if the field should be displayed or not.
**Example:**
```ts
{
fields: [
{
name: 'enableGreeting',
type: 'checkbox',
defaultValue: false,
},
{
name: 'greeting',
type: 'text',
admin: {
// highlight-start
condition: (data, siblingData, { blockData, path, user }) => {
if (data.enableGreeting) {
return true
} else {
return false
}
},
// highlight-end
},
},
]
}
```
### Custom Components
Within the [Admin Panel](../admin/overview), fields are represented in three distinct places:
- [Field](#field) - The actual form field rendered in the Edit View.
- [Cell](#cell) - The table cell component rendered in the List View.
- [Filter](#filter) - The filter component rendered in the List View.
- [Diff](#diff) - The Diff component rendered in the Version Diff View
To swap in Field Components with your own, use the `admin.components` property in your Field Config:
```ts
import type { CollectionConfig } from 'payload'
export const CollectionConfig: CollectionConfig = {
// ...
fields: [
// ...
{
// ...
admin: {
components: {
// highlight-line
// ...
},
},
},
],
}
```
The following options are available:
| Component | Description |
| ----------------- | ----------------------------------------------------------------------------------------------------------------------------- |
| **`Field`** | The form field rendered of the Edit View. [More details](#field). |
| **`Cell`** | The table cell rendered of the List View. [More details](#cell). |
| **`Filter`** | The filter component rendered in the List View. [More details](#filter). |
| **`Label`** | Override the default Label of the Field Component. [More details](#label). |
| **`Error`** | Override the default Error of the Field Component. [More details](#error). |
| **`Diff`** | Override the default Diff component rendered in the Version Diff View. [More details](#diff). |
| **`Description`** | Override the default Description of the Field Component. [More details](#description). |
| **`beforeInput`** | An array of elements that will be added before the input of the Field Component. [More details](#afterinput-and-beforeinput). |
| **`afterInput`** | An array of elements that will be added after the input of the Field Component. [More details](#afterinput-and-beforeinput). |
#### Field
The Field Component is the actual form field rendered in the Edit View. This is the input that user's will interact with when editing a document.
To swap in your own Field Component, use the `admin.components.Field` property in your Field Config:
```ts
import type { CollectionConfig } from 'payload'
export const CollectionConfig: CollectionConfig = {
// ...
fields: [
// ...
{
// ...
admin: {
components: {
Field: '/path/to/MyFieldComponent', // highlight-line
},
},
},
],
}
```
_For details on how to build Custom Components, see [Building Custom Components](../custom-components/overview#building-custom-components)._
<Banner type="warning">
Instead of replacing the entire Field Component, you can alternately replace
or slot-in only specific parts by using the [`Label`](#label),
[`Error`](#error), [`beforeInput`](#afterinput-and-beforinput), and
[`afterInput`](#afterinput-and-beforinput) properties.
</Banner>
##### Default Props
All Field Components receive the following props by default:
| Property | Description |
| -------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **`docPreferences`** | An object that contains the [Preferences](../admin/preferences) for the document. |
| **`field`** | In Client Components, this is the sanitized Client Field Config. In Server Components, this is the original Field Config. Server Components will also receive the sanitized field config through the`clientField` prop (see below). |
| **`locale`** | The locale of the field. [More details](../configuration/localization). |
| **`readOnly`** | A boolean value that represents if the field is read-only or not. |
| **`user`** | The currently authenticated user. [More details](../authentication/overview). |
| **`validate`** | A function that can be used to validate the field. |
| **`path`** | A string representing the direct, dynamic path to the field at runtime, i.e. `myGroup.myArray.0.myField`. |
| **`schemaPath`** | A string representing the direct, static path to the Field Config, i.e. `posts.myGroup.myArray.myField`. |
| **`indexPath`** | A hyphen-notated string representing the path to the field _within the nearest named ancestor field_, i.e. `0-0` |
In addition to the above props, all Server Components will also receive the following props:
| Property | Description |
| ----------------- | ----------------------------------------------------------------------------- |
| **`clientField`** | The serializable Client Field Config. |
| **`field`** | The Field Config. |
| **`data`** | The current document being edited. |
| **`i18n`** | The [i18n](../configuration/i18n) object. |
| **`payload`** | The [Payload](../local-api/overview) class. |
| **`permissions`** | The field permissions based on the currently authenticated user. |
| **`siblingData`** | The data of the field's siblings. |
| **`user`** | The currently authenticated user. [More details](../authentication/overview). |
| **`value`** | The value of the field at render-time. |
##### Sending and receiving values from the form
When swapping out the `Field` component, you are responsible for sending and receiving the field's `value` from the form itself.
To do so, import the [`useField`](../admin/react-hooks#usefield) hook from `@payloadcms/ui` and use it to manage the field's value:
```tsx
'use client'
import { useField } from '@payloadcms/ui'
export const CustomTextField: React.FC = () => {
const { value, setValue } = useField() // highlight-line
return <input onChange={(e) => setValue(e.target.value)} value={value} />
}
```
<Banner type="success">
For a complete list of all available React hooks, see the [Payload React
Hooks](../admin/react-hooks) documentation. For additional help, see [Building
Custom Components](../custom-components/overview#building-custom-components).
</Banner>
##### TypeScript#field-component-types
When building Custom Field Components, you can import the client field props to ensure type safety in your component. There is an explicit type for the Field Component, one for every Field Type and server/client environment. The convention is to prepend the field type onto the target type, i.e. `TextFieldClientComponent`:
```tsx
import type {
TextFieldClientComponent,
TextFieldServerComponent,
TextFieldClientProps,
TextFieldServerProps,
// ...and so on for each Field Type
} from 'payload'
```
See each individual Field Type for exact type imports.
#### Cell
The Cell Component is rendered in the table of the List View. It represents the value of the field when displayed in a table cell.
To swap in your own Cell Component, use the `admin.components.Cell` property in your Field Config:
```ts
import type { Field } from 'payload'
export const myField: Field = {
name: 'myField',
type: 'text',
admin: {
components: {
Cell: '/path/to/MyCustomCellComponent', // highlight-line
},
},
}
```
All Cell Components receive the same [Default Field Component Props](#field), plus the following:
| Property | Description |
| ------------- | --------------------------------------------------------------------- |
| **`link`** | A boolean representing whether this cell should be wrapped in a link. |
| **`onClick`** | A function that is called when the cell is clicked. |
For details on how to build Custom Components themselves, see [Building Custom Components](../custom-components/overview#building-custom-components).
#### Filter
The Filter Component is the actual input element rendered within the "Filter By" dropdown of the List View used to represent this field when building filters.
To swap in your own Filter Component, use the `admin.components.Filter` property in your Field Config:
```ts
import type { Field } from 'payload'
export const myField: Field = {
name: 'myField',
type: 'text',
admin: {
components: {
Filter: '/path/to/MyCustomFilterComponent', // highlight-line
},
},
}
```
All Custom Filter Components receive the same [Default Field Component Props](#field).
For details on how to build Custom Components themselves, see [Building Custom Components](../custom-components/overview#building-custom-components).
#### Label
The Label Component is rendered anywhere a field needs to be represented by a label. This is typically used in the Edit View, but can also be used in the List View and elsewhere.
To swap in your own Label Component, use the `admin.components.Label` property in your Field Config:
```ts
import type { Field } from 'payload'
export const myField: Field = {
name: 'myField',
type: 'text',
admin: {
components: {
Label: '/path/to/MyCustomLabelComponent', // highlight-line
},
},
}
```
All Custom Label Components receive the same [Default Field Component Props](#field).
For details on how to build Custom Components themselves, see [Building Custom Components](../custom-components/overview#building-custom-components).
##### TypeScript#label-component-types
When building Custom Label Components, you can import the component types to ensure type safety in your component. There is an explicit type for the Label Component, one for every Field Type and server/client environment. The convention is to append `LabelServerComponent` or `LabelClientComponent` to the type of field, i.e. `TextFieldLabelClientComponent`.
```tsx
import type {
TextFieldLabelServerComponent,
TextFieldLabelClientComponent,
// ...and so on for each Field Type
} from 'payload'
```
#### Description
Alternatively to the [Description Property](#field-descriptions), you can also use a [Custom Component](../custom-components/overview) as the Field Description. This can be useful when you need to provide more complex feedback to the user, such as rendering dynamic field values or other interactive elements.
To add a Description Component to a field, use the `admin.components.Description` property in your Field Config:
```ts
import type { CollectionConfig } from 'payload'
export const MyCollectionConfig: CollectionConfig = {
// ...
fields: [
// ...
{
name: 'myField',
type: 'text',
admin: {
components: {
Description: '/path/to/MyCustomDescriptionComponent', // highlight-line
},
},
},
],
}
```
All Custom Description Components receive the same [Default Field Component Props](#field).
For details on how to build a Custom Components themselves, see [Building Custom Components](../custom-components/overview#building-custom-components).
##### TypeScript#description-component-types
When building Custom Description Components, you can import the component props to ensure type safety in your component. There is an explicit type for the Description Component, one for every Field Type and server/client environment. The convention is to append `DescriptionServerComponent` or `DescriptionClientComponent` to the type of field, i.e. `TextFieldDescriptionClientComponent`.
```tsx
import type {
TextFieldDescriptionServerComponent,
TextFieldDescriptionClientComponent,
// And so on for each Field Type
} from 'payload'
```
#### Error
The Error Component is rendered when a field fails validation. It is typically displayed beneath the field input in a visually-compelling style.
To swap in your own Error Component, use the `admin.components.Error` property in your Field Config:
```ts
import type { Field } from 'payload'
export const myField: Field = {
name: 'myField',
type: 'text',
admin: {
components: {
Error: '/path/to/MyCustomErrorComponent', // highlight-line
},
},
}
```
All Error Components receive the [Default Field Component Props](#field).
For details on how to build Custom Components themselves, see [Building Custom Components](../custom-components/overview#building-custom-components).
##### TypeScript#error-component-types
When building Custom Error Components, you can import the component types to ensure type safety in your component. There is an explicit type for the Error Component, one for every Field Type and server/client environment. The convention is to append `ErrorServerComponent` or `ErrorClientComponent` to the type of field, i.e. `TextFieldErrorClientComponent`.
```tsx
import type {
TextFieldErrorServerComponent,
TextFieldErrorClientComponent,
// And so on for each Field Type
} from 'payload'
```
#### Diff
The Diff Component is rendered in the Version Diff view. It will only be visible in entities with versioning enabled,
To swap in your own Diff Component, use the `admin.components.Diff` property in your Field Config:
```ts
import type { Field } from 'payload'
export const myField: Field = {
name: 'myField',
type: 'text',
admin: {
components: {
Diff: '/path/to/MyCustomDiffComponent', // highlight-line
},
},
}
```
All Error Components receive the [Default Field Component Props](#field).
For details on how to build Custom Components themselves, see [Building Custom Components](../custom-components/overview#building-custom-components).
##### TypeScript#diff-component-types
When building Custom Diff Components, you can import the component types to ensure type safety in your component. There is an explicit type for the Diff Component, one for every Field Type and server/client environment. The convention is to append `DiffServerComponent` or `DiffClientComponent` to the type of field, i.e. `TextFieldDiffClientComponent`.
```tsx
import type {
TextFieldDiffServerComponent,
TextFieldDiffClientComponent,
// And so on for each Field Type
} from 'payload'
```
#### afterInput and beforeInput
With these properties you can add multiple components _before_ and _after_ the input element, as their name suggests. This is useful when you need to render additional elements alongside the field without replacing the entire field component.
To add components before and after the input element, use the `admin.components.beforeInput` and `admin.components.afterInput` properties in your Field Config:
```ts
import type { CollectionConfig } from 'payload'
export const MyCollectionConfig: CollectionConfig = {
// ...
fields: [
// ...
{
name: 'myField',
type: 'text',
admin: {
components: {
// highlight-start
beforeInput: ['/path/to/MyCustomComponent'],
afterInput: ['/path/to/MyOtherCustomComponent'],
// highlight-end
},
},
},
],
}
```
All `afterInput` and `beforeInput` Components receive the same [Default Field Component Props](#field).
For details on how to build Custom Components, see [Building Custom Components](../custom-components/overview#building-custom-components).
## TypeScript
You can import the Payload `Field` type as well as other common types from the `payload` package. [More details](../typescript/overview).
```ts
import type { Field } from 'payload'
```

View File

@@ -1,217 +0,0 @@
---
title: Point Field
label: Point
order: 110
desc: The Point field type stores coordinates in the database. Learn how to use Point field for geolocation and geometry.
keywords: point, geolocation, geospatial, geojson, 2dsphere, config, configuration, documentation, Content Management System, cms, headless, javascript, node, react, nextjs
---
The Point Field saves a pair of coordinates in the database and assigns an index for location related queries. The data structure in the database matches the GeoJSON structure to represent point. The Payload API simplifies the object data to only the [longitude, latitude] location.
<LightDarkImage
srcLight="https://payloadcms.com/images/docs/fields/point.png"
srcDark="https://payloadcms.com/images/docs/fields/point-dark.png"
alt="Shows a Point field in the Payload Admin Panel"
caption="Admin Panel screenshot of a Point field"
/>
To add a Point Field, set the `type` to `point` in your [Field Config](./overview):
```ts
import type { Field } from 'payload'
export const MyPointField: Field = {
// ...
type: 'point', // highlight-line
}
```
<Banner type="warning">
**Important:** The Point Field currently is not supported in SQLite.
</Banner>
## Config
| Option | Description |
| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **`name`** \* | To be used as the property name when stored and retrieved from the database. [More details](/docs/fields/overview#field-names). |
| **`label`** | Used as a field label in the Admin Panel and to name the generated GraphQL type. |
| **`unique`** | Enforce that each entry in the Collection has a unique value for this field. |
| **`index`** | Build an [index](../database/indexes) for this field to produce faster queries. To support location queries, point index defaults to `2dsphere`, to disable the index set to `false`. |
| **`validate`** | Provide a custom validation function that will be executed on both the Admin Panel and the backend. [More details](/docs/fields/overview#validation). |
| **`saveToJWT`** | If this field is top-level and nested in a config supporting [Authentication](/docs/authentication/overview), include its data in the user JWT. |
| **`hooks`** | Provide Field Hooks to control logic for this field. [More details](../hooks/fields). |
| **`access`** | Provide Field Access Control to denote what users can see and do with this field's data. [More details](../access-control/fields). |
| **`hidden`** | Restrict this field's visibility from all APIs entirely. Will still be saved to the database, but will not appear in any API or the Admin Panel. |
| **`defaultValue`** | Provide data to be used for this field's default value. [More details](/docs/fields/overview#default-values). |
| **`localized`** | Enable localization for this field. Requires [localization to be enabled](/docs/configuration/localization) in the Base config. |
| **`required`** | Require this field to have a value. |
| **`admin`** | Admin-specific configuration. [More details](./overview#admin-options). |
| **`custom`** | Extension point for adding custom data (e.g. for plugins) |
| **`typescriptSchema`** | Override field type generation with providing a JSON schema |
| **`virtual`** | Provide `true` to disable field in the database, or provide a string path to [link the field with a relationship](/docs/fields/relationship#linking-virtual-fields-with-relationships). See [Virtual Fields](https://payloadcms.com/blog/learn-how-virtual-fields-can-help-solve-common-cms-challenges) |
_\* An asterisk denotes that a property is required._
## Example
`collections/ExampleCollection.ts`
```ts
import type { CollectionConfig } from 'payload'
export const ExampleCollection: CollectionConfig = {
slug: 'example-collection',
fields: [
{
name: 'location',
type: 'point',
label: 'Location',
},
],
}
```
## Querying - near
In order to do query based on the distance to another point, you can use the `near` operator. When querying using the near operator, the returned documents will be sorted by nearest first.
## Querying - within
In order to do query based on whether points are within a specific area defined in GeoJSON, you can use the `within` operator.
Example:
```ts
const polygon: Point[] = [
[9.0, 19.0], // bottom-left
[9.0, 21.0], // top-left
[11.0, 21.0], // top-right
[11.0, 19.0], // bottom-right
[9.0, 19.0], // back to starting point to close the polygon
]
payload.find({
collection: 'points',
where: {
point: {
within: {
type: 'Polygon',
coordinates: [polygon],
},
},
},
})
```
## Querying - intersects
In order to do query based on whether points intersect a specific area defined in GeoJSON, you can use the `intersects` operator.
Example:
```ts
const polygon: Point[] = [
[9.0, 19.0], // bottom-left
[9.0, 21.0], // top-left
[11.0, 21.0], // top-right
[11.0, 19.0], // bottom-right
[9.0, 19.0], // back to starting point to close the polygon
]
payload.find({
collection: 'points',
where: {
point: {
intersects: {
type: 'Polygon',
coordinates: [polygon],
},
},
},
})
```
## Custom Components
### Field
#### Server Component
```tsx
import type React from 'react'
import { PointField } from '@payloadcms/ui'
import type { PointFieldServerComponent } from 'payload'
export const CustomPointFieldServer: PointFieldServerComponent = ({
clientField,
path,
schemaPath,
permissions,
}) => {
return (
<PointField
field={clientField}
path={path}
schemaPath={schemaPath}
permissions={permissions}
/>
)
}
```
#### Client Component
```tsx
'use client'
import React from 'react'
import { PointField } from '@payloadcms/ui'
import type { PointFieldClientComponent } from 'payload'
export const CustomPointFieldClient: PointFieldClientComponent = (props) => {
return <PointField {...props} />
}
```
### Label
#### Server Component
```tsx
import React from 'react'
import { FieldLabel } from '@payloadcms/ui'
import type { PointFieldLabelServerComponent } from 'payload'
export const CustomPointFieldLabelServer: PointFieldLabelServerComponent = ({
clientField,
path,
}) => {
return (
<FieldLabel
label={clientField?.label || clientField?.name}
path={path}
required={clientField?.required}
/>
)
}
```
#### Client Component
```tsx
'use client'
import React from 'react'
import { FieldLabel } from '@payloadcms/ui'
import type { PointFieldLabelClientComponent } from 'payload'
export const CustomPointFieldLabelClient: PointFieldLabelClientComponent = ({
field,
path,
}) => {
return (
<FieldLabel
label={field?.label || field?.name}
path={path}
required={field?.required}
/>
)
}
```

View File

@@ -1,209 +0,0 @@
---
title: Radio Group Field
label: Radio Group
order: 120
desc: The Radio field type allows for the selection of one value from a predefined set of possible values. Learn how to use Radio fields, see examples and options.
keywords: radio, fields, config, configuration, documentation, Content Management System, cms, headless, javascript, node, react, nextjs
---
The Radio Field allows for the selection of one value from a predefined set of possible values and presents a radio group-style set of inputs to the [Admin Panel](../admin/overview).
<LightDarkImage
srcLight="https://payloadcms.com/images/docs/fields/radio.png"
srcDark="https://payloadcms.com/images/docs/fields/radio-dark.png"
alt="Shows a Radio field in the Payload Admin Panel"
caption="Admin Panel screenshot of a Radio field"
/>
To add a Radio Field, set the `type` to `radio` in your [Field Config](./overview):
```ts
import type { Field } from 'payload'
export const MyRadioField: Field = {
// ...
// highlight-start
type: 'radio',
options: [
// ...
],
// highlight-end
}
```
## Config Options
| Option | Description |
| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **`name`** \* | To be used as the property name when stored and retrieved from the database. [More details](/docs/fields/overview#field-names). |
| **`options`** \* | Array of options to allow the field to store. Can either be an array of strings, or an array of objects containing a `label` string and a `value` string. |
| **`label`** | Text used as a field label in the Admin Panel or an object with keys for each language. |
| **`validate`** | Provide a custom validation function that will be executed on both the Admin Panel and the backend. [More details](/docs/fields/overview#validation). |
| **`index`** | Build an [index](../database/indexes) for this field to produce faster queries. Set this field to `true` if your users will perform queries on this field's data often. |
| **`saveToJWT`** | If this field is top-level and nested in a config supporting [Authentication](/docs/authentication/overview), include its data in the user JWT. |
| **`hooks`** | Provide Field Hooks to control logic for this field. [More details](../hooks/fields). |
| **`access`** | Provide Field Access Control to denote what users can see and do with this field's data. [More details](../access-control/fields). |
| **`hidden`** | Restrict this field's visibility from all APIs entirely. Will still be saved to the database, but will not appear in any API or the Admin Panel. |
| **`defaultValue`** | Provide data to be used for this field's default value. The default value must exist within provided values in `options`. [More details](/docs/fields/overview#default-values). |
| **`localized`** | Enable localization for this field. Requires [localization to be enabled](/docs/configuration/localization) in the Base config. |
| **`required`** | Require this field to have a value. |
| **`admin`** | Admin-specific configuration. [More details](#admin-options). |
| **`custom`** | Extension point for adding custom data (e.g. for plugins) |
| **`enumName`** | Custom enum name for this field when using SQL Database Adapter ([Postgres](/docs/database/postgres)). Auto-generated from name if not defined. |
| **`interfaceName`** | Create a top level, reusable [Typescript interface](/docs/typescript/generating-types#custom-field-interfaces) & [GraphQL type](/docs/graphql/graphql-schema#custom-field-schemas). |
| **`typescriptSchema`** | Override field type generation with providing a JSON schema |
| **`virtual`** | Provide `true` to disable field in the database, or provide a string path to [link the field with a relationship](/docs/fields/relationship#linking-virtual-fields-with-relationships). See [Virtual Fields](https://payloadcms.com/blog/learn-how-virtual-fields-can-help-solve-common-cms-challenges) |
_\* An asterisk denotes that a property is required._
<Banner type="warning">
**Important:**
Option values should be strings that do not contain hyphens or special characters due to GraphQL
enumeration naming constraints. Underscores are allowed. If you determine you need your option
values to be non-strings or contain special characters, they will be formatted accordingly before
being used as a GraphQL enum.
</Banner>
## Admin Options
To customize the appearance and behavior of the Radio Field in the [Admin Panel](../admin/overview), you can use the `admin` option:
```ts
import type { Field } from 'payload'
export const MyRadioField: Field = {
// ...
admin: {
// highlight-line
// ...
},
}
```
The Radio Field inherits all of the default admin options from the base [Field Admin Config](./overview#admin-options), plus the following additional options:
| Property | Description |
| ------------ | ---------------------------------------------------------------------------------------------------------------------------- |
| **`layout`** | Allows for the radio group to be styled as a horizontally or vertically distributed list. The default value is `horizontal`. |
## Example
`collections/ExampleCollection.ts`
```ts
import type { CollectionConfig } from 'payload'
export const ExampleCollection: CollectionConfig = {
slug: 'example-collection',
fields: [
{
name: 'color', // required
type: 'radio', // required
options: [
// required
{
label: 'Mint',
value: 'mint',
},
{
label: 'Dark Gray',
value: 'dark_gray',
},
],
defaultValue: 'mint', // The first value in options.
admin: {
layout: 'horizontal',
},
},
],
}
```
## Custom Components
### Field
#### Server Component
```tsx
import type React from 'react'
import { RadioGroupField } from '@payloadcms/ui'
import type { RadioFieldServerComponent } from 'payload'
export const CustomRadioFieldServer: RadioFieldServerComponent = ({
clientField,
path,
schemaPath,
permissions,
}) => {
return (
<RadioGroupField
field={clientField}
path={path}
schemaPath={schemaPath}
permissions={permissions}
/>
)
}
```
#### Client Component
```tsx
'use client'
import React from 'react'
import { RadioGroupField } from '@payloadcms/ui'
import type { RadioFieldClientComponent } from 'payload'
export const CustomRadioFieldClient: RadioFieldClientComponent = (props) => {
return <RadioGroupField {...props} />
}
```
### Label
#### Server Component
```tsx
import React from 'react'
import { FieldLabel } from '@payloadcms/ui'
import type { RadioFieldLabelServerComponent } from 'payload'
export const CustomRadioFieldLabelServer: RadioFieldLabelServerComponent = ({
clientField,
path,
required,
}) => {
return (
<FieldLabel
label={clientField?.label || clientField?.name}
path={path}
required={clientField?.required}
/>
)
}
```
#### Client Component
```tsx
'use client'
import React from 'react'
import { FieldLabel } from '@payloadcms/ui'
import type { RadioFieldLabelClientComponent } from 'payload'
export const CustomRadioFieldLabelClient: RadioFieldLabelClientComponent = ({
field,
path,
}) => {
return (
<FieldLabel
label={field?.label || field?.name}
path={path}
required={field?.required}
/>
)
}
```

View File

@@ -1,497 +0,0 @@
---
title: Relationship Field
label: Relationship
order: 130
desc: The Relationship field provides the ability to relate documents together. Learn how to use Relationship fields, see examples and options.
keywords: relationship, fields, config, configuration, documentation, Content Management System, cms, headless, javascript, node, react, nextjs
---
The Relationship Field is one of the most powerful fields Payload features. It provides the ability to easily relate documents together.
<LightDarkImage
srcLight="https://payloadcms.com/images/docs/fields/relationship.png"
srcDark="https://payloadcms.com/images/docs/fields/relationship-dark.png"
alt="Shows a relationship field in the Payload Admin Panel"
caption="Admin Panel screenshot of a Relationship field"
/>
The Relationship field is used in a variety of ways, including:
- To add `Product` documents to an `Order` document
- To allow for an `Order` to feature a `placedBy` relationship to either an `Organization` or `User` collection
- To assign `Category` documents to `Post` documents
To add a Relationship Field, set the `type` to `relationship` in your [Field Config](./overview):
```ts
import type { Field } from 'payload'
export const MyRelationshipField: Field = {
// ...
// highlight-start
type: 'relationship',
relationTo: 'products',
// highlight-end
}
```
## Config Options
| Option | Description |
| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **`name`** \* | To be used as the property name when stored and retrieved from the database. [More details](/docs/fields/overview#field-names). |
| **`relationTo`** \* | Provide one or many collection `slug`s to be able to assign relationships to. |
| **`filterOptions`** | A query to filter which options appear in the UI and validate against. [More details](#filtering-relationship-options). |
| **`hasMany`** | Boolean when, if set to `true`, allows this field to have many relations instead of only one. |
| **`minRows`** | A number for the fewest allowed items during validation when a value is present. Used with `hasMany`. |
| **`maxRows`** | A number for the most allowed items during validation when a value is present. Used with `hasMany`. |
| **`maxDepth`** | Sets a maximum population depth for this field, regardless of the remaining depth when this field is reached. [Max Depth](/docs/queries/depth#max-depth) |
| **`label`** | Text used as a field label in the Admin Panel or an object with keys for each language. |
| **`unique`** | Enforce that each entry in the Collection has a unique value for this field. |
| **`validate`** | Provide a custom validation function that will be executed on both the Admin Panel and the backend. [More details](/docs/fields/overview#validation). |
| **`index`** | Build an [index](../database/indexes) for this field to produce faster queries. Set this field to `true` if your users will perform queries on this field's data often. |
| **`saveToJWT`** | If this field is top-level and nested in a config supporting [Authentication](/docs/authentication/overview), include its data in the user JWT. |
| **`hooks`** | Provide Field Hooks to control logic for this field. [More details](../hooks/fields). |
| **`access`** | Provide Field Access Control to denote what users can see and do with this field's data. [More details](../access-control/fields). |
| **`hidden`** | Restrict this field's visibility from all APIs entirely. Will still be saved to the database, but will not appear in any API or the Admin Panel. |
| **`defaultValue`** | Provide data to be used for this field's default value. [More details](/docs/fields/overview#default-values). |
| **`localized`** | Enable localization for this field. Requires [localization to be enabled](/docs/configuration/localization) in the Base config. |
| **`required`** | Require this field to have a value. |
| **`admin`** | Admin-specific configuration. [More details](#admin-options). |
| **`custom`** | Extension point for adding custom data (e.g. for plugins) |
| **`typescriptSchema`** | Override field type generation with providing a JSON schema |
| **`virtual`** | Provide `true` to disable field in the database, or provide a string path to [link the field with a relationship](/docs/fields/relationship#linking-virtual-fields-with-relationships). See [Virtual Fields](https://payloadcms.com/blog/learn-how-virtual-fields-can-help-solve-common-cms-challenges) |
| **`graphQL`** | Custom graphQL configuration for the field. [More details](/docs/graphql/overview#field-complexity) |
_\* An asterisk denotes that a property is required._
<Banner type="success">
**Tip:** The [Depth](../queries/depth) parameter can be used to automatically
populate related documents that are returned by the API.
</Banner>
## Admin Options
To the appearance and behavior of the Relationship Field in the [Admin Panel](../admin/overview), you can use the `admin` option:
```ts
import type { Field } from 'payload'
export const MyRelationshipField: Field = {
// ...
admin: {
// highlight-line
// ...
},
}
```
The Relationship Field inherits all of the default admin options from the base [Field Admin Config](./overview#admin-options), plus the following additional options:
| Property | Description |
| ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
| **`isSortable`** | Set to `true` if you'd like this field to be sortable within the Admin UI using drag and drop (only works when `hasMany` is set to `true`). |
| **`allowCreate`** | Set to `false` if you'd like to disable the ability to create new documents from within the relationship field. |
| **`allowEdit`** | Set to `false` if you'd like to disable the ability to edit documents from within the relationship field. |
| **`sortOptions`** | Define a default sorting order for the options within a Relationship field's dropdown. [More details](#sort-options) |
| **`placeholder`** | Define a custom text or function to replace the generic default placeholder |
| **`appearance`** | Set to `drawer` or `select` to change the behavior of the field. Defaults to `select`. |
### Sort Options
You can specify `sortOptions` in two ways:
**As a string:**
Provide a string to define a global default sort field for all relationship field dropdowns across different
collections. You can prefix the field name with a minus symbol ("-") to sort in descending order.
Example:
```ts
sortOptions: 'fieldName',
```
This configuration will sort all relationship field dropdowns by `"fieldName"` in ascending order.
**As an object :**
Specify an object where keys are collection slugs and values are strings representing the field names to sort by. This
allows for different sorting fields for each collection's relationship dropdown.
Example:
```ts
sortOptions: {
"pages": "fieldName1",
"posts": "-fieldName2",
"categories": "fieldName3"
}
```
In this configuration:
- Dropdowns related to `pages` will be sorted by `"fieldName1"` in ascending order.
- Dropdowns for `posts` will use `"fieldName2"` for sorting in descending order (noted by the "-" prefix).
- Dropdowns associated with `categories` will sort based on `"fieldName3"` in ascending order.
Note: If `sortOptions` is not defined, the default sorting behavior of the Relationship field dropdown will be used.
## Filtering relationship options
Options can be dynamically limited by supplying a [query constraint](/docs/queries/overview), which will be used both for validating input and filtering available relationships in the UI.
The `filterOptions` property can either be a `Where` query, or a function returning `true` to not filter, `false` to prevent all, or a `Where` query. When using a function, it will be called with an argument object with the following properties:
| Property | Description |
| ------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `blockData` | The data of the nearest parent block. Will be `undefined` if the field is not within a block or when called on a `Filter` component within the list view. |
| `data` | An object containing the full collection or global document currently being edited. Will be an empty object when called on a `Filter` component within the list view. |
| `id` | The `id` of the current document being edited. Will be `undefined` during the `create` operation or when called on a `Filter` component within the list view. |
| `relationTo` | The collection `slug` to filter against, limited to this field's `relationTo` property. |
| `req` | The Payload Request, which contains references to `payload`, `user`, `locale`, and more. |
| `siblingData` | An object containing document data that is scoped to only fields within the same parent of this field. Will be an empty object when called on a `Filter` component within the list view. |
| `user` | An object containing the currently authenticated user. |
## Example
```ts
import type { CollectionConfig } from 'payload'
export const ExampleCollection: CollectionConfig = {
slug: 'example-collection',
fields: [
{
name: 'purchase',
type: 'relationship',
relationTo: ['products', 'services'],
filterOptions: ({ relationTo, siblingData }) => {
// returns a Where query dynamically by the type of relationship
if (relationTo === 'products') {
return {
stock: { greater_than: siblingData.quantity },
}
}
if (relationTo === 'services') {
return {
isAvailable: { equals: true },
}
}
},
},
],
}
```
You can learn more about writing queries [here](/docs/queries/overview).
<Banner type="warning">
**Note:**
When a relationship field has both **filterOptions** and a custom
**validate** function, the api will not validate **filterOptions**
unless you call the default relationship field validation function imported from
**payload/shared** in your validate function.
</Banner>
## Bi-directional relationships
The `relationship` field on its own is used to define relationships for the document that contains the relationship field, and this can be considered as a "one-way" relationship. For example, if you have a Post that has a `category` relationship field on it, the related `category` itself will not surface any information about the posts that have the category set.
However, the `relationship` field can be used in conjunction with the `Join` field to produce powerful bi-directional relationship authoring capabilities. If you're interested in bi-directional relationships, check out the [documentation for the Join field](./join).
## How the data is saved
Given the variety of options possible within the `relationship` field type, the shape of the data needed for creating
and updating these fields can vary. The following sections will describe the variety of data shapes that can arise from
this field.
### Has One
The most simple pattern of a relationship is to use `hasMany: false` with a `relationTo` that allows for only one type
of collection.
```ts
{
slug: 'example-collection',
fields: [
{
name: 'owner', // required
type: 'relationship', // required
relationTo: 'users', // required
hasMany: false,
}
]
}
```
The shape of the data to save for a document with the field configured this way would be:
```json
{
// ObjectID of the related user
"owner": "6031ac9e1289176380734024"
}
```
When querying documents in this collection via REST API, you could query as follows:
`?where[owner][equals]=6031ac9e1289176380734024`.
### Has One - Polymorphic
Also known as **dynamic references**, in this configuration, the `relationTo` field is an array of Collection slugs that
tells Payload which Collections are valid to reference.
```ts
{
slug: 'example-collection',
fields: [
{
name: 'owner', // required
type: 'relationship', // required
relationTo: ['users', 'organizations'], // required
hasMany: false,
}
]
}
```
The shape of the data to save for a document with more than one relationship type would be:
```json
{
"owner": {
"relationTo": "organizations",
"value": "6031ac9e1289176380734024"
}
}
```
Here is an example for how to query documents by this data (note the difference in referencing the `owner.value`):
`?where[owner.value][equals]=6031ac9e1289176380734024`.
You can also query for documents where a field has a relationship to a specific Collection:
`?where[owners.relationTo][equals]=organizations`.
This query would return only documents that have an owner relationship to organizations.
### Has Many
The `hasMany` tells Payload that there may be more than one collection saved to the field.
```ts
{
slug: 'example-collection',
fields: [
{
name: 'owners', // required
type: 'relationship', // required
relationTo: 'users', // required
hasMany: true,
}
]
}
```
To save to the `hasMany` relationship field we need to send an array of IDs:
```json
{
"owners": ["6031ac9e1289176380734024", "602c3c327b811235943ee12b"]
}
```
When querying documents, the format does not change for arrays:
`?where[owners][equals]=6031ac9e1289176380734024`.
### Has Many - Polymorphic
```ts
{
slug: 'example-collection',
fields: [
{
name: 'owners', // required
type: 'relationship', // required
relationTo: ['users', 'organizations'], // required
hasMany: true,
required: true,
}
]
}
```
Relationship fields with `hasMany` set to more than one kind of collections save their data as an array of objects—each
containing the Collection `slug` as the `relationTo` value, and the related document `id` for the `value`:
```json
{
"owners": [
{
"relationTo": "users",
"value": "6031ac9e1289176380734024"
},
{
"relationTo": "organizations",
"value": "602c3c327b811235943ee12b"
}
]
}
```
Querying is done in the same way as the earlier Polymorphic example:
`?where[owners.value][equals]=6031ac9e1289176380734024`.
### Querying and Filtering Polymorphic Relationships
Polymorphic and non-polymorphic relationships must be queried differently because of how the related data is stored and
may be inconsistent across different collections. Because of this, filtering polymorphic relationship fields from the
Collection List admin UI is limited to the `id` value.
For a polymorphic relationship, the response will always be an array of objects. Each object will contain
the `relationTo` and `value` properties.
The data can be queried by the related document ID:
`?where[field.value][equals]=6031ac9e1289176380734024`.
Or by the related document Collection slug:
`?where[field.relationTo][equals]=your-collection-slug`.
However, you **cannot** query on any field values within the related document.
Since we are referencing multiple collections, the field you are querying on may not exist and break the query.
<Banner type="warning">
**Note:**
You **cannot** query on a field within a polymorphic relationship as you would with a
non-polymorphic relationship.
</Banner>
### Linking virtual fields with relationships
You can link virtual fields to fields in other collections through a relationship (or upload) field, for example:
```ts
{
collections: [
{
slug: 'categories',
fields: [
{
name: 'title',
type: 'text',
},
],
},
{
slug: 'posts',
fields: [
{
type: 'relationship',
name: 'category',
relationTo: 'categories',
},
{
type: 'text',
name: 'categoryTitle',
virtual: 'category.title',
},
],
},
],
}
```
Here, `categoryTitle` will _always_ be populated with the corresponding value, even if the current `depth` is `0`, You can also query and sort by this field.
The relationship must not be `hasMany: true` or polymorphic.
The path can be deeply nested into 2 or more relationship fields, for example `post.category.title` as long as all the relationship fields meet the above requirement.
## Custom Components
### Field
#### Server Component
```tsx
import type React from 'react'
import { RelationshipField } from '@payloadcms/ui'
import type { RelationshipFieldServerComponent } from 'payload'
export const CustomRelationshipFieldServer: RelationshipFieldServerComponent =
({ clientField, path, schemaPath, permissions }) => {
return (
<RelationshipField
field={clientField}
path={path}
schemaPath={schemaPath}
permissions={permissions}
/>
)
}
```
#### Client Component
```tsx
'use client'
import React from 'react'
import { RelationshipField } from '@payloadcms/ui'
import type { RelationshipFieldClientComponent } from 'payload'
export const CustomRelationshipFieldClient: RelationshipFieldClientComponent = (
props,
) => {
return <RelationshipField {...props} />
}
```
### Label
#### Server Component
```tsx
import React from 'react'
import { FieldLabel } from '@payloadcms/ui'
import type { RelationshipFieldLabelServerComponent } from 'payload'
export const CustomRelationshipFieldLabelServer: RelationshipFieldLabelServerComponent =
(clientField, path) => {
return (
<FieldLabel
label={clientField?.label || clientField?.name}
path={path}
required={clientField?.required}
/>
)
}
```
#### Client Component
```tsx
'use client'
import React from 'react'
import { FieldLabel } from '@payloadcms/ui'
import type { RelationshipFieldLabelClientComponent } from 'payload'
export const CustomRelationshipFieldLabelClient: RelationshipFieldLabelClientComponent =
({ field, path }) => {
return (
<FieldLabel
label={field?.label || field?.name}
path={path}
required={field?.required}
/>
)
}
```

View File

@@ -1,65 +0,0 @@
---
description: The Rich Text field allows dynamic content to be written through the Admin Panel. Learn how to use Rich Text fields, see examples and options.
keywords: rich text, fields, config, configuration, documentation, Content Management System, cms, headless, javascript, node, react, nextjs
label: Rich Text
order: 140
title: Rich Text Field
---
The Rich Text Field lets editors write and format dynamic content in a familiar interface. The content is saved as JSON in the database and can be converted to HTML or any other format needed.
Consistent with Payload's goal of making you learn as little of Payload as possible, customizing and using the Rich Text Editor does not involve learning how to develop for a Payload rich text editor.
Instead, you can invest your time and effort into learning the underlying open-source tools that will allow you to apply your learnings elsewhere as well.
<LightDarkImage
alt="Shows a Rich Text field in the Payload Admin Panel"
caption="Admin Panel screenshot of a Rich Text field"
srcDark="https://payloadcms.com/images/docs/fields/richtext-dark.png"
srcLight="https://payloadcms.com/images/docs/fields/richtext.png"
/>
## Config Options
| Option | Description |
| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **`name`** \* | To be used as the property name when stored and retrieved from the database. [More details](./overview#field-names). |
| **`label`** | Text used as a field label in the Admin Panel or an object with keys for each language. |
| **`validate`** | Provide a custom validation function that will be executed on both the Admin Panel and the backend. [More details](./overview#validation). |
| **`saveToJWT`** | If this field is top-level and nested in a config supporting [Authentication](../authentication/overview), include its data in the user JWT. |
| **`hooks`** | Provide Field Hooks to control logic for this field. [More details](../hooks/fields). |
| **`access`** | Provide Field Access Control to denote what users can see and do with this field's data. [More details](../access-control/fields). |
| **`hidden`** | Restrict this field's visibility from all APIs entirely. Will still be saved to the database, but will not appear in any API or the Admin Panel. |
| **`defaultValue`** | Provide data to be used for this field's default value. [More details](./overview#default-values). |
| **`localized`** | Enable localization for this field. Requires [localization to be enabled](../configuration/localization) in the Base config. |
| **`required`** | Require this field to have a value. |
| **`admin`** | Admin-specific configuration. [More details](#admin-options). |
| **`editor`** | Customize or override the rich text editor. [More details](../rich-text/overview). |
| **`custom`** | Extension point for adding custom data (e.g. for plugins) |
| **`typescriptSchema`** | Override field type generation with providing a JSON schema |
| **`virtual`** | Provide `true` to disable field in the database, or provide a string path to [link the field with a relationship](/docs/fields/relationship#linking-virtual-fields-with-relationships). See [Virtual Fields](https://payloadcms.com/blog/learn-how-virtual-fields-can-help-solve-common-cms-challenges) |
\*_ An asterisk denotes that a property is required._
## Admin Options
To customize the appearance and behavior of the Rich Text Field in the [Admin Panel](../admin/overview), you can use the `admin` option. The Rich Text Field inherits all the default options from the base [Field Admin Config](./overview#admin-options).
```ts
import type { Field } from 'payload'
export const MyRichTextField: Field = {
// ...
admin: {
// highlight-line
// ...
},
}
```
Further customization can be done with editor-specific options.
## Editor-specific Options
For a ton more editor-specific options, including how to build custom rich text elements directly into your editor,
take a look at the [rich text editor documentation](../rich-text/overview).

View File

@@ -1,78 +0,0 @@
---
title: Row Field
label: Row
order: 150
desc: With the Row field you can arrange fields next to each other in the Admin Panel to help you customize your Dashboard.
keywords: row, fields, config, configuration, documentation, Content Management System, cms, headless, javascript, node, react, nextjs
---
The Row Field is presentational-only and only affects the [Admin Panel](../admin/overview). By using it, you can arrange [Fields](./overview) next to each other horizontally.
<LightDarkImage
srcLight="https://payloadcms.com/images/docs/fields/row.png"
srcDark="https://payloadcms.com/images/docs/fields/row-dark.png"
alt="Shows a row field in the Payload Admin Panel"
caption="Admin Panel screenshot of a Row field"
/>
To add a Row Field, set the `type` to `row` in your [Field Config](./overview):
```ts
import type { Field } from 'payload'
export const MyRowField: Field = {
// ...
// highlight-start
type: 'row',
fields: [
// ...
],
// highlight-end
}
```
## Config Options
| Option | Description |
| --------------- | ------------------------------------------------------------------------------------------------------------------------- |
| **`fields`** \* | Array of field types to nest within this Row. |
| **`admin`** | Admin-specific configuration excluding `description`, `readOnly`, and `hidden`. [More details](./overview#admin-options). |
| **`custom`** | Extension point for adding custom data (e.g. for plugins) |
_\* An asterisk denotes that a property is required._
## Example
`collections/ExampleCollection.ts`
```ts
import type { CollectionConfig } from 'payload'
export const ExampleCollection: CollectionConfig = {
slug: 'example-collection',
fields: [
{
type: 'row', // required
fields: [
// required
{
name: 'label',
type: 'text',
required: true,
admin: {
width: '50%',
},
},
{
name: 'value',
type: 'text',
required: true,
admin: {
width: '50%',
},
},
],
},
],
}
```

View File

@@ -1,273 +0,0 @@
---
title: Select Field
label: Select
order: 160
desc: The Select field provides a dropdown-style interface for choosing options from a predefined list. Learn how to use Select fields, see examples and options.
keywords: select, multi-select, fields, config, configuration, documentation, Content Management System, cms, headless, javascript, node, react, nextjs
---
The Select Field provides a dropdown-style interface for choosing options from a predefined list as an enumeration.
<LightDarkImage
srcLight="https://payloadcms.com/images/docs/fields/select.png"
srcDark="https://payloadcms.com/images/docs/fields/select-dark.png"
alt="Shows a Select field in the Payload Admin Panel"
caption="Admin Panel screenshot of a Select field"
/>
To add a Select Field, set the `type` to `select` in your [Field Config](./overview):
```ts
import type { Field } from 'payload'
export const MySelectField: Field = {
// ...
// highlight-start
type: 'select',
options: [
// ...
],
// highlight-end
}
```
## Config Options
| Option | Description |
| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **`name`** \* | To be used as the property name when stored and retrieved from the database. [More details](/docs/fields/overview#field-names). |
| **`options`** \* | Array of options to allow the field to store. Can either be an array of strings, or an array of objects containing a `label` string and a `value` string. |
| **`hasMany`** | Boolean when, if set to `true`, allows this field to have many selections instead of only one. |
| **`label`** | Text used as a field label in the Admin Panel or an object with keys for each language. |
| **`unique`** | Enforce that each entry in the Collection has a unique value for this field. |
| **`validate`** | Provide a custom validation function that will be executed on both the Admin Panel and the backend. [More details](/docs/fields/overview#validation). |
| **`index`** | Build an [index](../database/indexes) for this field to produce faster queries. Set this field to `true` if your users will perform queries on this field's data often. |
| **`saveToJWT`** | If this field is top-level and nested in a config supporting [Authentication](/docs/authentication/overview), include its data in the user JWT. |
| **`hooks`** | Provide Field Hooks to control logic for this field. [More details](../hooks/fields). |
| **`access`** | Provide Field Access Control to denote what users can see and do with this field's data. [More details](../access-control/fields). |
| **`hidden`** | Restrict this field's visibility from all APIs entirely. Will still be saved to the database, but will not appear in any API or the Admin Panel. |
| **`defaultValue`** | Provide data to be used for this field's default value. [More details](/docs/fields/overview#default-values). |
| **`localized`** | Enable localization for this field. Requires [localization to be enabled](/docs/configuration/localization) in the Base config. |
| **`required`** | Require this field to have a value. |
| **`admin`** | Admin-specific configuration. See the [default field admin config](/docs/fields/overview#admin-options) for more details. |
| **`custom`** | Extension point for adding custom data (e.g. for plugins) |
| **`enumName`** | Custom enum name for this field when using SQL Database Adapter ([Postgres](/docs/database/postgres)). Auto-generated from name if not defined. |
| **`dbName`** | Custom table name (if `hasMany` set to `true`) for this field when using SQL Database Adapter ([Postgres](/docs/database/postgres)). Auto-generated from name if not defined. |
| **`interfaceName`** | Create a top level, reusable [Typescript interface](/docs/typescript/generating-types#custom-field-interfaces) & [GraphQL type](/docs/graphql/graphql-schema#custom-field-schemas). |
| **`filterOptions`** | Dynamically filter which options are available based on the user, data, etc. [More details](#filteroptions) |
| **`typescriptSchema`** | Override field type generation with providing a JSON schema |
| **`virtual`** | Provide `true` to disable field in the database, or provide a string path to [link the field with a relationship](/docs/fields/relationship#linking-virtual-fields-with-relationships). See [Virtual Fields](https://payloadcms.com/blog/learn-how-virtual-fields-can-help-solve-common-cms-challenges) |
_\* An asterisk denotes that a property is required._
<Banner type="warning">
**Important:** Option values should be strings that do not contain hyphens or
special characters due to GraphQL enumeration naming constraints. Underscores
are allowed. If you determine you need your option values to be non-strings or
contain special characters, they will be formatted accordingly before being
used as a GraphQL enum.
</Banner>
### filterOptions
Used to dynamically filter which options are available based on the current user, document data, or other criteria.
Some examples of this might include:
- Restricting options based on a user's role, e.g. admin-only options
- Displaying different options based on the value of another field, e.g. a city/state selector
The result of `filterOptions` will determine:
- Which options are displayed in the Admin Panel
- Which options can be saved to the database
To do this, use the `filterOptions` property in your [Field Config](./overview):
```ts
import type { Field } from 'payload'
export const MySelectField: Field = {
// ...
// highlight-start
type: 'select',
options: [
{
label: 'One',
value: 'one',
},
{
label: 'Two',
value: 'two',
},
{
label: 'Three',
value: 'three',
},
],
filterOptions: ({ options, data }) =>
data.disallowOption1
? options.filter(
(option) =>
(typeof option === 'string' ? options : option.value) !== 'one',
)
: options,
// highlight-end
}
```
<Banner type="warning">
**Note:** This property is similar to `filterOptions` in
[Relationship](./relationship) or [Upload](./upload) fields, except that the
return value of this function is simply an array of options, not a query
constraint.
</Banner>
## Admin Options
To customize the appearance and behavior of the Select Field in the [Admin Panel](../admin/overview), you can use the `admin` option:
```ts
import type { Field } from 'payload'
export const MySelectField: Field = {
// ...
admin: {
// highlight-line
// ...
},
}
```
The Select Field inherits all of the default admin options from the base [Field Admin Config](./overview#admin-options), plus the following additional options:
| Property | Description |
| ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
| **`isClearable`** | Set to `true` if you'd like this field to be clearable within the Admin UI. |
| **`isSortable`** | Set to `true` if you'd like this field to be sortable within the Admin UI using drag and drop. (Only works when `hasMany` is set to `true`) |
| **`placeholder`** | Define a custom text or function to replace the generic default placeholder |
## Example
`collections/ExampleCollection.ts`
```ts
import type { CollectionConfig } from 'payload'
export const ExampleCollection: CollectionConfig = {
slug: 'example-collection',
fields: [
{
name: 'selectedFeatures', // required
type: 'select', // required
hasMany: true,
admin: {
isClearable: true,
isSortable: true, // use mouse to drag and drop different values, and sort them according to your choice
},
options: [
{
label: 'Metallic Paint',
value: 'metallic_paint',
},
{
label: 'Alloy Wheels',
value: 'alloy_wheels',
},
{
label: 'Carbon Fiber Dashboard',
value: 'carbon_fiber_dashboard',
},
],
},
],
}
```
## Custom Components
### Field
#### Server Component
```tsx
import type { SelectFieldServerComponent } from 'payload'
import type React from 'react'
import { SelectField } from '@payloadcms/ui'
export const CustomSelectFieldServer: SelectFieldServerComponent = ({
clientField,
path,
schemaPath,
permissions,
}) => {
return (
<SelectField
field={clientField}
path={path}
schemaPath={schemaPath}
permissions={permissions}
/>
)
}
```
#### Client Component
```tsx
'use client'
import type { SelectFieldClientComponent } from 'payload'
import { SelectField } from '@payloadcms/ui'
import React from 'react'
export const CustomSelectFieldClient: SelectFieldClientComponent = (props) => {
return <SelectField {...props} />
}
```
### Label
#### Server Component
```tsx
import React from 'react'
import { FieldLabel } from '@payloadcms/ui'
import type { SelectFieldLabelServerComponent } from 'payload'
export const CustomSelectFieldLabelServer: SelectFieldLabelServerComponent = ({
clientField,
path,
}) => {
return (
<FieldLabel
label={clientField?.label || clientField?.name}
path={path}
required={clientField?.required}
/>
)
}
```
#### Client Component
```tsx
'use client'
import React from 'react'
import { FieldLabel } from '@payloadcms/ui'
import type { SelectFieldLabelClientComponent } from 'payload'
export const CustomSelectFieldLabelClient: SelectFieldLabelClientComponent = ({
field,
path,
}) => {
return (
<FieldLabel
label={field?.label || field?.name}
path={path}
required={field?.required}
/>
)
}
```

Some files were not shown because too many files have changed in this diff Show More