Compare commits
1 Commits
docs/migra
...
feat/graph
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9f64534213 |
29
.github/CODEOWNERS
vendored
Normal file
29
.github/CODEOWNERS
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
# Order matters. The last matching pattern takes precedence.
|
||||
# Approvals are not required currently but may be enabled in the future.
|
||||
|
||||
### Package Exports ###
|
||||
/**/exports/ @denolfe @jmikrut @DanRibbens
|
||||
|
||||
### Packages ###
|
||||
/packages/plugin-cloud*/src/ @denolfe
|
||||
/packages/email-*/src/ @denolfe
|
||||
/packages/storage-*/src/ @denolfe
|
||||
/packages/create-payload-app/src/ @denolfe
|
||||
/packages/eslint-*/ @denolfe @AlessioGr
|
||||
|
||||
### Templates ###
|
||||
/templates/_data/ @denolfe
|
||||
/templates/_template/ @denolfe
|
||||
|
||||
### Build Files ###
|
||||
/tsconfig.json @denolfe
|
||||
/**/tsconfig*.json @denolfe
|
||||
/jest.config.js @denolfe
|
||||
/**/jest.config.js @denolfe
|
||||
|
||||
### Root ###
|
||||
/package.json @denolfe
|
||||
/scripts/ @denolfe
|
||||
/.husky/ @denolfe
|
||||
/.vscode/ @denolfe @AlessioGr
|
||||
/.github/ @denolfe
|
||||
6
.github/ISSUE_TEMPLATE/1.bug_report_v3.yml
vendored
6
.github/ISSUE_TEMPLATE/1.bug_report_v3.yml
vendored
@@ -1,6 +1,6 @@
|
||||
name: Functionality Bug
|
||||
description: '[REPRODUCTION REQUIRED] - Create a bug report'
|
||||
labels: ['status: needs-triage', 'validate-reproduction']
|
||||
labels: ['status: needs-triage', 'v3', 'validate-reproduction']
|
||||
body:
|
||||
- type: textarea
|
||||
attributes:
|
||||
@@ -14,7 +14,7 @@ body:
|
||||
label: Link to the code that reproduces this issue
|
||||
description: >-
|
||||
_REQUIRED_: Please provide a link to your reproduction. Note, if the URL is invalid (404 or a private repository), we may close the issue.
|
||||
Either use `pnpx create-payload-app@latest -t blank` then push to a repo or follow the [reproduction-guide](https://github.com/payloadcms/payload/blob/main/.github/reproduction-guide.md) for more information.
|
||||
Either use `npx create-payload-app@beta -t blank` then push to a repo or follow the [reproduction-guide](https://github.com/payloadcms/payload/blob/main/.github/reproduction-guide.md) for more information.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
@@ -57,7 +57,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` (>= beta.92) _or_ Payload, Node.js, and Next.js versions.
|
||||
render: text
|
||||
placeholder: |
|
||||
Payload:
|
||||
|
||||
2
.github/ISSUE_TEMPLATE/2.design_issue.yml
vendored
2
.github/ISSUE_TEMPLATE/2.design_issue.yml
vendored
@@ -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` (>= beta.92) _or_ Payload, Node.js, and Next.js versions.
|
||||
render: text
|
||||
placeholder: |
|
||||
Payload:
|
||||
|
||||
27
.github/actions/release-commenter/LICENSE
vendored
27
.github/actions/release-commenter/LICENSE
vendored
@@ -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
|
||||
@@ -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",
|
||||
@@ -19,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",
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
3648
.github/actions/release-commenter/pnpm-lock.yaml
generated
vendored
3648
.github/actions/release-commenter/pnpm-lock.yaml
generated
vendored
File diff suppressed because it is too large
Load Diff
48
.github/actions/setup/action.yml
vendored
48
.github/actions/setup/action.yml
vendored
@@ -1,33 +1,15 @@
|
||||
name: Setup node and pnpm
|
||||
description: |
|
||||
Configures Node, pnpm, cache, performs pnpm install
|
||||
description: Configure the Node.js and pnpm versions
|
||||
|
||||
inputs:
|
||||
node-version:
|
||||
description: Node.js version
|
||||
description: 'The Node.js version to use'
|
||||
required: true
|
||||
default: 23.11.0
|
||||
default: 22.6.2
|
||||
pnpm-version:
|
||||
description: Pnpm version
|
||||
description: 'The pnpm version to use'
|
||||
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 for the pnpm install cache
|
||||
default: pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||
|
||||
outputs:
|
||||
pnpm-store-path:
|
||||
description: The resolved pnpm store path
|
||||
pnpm-install-cache-key:
|
||||
description: The cache key used for pnpm install cache
|
||||
|
||||
runs:
|
||||
using: composite
|
||||
@@ -48,29 +30,19 @@ runs:
|
||||
version: ${{ inputs.pnpm-version }}
|
||||
run_install: false
|
||||
|
||||
- name: Get pnpm store path
|
||||
- name: Get pnpm store directory
|
||||
shell: bash
|
||||
run: |
|
||||
STORE_PATH=$(pnpm store path --silent)
|
||||
echo "STORE_PATH=$STORE_PATH" >> $GITHUB_ENV
|
||||
echo "Pnpm store path resolved to: $STORE_PATH"
|
||||
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
|
||||
|
||||
- name: Restore pnpm install cache
|
||||
if: ${{ inputs.pnpm-restore-cache == 'true' }}
|
||||
- name: Setup pnpm cache
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ${{ env.STORE_PATH }}
|
||||
key: ${{ inputs.pnpm-install-cache-key }}
|
||||
key: pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||
restore-keys: |
|
||||
pnpm-store-${{ inputs.pnpm-version }}-
|
||||
pnpm-store-
|
||||
pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||
|
||||
- name: Run pnpm install
|
||||
if: ${{ inputs.pnpm-run-install == 'true' }}
|
||||
shell: bash
|
||||
- shell: bash
|
||||
run: pnpm install
|
||||
|
||||
# Set the cache key output
|
||||
- run: |
|
||||
echo "pnpm-install-cache-key=${{ inputs.pnpm-install-cache-key }}" >> $GITHUB_ENV
|
||||
shell: bash
|
||||
|
||||
3
.github/actions/triage/package.json
vendored
3
.github/actions/triage/package.json
vendored
@@ -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
3648
.github/actions/triage/pnpm-lock.yaml
generated
vendored
File diff suppressed because it is too large
Load Diff
2
.github/comments/invalid-reproduction.md
vendored
2
.github/comments/invalid-reproduction.md
vendored
@@ -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.
|
||||
|
||||
|
||||
9
.github/dependabot.yml
vendored
9
.github/dependabot.yml
vendored
@@ -21,7 +21,6 @@ updates:
|
||||
- package-ecosystem: npm
|
||||
directory: /
|
||||
target-branch: main
|
||||
open-pull-requests-limit: 0 # Only allow security updates
|
||||
schedule:
|
||||
interval: weekly
|
||||
day: sunday
|
||||
@@ -39,6 +38,8 @@ updates:
|
||||
- patch
|
||||
patterns:
|
||||
- '*'
|
||||
exclude-patterns:
|
||||
- 'drizzle*'
|
||||
dev-deps:
|
||||
dependency-type: development
|
||||
update-types:
|
||||
@@ -46,11 +47,13 @@ updates:
|
||||
- patch
|
||||
patterns:
|
||||
- '*'
|
||||
exclude-patterns:
|
||||
- 'drizzle*'
|
||||
|
||||
# Only bump patch versions for 2.x
|
||||
- package-ecosystem: npm
|
||||
directory: /
|
||||
target-branch: 2.x
|
||||
open-pull-requests-limit: 0 # Only allow security updates
|
||||
schedule:
|
||||
interval: weekly
|
||||
day: sunday
|
||||
@@ -67,3 +70,5 @@ updates:
|
||||
- patch
|
||||
patterns:
|
||||
- '*'
|
||||
exclude-patterns:
|
||||
- 'drizzle*'
|
||||
|
||||
114
.github/pnpm-lock.yaml
generated
vendored
114
.github/pnpm-lock.yaml
generated
vendored
@@ -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:
|
||||
|
||||
4
.github/reproduction-guide.md
vendored
4
.github/reproduction-guide.md
vendored
@@ -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/packages/payload/src/assets/images/github/int-debug.png" />
|
||||
<img src="https://raw.githubusercontent.com/payloadcms/payload/main/packages/payload/src/admin/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/packages/payload/src/assets/images/github/e2e-debug.png" />
|
||||
<img src="https://raw.githubusercontent.com/payloadcms/payload/main/packages/payload/src/admin/assets/images/github/e2e-debug.png" />
|
||||
|
||||
#### Notes
|
||||
|
||||
|
||||
24
.github/workflows/dispatch-event.yml
vendored
24
.github/workflows/dispatch-event.yml
vendored
@@ -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) }}}'
|
||||
12
.github/workflows/label-on-change.yml
vendored
12
.github/workflows/label-on-change.yml
vendored
@@ -13,7 +13,7 @@ on:
|
||||
|
||||
jobs:
|
||||
on-labeled-ensure-one-status:
|
||||
runs-on: ubuntu-24.04
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
issues: write
|
||||
# Only run on issue labeled and if label starts with 'status:'
|
||||
@@ -49,7 +49,7 @@ jobs:
|
||||
}
|
||||
|
||||
on-issue-close:
|
||||
runs-on: ubuntu-24.04
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
issues: write
|
||||
if: github.event.action == 'closed'
|
||||
@@ -82,7 +82,7 @@ jobs:
|
||||
}
|
||||
|
||||
on-issue-reopen:
|
||||
runs-on: ubuntu-24.04
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
issues: write
|
||||
if: github.event.action == 'reopened'
|
||||
@@ -93,7 +93,7 @@ jobs:
|
||||
labels: 'status: needs-triage'
|
||||
|
||||
on-issue-assigned:
|
||||
runs-on: ubuntu-24.04
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
issues: write
|
||||
if: >
|
||||
@@ -106,11 +106,11 @@ jobs:
|
||||
labels: 'status: needs-triage'
|
||||
|
||||
# on-pr-merge:
|
||||
# runs-on: ubuntu-24.04
|
||||
# runs-on: ubuntu-latest
|
||||
# if: github.event.pull_request.merged == true
|
||||
# steps:
|
||||
|
||||
# on-pr-close:
|
||||
# runs-on: ubuntu-24.04
|
||||
# runs-on: ubuntu-latest
|
||||
# if: github.event_name == 'pull_request_target' && github.event.pull_request.merged == false
|
||||
# steps:
|
||||
|
||||
8
.github/workflows/lock-issues.yml
vendored
8
.github/workflows/lock-issues.yml
vendored
@@ -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:
|
||||
@@ -11,13 +11,13 @@ permissions:
|
||||
|
||||
jobs:
|
||||
lock_issues:
|
||||
runs-on: ubuntu-24.04
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Lock issues
|
||||
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: >
|
||||
|
||||
313
.github/workflows/main.yml
vendored
313
.github/workflows/main.yml
vendored
@@ -16,19 +16,18 @@ concurrency:
|
||||
cancel-in-progress: true
|
||||
|
||||
env:
|
||||
NODE_VERSION: 23.11.0
|
||||
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:
|
||||
changes:
|
||||
runs-on: ubuntu-24.04
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
pull-requests: read
|
||||
outputs:
|
||||
needs_build: ${{ steps.filter.outputs.needs_build }}
|
||||
needs_tests: ${{ steps.filter.outputs.needs_tests }}
|
||||
templates: ${{ steps.filter.outputs.templates }}
|
||||
steps:
|
||||
# https://github.com/actions/virtual-environments/issues/1187
|
||||
@@ -36,91 +35,146 @@ jobs:
|
||||
run: sudo ethtool -K eth0 tx off rx off
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 25
|
||||
- uses: dorny/paths-filter@v3
|
||||
id: filter
|
||||
with:
|
||||
filters: |
|
||||
needs_build:
|
||||
- '.github/workflows/main.yml'
|
||||
- '.github/workflows/**'
|
||||
- 'packages/**'
|
||||
- 'test/**'
|
||||
- 'pnpm-lock.yaml'
|
||||
- 'package.json'
|
||||
- 'templates/**'
|
||||
needs_tests:
|
||||
- '.github/workflows/main.yml'
|
||||
- 'packages/**'
|
||||
- 'test/**'
|
||||
- 'pnpm-lock.yaml'
|
||||
- 'package.json'
|
||||
templates:
|
||||
- 'templates/**'
|
||||
- name: Log all filter results
|
||||
run: |
|
||||
echo "needs_build: ${{ steps.filter.outputs.needs_build }}"
|
||||
echo "needs_tests: ${{ steps.filter.outputs.needs_tests }}"
|
||||
echo "templates: ${{ steps.filter.outputs.templates }}"
|
||||
|
||||
lint:
|
||||
runs-on: ubuntu-24.04
|
||||
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-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Node setup
|
||||
uses: ./.github/actions/setup
|
||||
# https://github.com/actions/virtual-environments/issues/1187
|
||||
- name: tune linux network
|
||||
run: sudo ethtool -K eth0 tx off rx off
|
||||
|
||||
- name: Setup Node@${{ env.NODE_VERSION }}
|
||||
uses: actions/setup-node@v4
|
||||
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: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: ${{ env.PNPM_VERSION }}
|
||||
run_install: false
|
||||
|
||||
- name: Get pnpm store directory
|
||||
shell: bash
|
||||
run: |
|
||||
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
|
||||
|
||||
- name: Setup pnpm cache
|
||||
uses: actions/cache@v4
|
||||
timeout-minutes: 720
|
||||
with:
|
||||
path: ${{ env.STORE_PATH }}
|
||||
key: pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||
restore-keys: |
|
||||
pnpm-store-
|
||||
pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||
|
||||
- run: pnpm install
|
||||
- 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
|
||||
if: ${{ needs.changes.outputs.needs_build == 'true' }}
|
||||
runs-on: ubuntu-24.04
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 25
|
||||
|
||||
- name: Node setup
|
||||
uses: ./.github/actions/setup
|
||||
# https://github.com/actions/virtual-environments/issues/1187
|
||||
- name: tune linux network
|
||||
run: sudo ethtool -K eth0 tx off rx off
|
||||
|
||||
- name: Setup Node@${{ env.NODE_VERSION }}
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
pnpm-version: ${{ env.PNPM_VERSION }}
|
||||
pnpm-install-cache-key: pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: ${{ env.PNPM_VERSION }}
|
||||
run_install: false
|
||||
|
||||
- name: Get pnpm store directory
|
||||
shell: bash
|
||||
run: |
|
||||
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
|
||||
|
||||
- name: Setup pnpm cache
|
||||
uses: actions/cache@v4
|
||||
timeout-minutes: 720
|
||||
with:
|
||||
path: ${{ env.STORE_PATH }}
|
||||
key: pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||
restore-keys: |
|
||||
pnpm-store-
|
||||
pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||
|
||||
- run: pnpm install
|
||||
- run: pnpm run build:all
|
||||
env:
|
||||
DO_NOT_TRACK: 1 # Disable Turbopack telemetry
|
||||
|
||||
- name: Cache build
|
||||
uses: actions/cache@v4
|
||||
timeout-minutes: 10
|
||||
with:
|
||||
path: ./*
|
||||
key: ${{ github.sha }}-${{ github.run_number }}
|
||||
|
||||
tests-unit:
|
||||
runs-on: ubuntu-24.04
|
||||
needs: [changes, build]
|
||||
if: ${{ needs.changes.outputs.needs_tests == 'true' }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
runs-on: ubuntu-latest
|
||||
needs: build
|
||||
|
||||
- name: Node setup
|
||||
uses: ./.github/actions/setup
|
||||
steps:
|
||||
# https://github.com/actions/virtual-environments/issues/1187
|
||||
- name: tune linux network
|
||||
run: sudo ethtool -K eth0 tx off rx off
|
||||
|
||||
- name: Setup Node@${{ env.NODE_VERSION }}
|
||||
uses: actions/setup-node@v4
|
||||
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: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: ${{ env.PNPM_VERSION }}
|
||||
run_install: false
|
||||
|
||||
- name: Restore build
|
||||
uses: actions/cache@v4
|
||||
timeout-minutes: 10
|
||||
with:
|
||||
path: ./*
|
||||
key: ${{ github.sha }}-${{ github.run_number }}
|
||||
@@ -130,37 +184,9 @@ jobs:
|
||||
env:
|
||||
NODE_OPTIONS: --max-old-space-size=8096
|
||||
|
||||
tests-types:
|
||||
runs-on: ubuntu-24.04
|
||||
needs: [changes, build]
|
||||
if: ${{ needs.changes.outputs.needs_tests == 'true' }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- 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
|
||||
with:
|
||||
path: ./*
|
||||
key: ${{ github.sha }}-${{ github.run_number }}
|
||||
|
||||
- name: Types Tests
|
||||
run: pnpm test:types --target '>=5.7'
|
||||
env:
|
||||
NODE_OPTIONS: --max-old-space-size=8096
|
||||
|
||||
tests-int:
|
||||
runs-on: ubuntu-24.04
|
||||
needs: [changes, build]
|
||||
if: ${{ needs.changes.outputs.needs_tests == 'true' }}
|
||||
runs-on: ubuntu-latest
|
||||
needs: build
|
||||
name: int-${{ matrix.database }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
@@ -172,7 +198,6 @@ jobs:
|
||||
- postgres-uuid
|
||||
- supabase
|
||||
- sqlite
|
||||
- sqlite-uuid
|
||||
env:
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_PASSWORD: postgres
|
||||
@@ -197,21 +222,24 @@ jobs:
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 25
|
||||
# https://github.com/actions/virtual-environments/issues/1187
|
||||
- name: tune linux network
|
||||
run: sudo ethtool -K eth0 tx off rx off
|
||||
|
||||
- name: Node setup
|
||||
uses: ./.github/actions/setup
|
||||
- name: Setup Node@${{ env.NODE_VERSION }}
|
||||
uses: actions/setup-node@v4
|
||||
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
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
path: ./*
|
||||
key: ${{ github.sha }}-${{ github.run_number }}
|
||||
version: ${{ env.PNPM_VERSION }}
|
||||
run_install: false
|
||||
|
||||
- run: pnpm install
|
||||
|
||||
- name: Start LocalStack
|
||||
run: pnpm docker:start
|
||||
@@ -253,9 +281,8 @@ jobs:
|
||||
POSTGRES_URL: ${{ env.POSTGRES_URL }}
|
||||
|
||||
tests-e2e:
|
||||
runs-on: ubuntu-24.04
|
||||
needs: [changes, build]
|
||||
if: ${{ needs.changes.outputs.needs_tests == 'true' }}
|
||||
runs-on: ubuntu-latest
|
||||
needs: build
|
||||
name: e2e-${{ matrix.suite }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
@@ -264,74 +291,57 @@ 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-nested-docs
|
||||
- plugin-seo
|
||||
- sort
|
||||
- versions
|
||||
- uploads
|
||||
env:
|
||||
SUITE_NAME: ${{ matrix.suite }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
# https://github.com/actions/virtual-environments/issues/1187
|
||||
- name: tune linux network
|
||||
run: sudo ethtool -K eth0 tx off rx off
|
||||
|
||||
- name: Node setup
|
||||
uses: ./.github/actions/setup
|
||||
- name: Setup Node@${{ env.NODE_VERSION }}
|
||||
uses: actions/setup-node@v4
|
||||
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: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: ${{ env.PNPM_VERSION }}
|
||||
run_install: false
|
||||
|
||||
- name: Restore build
|
||||
uses: actions/cache@v4
|
||||
timeout-minutes: 10
|
||||
with:
|
||||
path: ./*
|
||||
key: ${{ github.sha }}-${{ github.run_number }}
|
||||
@@ -385,7 +395,7 @@ jobs:
|
||||
|
||||
# Build listed templates with packed local packages
|
||||
build-templates:
|
||||
runs-on: ubuntu-24.04
|
||||
runs-on: ubuntu-latest
|
||||
needs: build
|
||||
strategy:
|
||||
matrix:
|
||||
@@ -404,8 +414,6 @@ jobs:
|
||||
- template: with-vercel-postgres
|
||||
database: postgres
|
||||
|
||||
- template: plugin
|
||||
|
||||
# Re-enable once PG conncection is figured out
|
||||
# - template: with-vercel-website
|
||||
# database: postgres
|
||||
@@ -416,22 +424,26 @@ jobs:
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_PASSWORD: postgres
|
||||
POSTGRES_DB: payloadtests
|
||||
MONGODB_VERSION: 6.0
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
# https://github.com/actions/virtual-environments/issues/1187
|
||||
- name: tune linux network
|
||||
run: sudo ethtool -K eth0 tx off rx off
|
||||
|
||||
- name: Node setup
|
||||
uses: ./.github/actions/setup
|
||||
- name: Setup Node@${{ env.NODE_VERSION }}
|
||||
uses: actions/setup-node@v4
|
||||
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: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: ${{ env.PNPM_VERSION }}
|
||||
run_install: false
|
||||
|
||||
- name: Restore build
|
||||
uses: actions/cache@v4
|
||||
timeout-minutes: 10
|
||||
with:
|
||||
path: ./*
|
||||
key: ${{ github.sha }}-${{ github.run_number }}
|
||||
@@ -456,43 +468,39 @@ 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
|
||||
pnpm runts scripts/build-template-with-local-pkgs.ts ${{ matrix.template }} $POSTGRES_URL
|
||||
|
||||
tests-type-generation:
|
||||
runs-on: ubuntu-24.04
|
||||
needs: [changes, build]
|
||||
if: ${{ needs.changes.outputs.needs_tests == 'true' }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
runs-on: ubuntu-latest
|
||||
needs: build
|
||||
|
||||
- name: Node setup
|
||||
uses: ./.github/actions/setup
|
||||
steps:
|
||||
# https://github.com/actions/virtual-environments/issues/1187
|
||||
- name: tune linux network
|
||||
run: sudo ethtool -K eth0 tx off rx off
|
||||
|
||||
- name: Setup Node@${{ env.NODE_VERSION }}
|
||||
uses: actions/setup-node@v4
|
||||
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: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: ${{ env.PNPM_VERSION }}
|
||||
run_install: false
|
||||
|
||||
- name: Restore build
|
||||
uses: actions/cache@v4
|
||||
timeout-minutes: 10
|
||||
with:
|
||||
path: ./*
|
||||
key: ${{ github.sha }}-${{ github.run_number }}
|
||||
@@ -506,16 +514,13 @@ jobs:
|
||||
all-green:
|
||||
name: All Green
|
||||
if: always()
|
||||
runs-on: ubuntu-24.04
|
||||
runs-on: ubuntu-latest
|
||||
needs:
|
||||
- lint
|
||||
- build
|
||||
- build-templates
|
||||
- tests-unit
|
||||
- tests-int
|
||||
- tests-e2e
|
||||
- tests-types
|
||||
- tests-type-generation
|
||||
|
||||
steps:
|
||||
- if: ${{ always() && (contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled')) }}
|
||||
@@ -523,7 +528,7 @@ jobs:
|
||||
|
||||
publish-canary:
|
||||
name: Publish Canary
|
||||
runs-on: ubuntu-24.04
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ needs.all-green.result == 'success' && github.ref_name == 'main' }}
|
||||
needs:
|
||||
- all-green
|
||||
|
||||
121
.github/workflows/post-release-templates.yml
vendored
121
.github/workflows/post-release-templates.yml
vendored
@@ -1,121 +0,0 @@
|
||||
name: post-release-templates
|
||||
|
||||
on:
|
||||
release:
|
||||
types:
|
||||
- published
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
NODE_VERSION: 23.11.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
|
||||
pull-requests: write
|
||||
env:
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_PASSWORD: postgres
|
||||
POSTGRES_DB: payloadtests
|
||||
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: Start PostgreSQL
|
||||
uses: CasperWA/postgresql-action@v1.2
|
||||
with:
|
||||
postgresql version: '14' # See https://hub.docker.com/_/postgres for available versions
|
||||
postgresql db: ${{ env.POSTGRES_DB }}
|
||||
postgresql user: ${{ env.POSTGRES_USER }}
|
||||
postgresql password: ${{ env.POSTGRES_PASSWORD }}
|
||||
|
||||
- name: Wait for PostgreSQL
|
||||
run: sleep 30
|
||||
|
||||
- name: Configure PostgreSQL
|
||||
run: |
|
||||
psql "postgresql://$POSTGRES_USER:$POSTGRES_PASSWORD@localhost:5432/$POSTGRES_DB" -c "CREATE ROLE runner SUPERUSER LOGIN;"
|
||||
psql "postgresql://$POSTGRES_USER:$POSTGRES_PASSWORD@localhost:5432/$POSTGRES_DB" -c "SELECT version();"
|
||||
echo "POSTGRES_URL=postgresql://$POSTGRES_USER:$POSTGRES_PASSWORD@localhost:5432/$POSTGRES_DB" >> $GITHUB_ENV
|
||||
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
|
||||
with:
|
||||
mongodb-version: 6.0
|
||||
|
||||
- name: Update template lockfiles and migrations
|
||||
run: pnpm script:gen-templates
|
||||
|
||||
- name: Commit and push changes
|
||||
id: commit
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
set -ex
|
||||
git config --global user.name "github-actions[bot]"
|
||||
git config --global user.email "github-actions[bot]@users.noreply.github.com"
|
||||
|
||||
git diff --name-only
|
||||
|
||||
export BRANCH_NAME=templates/bump-${{ needs.wait_for_release.outputs.release_tag }}-$(date +%s)
|
||||
echo "branch=$BRANCH_NAME" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Create pull request
|
||||
uses: peter-evans/create-pull-request@v7
|
||||
with:
|
||||
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 }}'
|
||||
branch: ${{ steps.commit.outputs.branch }}
|
||||
base: main
|
||||
assignees: ${{ github.actor }}
|
||||
title: 'templates: bump for ${{ needs.wait_for_release.outputs.release_tag }}'
|
||||
body: |
|
||||
🤖 Automated bump of templates for ${{ needs.wait_for_release.outputs.release_tag }}
|
||||
|
||||
Triggered by user: @${{ github.actor }}
|
||||
77
.github/workflows/post-release.yml
vendored
77
.github/workflows/post-release.yml
vendored
@@ -12,17 +12,19 @@ on:
|
||||
default: ''
|
||||
|
||||
env:
|
||||
NODE_VERSION: 23.11.0
|
||||
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:
|
||||
post_release:
|
||||
runs-on: ubuntu-24.04
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.event_name != 'workflow_dispatch' }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: ./.github/actions/release-commenter
|
||||
continue-on-error: true
|
||||
env:
|
||||
@@ -37,16 +39,69 @@ jobs:
|
||||
comment-template: |
|
||||
🚀 This is included in version {release_link}
|
||||
|
||||
github-releases-to-discord:
|
||||
runs-on: ubuntu-24.04
|
||||
if: ${{ github.event_name != 'workflow_dispatch' }}
|
||||
update_templates:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Github Releases To Discord
|
||||
uses: SethCohen/github-releases-to-discord@v1.16.2
|
||||
- name: Setup
|
||||
uses: ./.github/actions/setup
|
||||
with:
|
||||
webhook_url: ${{ secrets.DISCORD_RELEASES_WEBHOOK_URL }}
|
||||
color: '16777215'
|
||||
username: 'Payload Releases'
|
||||
avatar_url: 'https://l4wlsi8vxy8hre4v.public.blob.vercel-storage.com/discord-bot-logo.png'
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
pnpm-version: ${{ env.PNPM_VERSION }}
|
||||
|
||||
- name: Update template lockfiles and migrations
|
||||
run: pnpm script:gen-templates
|
||||
|
||||
- name: Determine Release Tag
|
||||
id: determine_tag
|
||||
run: |
|
||||
if [ "${{ github.event.inputs.tag }}" != "" ]; then
|
||||
echo "Using tag from input: ${{ github.event.inputs.tag }}"
|
||||
echo "release_tag=${{ github.event.inputs.tag }}" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
echo "Using tag from release event: ${{ github.event.release.tag_name }}"
|
||||
echo "release_tag=${{ github.event.release.tag_name }}" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
|
||||
- name: Commit and push changes
|
||||
id: commit
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
set -ex
|
||||
git config --global user.name "github-actions[bot]"
|
||||
git config --global user.email "github-actions[bot]@users.noreply.github.com"
|
||||
export BRANCH_NAME=chore/templates-${{ steps.determine_tag.outputs.release_tag }}
|
||||
git checkout -b $BRANCH_NAME
|
||||
git add -A
|
||||
|
||||
# If no files have changed, exit early with success
|
||||
git diff --cached --quiet --exit-code && exit 0
|
||||
|
||||
git commit -m "chore(templates): bump lockfiles after ${{ steps.determine_tag.outputs.release_tag }}"
|
||||
git push origin $BRANCH_NAME
|
||||
echo "committed=true" >> "$GITHUB_OUTPUT"
|
||||
echo "branch=$BRANCH_NAME" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Debug Branches
|
||||
run: |
|
||||
echo "Target Commitish: ${{ github.event.release.target_commitish }}"
|
||||
echo "Branch: ${{ steps.commit.outputs.branch }}"
|
||||
echo "Ref: ${{ github.ref }}"
|
||||
|
||||
- name: Create pull request
|
||||
uses: peter-evans/create-pull-request@v7
|
||||
if: steps.commit.outputs.committed == 'true'
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
labels: 'area: templates'
|
||||
author: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
|
||||
commit-message: 'Automated update after release'
|
||||
branch: ${{ steps.commit.outputs.branch }}
|
||||
base: ${{ github.event_name != 'workflow_dispatch' && github.event.release.target_commitish || github.ref }}
|
||||
title: 'chore(templates): bump lockfiles after ${{ steps.determine_tag.outputs.release_tag }}'
|
||||
body: 'Automated bump of template lockfiles after release ${{ steps.determine_tag.outputs.release_tag }}'
|
||||
|
||||
18
.github/workflows/pr-title.yml
vendored
18
.github/workflows/pr-title.yml
vendored
@@ -1,7 +1,7 @@
|
||||
name: pr-title
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
pull_request:
|
||||
types:
|
||||
- opened
|
||||
- edited
|
||||
@@ -12,7 +12,7 @@ permissions:
|
||||
jobs:
|
||||
main:
|
||||
name: lint-pr-title
|
||||
runs-on: ubuntu-24.04
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: amannn/action-semantic-pull-request@v5
|
||||
id: lint_pr_title
|
||||
@@ -24,15 +24,14 @@ jobs:
|
||||
chore
|
||||
ci
|
||||
docs
|
||||
examples
|
||||
feat
|
||||
fix
|
||||
perf
|
||||
refactor
|
||||
revert
|
||||
style
|
||||
templates
|
||||
test
|
||||
types
|
||||
scopes: |
|
||||
cpa
|
||||
db-\*
|
||||
@@ -41,9 +40,7 @@ jobs:
|
||||
db-vercel-postgres
|
||||
db-sqlite
|
||||
drizzle
|
||||
email-\*
|
||||
email-nodemailer
|
||||
email-resend
|
||||
eslint
|
||||
graphql
|
||||
live-preview
|
||||
@@ -53,8 +50,6 @@ jobs:
|
||||
plugin-cloud
|
||||
plugin-cloud-storage
|
||||
plugin-form-builder
|
||||
plugin-import-export
|
||||
plugin-multi-tenant
|
||||
plugin-nested-docs
|
||||
plugin-redirects
|
||||
plugin-search
|
||||
@@ -111,7 +106,7 @@ jobs:
|
||||
|
||||
label-pr-on-open:
|
||||
name: label-pr-on-open
|
||||
runs-on: ubuntu-24.04
|
||||
runs-on: ubuntu-latest
|
||||
if: github.event.action == 'opened'
|
||||
steps:
|
||||
- name: Tag with 2.x branch with v2
|
||||
@@ -119,3 +114,8 @@ jobs:
|
||||
uses: actions-ecosystem/action-add-labels@v1
|
||||
with:
|
||||
labels: v2
|
||||
- name: Tag with main branch with v3
|
||||
if: github.event.pull_request.base.ref == 'main'
|
||||
uses: actions-ecosystem/action-add-labels@v1
|
||||
with:
|
||||
labels: v3
|
||||
|
||||
48
.github/workflows/publish-prerelease.yml
vendored
48
.github/workflows/publish-prerelease.yml
vendored
@@ -1,48 +0,0 @@
|
||||
name: publish-prerelease
|
||||
|
||||
on:
|
||||
schedule:
|
||||
# Run nightly at 10pm EST
|
||||
- cron: '0 3 * * *'
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
NODE_VERSION: 23.11.0
|
||||
PNPM_VERSION: 9.7.1
|
||||
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
|
||||
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: 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
|
||||
36
.github/workflows/release-canary.yml
vendored
Normal file
36
.github/workflows/release-canary.yml
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
name: release-canary
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
branches:
|
||||
- main
|
||||
|
||||
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:
|
||||
permissions:
|
||||
id-token: write
|
||||
runs-on: ubuntu-latest
|
||||
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
|
||||
43
.github/workflows/stale.yml
vendored
43
.github/workflows/stale.yml
vendored
@@ -1,21 +1,11 @@
|
||||
name: stale
|
||||
|
||||
on:
|
||||
schedule:
|
||||
# Run nightly at 1am EST, staggered with lock-issues workflow
|
||||
- cron: '0 6 * * *'
|
||||
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
dry-run:
|
||||
description: Run the stale action in debug-only mode
|
||||
default: false
|
||||
required: false
|
||||
type: boolean
|
||||
|
||||
jobs:
|
||||
stale:
|
||||
runs-on: ubuntu-24.04
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
issues: write
|
||||
pull-requests: write
|
||||
@@ -23,29 +13,30 @@ jobs:
|
||||
- uses: actions/stale@v9
|
||||
id: stale
|
||||
with:
|
||||
debug-only: ${{ inputs.dry-run || false }}
|
||||
|
||||
days-before-stale: 30
|
||||
days-before-close: -1 # Disable closing
|
||||
debug-only: true
|
||||
days-before-stale: 90
|
||||
days-before-close: 7
|
||||
ascending: true
|
||||
operations-per-run: 300
|
||||
exempt-all-assignees: true
|
||||
|
||||
# Ignore all assigned
|
||||
exempt-all-assignees: false
|
||||
|
||||
# Issues
|
||||
stale-issue-label: stale
|
||||
exempt-issue-labels: 'prioritized,keep,created-by: Payload team,created-by: Contributor,status: verified'
|
||||
stale-issue-message: ''
|
||||
|
||||
close-issue-message: |
|
||||
stale-issue-label: 'stale'
|
||||
exempt-issue-labels: 'blocked,must,should,keep,created-by: Payload team,created-by: Contributor'
|
||||
stale-issue-message: >
|
||||
This issue has been marked as stale due to lack of activity. To keep the ticket 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.
|
||||
|
||||
# Pull Requests
|
||||
stale-pr-label: stale
|
||||
exempt-pr-labels: 'prioritized,keep,created-by: Payload team,created-by: Contributor'
|
||||
stale-pr-message: ''
|
||||
close-pr-message: |
|
||||
stale-pr-label: 'stale'
|
||||
exempt-pr-labels: 'blocked,must,should,keep,created-by: Payload team,created-by: Contributor'
|
||||
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.
|
||||
|
||||
# TODO: Add a step to notify team
|
||||
- name: Print outputs
|
||||
run: echo ${{ format('{0},{1}', toJSON(steps.stale.outputs.staled-issues-prs), toJSON(steps.stale.outputs.closed-issues-prs)) }}
|
||||
|
||||
10
.github/workflows/triage.yml
vendored
10
.github/workflows/triage.yml
vendored
@@ -1,7 +1,7 @@
|
||||
name: triage
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
pull_request:
|
||||
types:
|
||||
- opened
|
||||
issues:
|
||||
@@ -18,7 +18,7 @@ permissions:
|
||||
|
||||
jobs:
|
||||
debug-context:
|
||||
runs-on: ubuntu-24.04
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: View context attributes
|
||||
uses: actions/github-script@v7
|
||||
@@ -27,10 +27,11 @@ jobs:
|
||||
|
||||
label-created-by:
|
||||
name: label-on-open
|
||||
runs-on: ubuntu-24.04
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Tag with 'created-by'
|
||||
uses: actions/github-script@v7
|
||||
if: github.event.action == 'opened'
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
script: |
|
||||
@@ -88,11 +89,10 @@ jobs:
|
||||
triage:
|
||||
name: initial-triage
|
||||
if: github.event_name == 'issues'
|
||||
runs-on: ubuntu-24.04
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.base.ref }}
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
- uses: ./.github/actions/triage
|
||||
with:
|
||||
|
||||
31
.github/workflows/wait-until-package-version.sh
vendored
31
.github/workflows/wait-until-package-version.sh
vendored
@@ -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
|
||||
9
.gitignore
vendored
9
.gitignore
vendored
@@ -3,7 +3,6 @@ package-lock.json
|
||||
dist
|
||||
/.idea/*
|
||||
!/.idea/runConfigurations
|
||||
/.idea/runConfigurations/_template*
|
||||
!/.idea/payload.iml
|
||||
|
||||
# Custom actions
|
||||
@@ -307,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
|
||||
@@ -320,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
3
.idea/payload.iml
generated
@@ -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>
|
||||
|
||||
9
.idea/runConfigurations/_template__of_JavaScriptTestRunnerJest.xml
generated
Normal file
9
.idea/runConfigurations/_template__of_JavaScriptTestRunnerJest.xml
generated
Normal 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>
|
||||
@@ -1 +1 @@
|
||||
v23.11.0
|
||||
v22.6.0
|
||||
|
||||
1
.npmrc
1
.npmrc
@@ -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=''
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
**/dist/**
|
||||
**/node_modules
|
||||
**/temp
|
||||
**/docs/**
|
||||
tsconfig.json
|
||||
packages/payload/*.js
|
||||
packages/payload/*.d.ts
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
pnpm 9.7.1
|
||||
nodejs 23.11.0
|
||||
nodejs 22.6.0
|
||||
|
||||
14
.vscode/launch.json
vendored
14
.vscode/launch.json
vendored
@@ -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}",
|
||||
|
||||
46
.vscode/settings.json
vendored
46
.vscode/settings.json
vendored
@@ -1,12 +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",
|
||||
"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 },
|
||||
@@ -15,14 +43,14 @@
|
||||
"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"
|
||||
}
|
||||
}
|
||||
|
||||
4463
CHANGELOG.md
Normal file
4463
CHANGELOG.md
Normal file
File diff suppressed because it is too large
Load Diff
@@ -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.
|
||||
|
||||
|
||||
@@ -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/packages/payload/src/assets/images/github/int-debug.png" />
|
||||
<img src="https://raw.githubusercontent.com/payloadcms/payload/main/src/admin/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/packages/payload/src/assets/images/github/e2e-debug.png" />
|
||||
<img src="https://raw.githubusercontent.com/payloadcms/payload/main/src/admin/assets/images/github/e2e-debug.png" />
|
||||
|
||||
#### Notes
|
||||
|
||||
|
||||
22
LICENSE.md
22
LICENSE.md
@@ -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.
|
||||
48
README.md
48
README.md
@@ -1,4 +1,4 @@
|
||||
<a href="https://payloadcms.com"><img width="100%" src="https://l4wlsi8vxy8hre4v.public.blob.vercel-storage.com/github-banner-new-logo.jpg" alt="Payload headless CMS Admin panel built with React" /></a>
|
||||
<a href="https://payloadcms.com"><img width="100%" src="https://github.com/payloadcms/payload/blob/beta/packages/payload/src/assets/images/github-banner-nextjs-native.jpg" alt="Payload headless CMS Admin panel built with React" /></a>
|
||||
<br />
|
||||
<br />
|
||||
|
||||
@@ -9,20 +9,18 @@
|
||||
|
||||
<a href="https://www.npmjs.com/package/payload"><img alt="npm" src="https://img.shields.io/npm/dw/payload?style=flat-square" /></a>
|
||||
|
||||
<a href="https://github.com/payloadcms/payload/graphs/contributors"><img alt="npm" src="https://img.shields.io/github/contributors-anon/payloadcms/payload?color=yellow&style=flat-square" /></a>
|
||||
|
||||
<a href="https://www.npmjs.com/package/payload"><img alt="npm" src="https://img.shields.io/npm/v/payload?style=flat-square" /></a>
|
||||
|
||||
<a href="https://twitter.com/payloadcms"><img src="https://img.shields.io/badge/follow-payloadcms-1DA1F2?logo=twitter&style=flat-square" alt="Payload Twitter" /></a>
|
||||
</p>
|
||||
<hr/>
|
||||
<h4>
|
||||
<a target="_blank" href="https://payloadcms.com/docs/getting-started/what-is-payload" rel="dofollow"><strong>Explore the Docs</strong></a> · <a target="_blank" href="https://payloadcms.com/community-help" rel="dofollow"><strong>Community Help</strong></a> · <a target="_blank" href="https://github.com/payloadcms/payload/discussions/1539" rel="dofollow"><strong>Roadmap</strong></a> · <a target="_blank" href="https://www.g2.com/products/payload-cms/reviews#reviews" rel="dofollow"><strong>View G2 Reviews</strong></a>
|
||||
<a target="_blank" href="https://payloadcms.com/docs/beta/getting-started/what-is-payload" rel="dofollow"><strong>Explore the Docs</strong></a> · <a target="_blank" href="https://payloadcms.com/community-help" rel="dofollow"><strong>Community Help</strong></a> · <a target="_blank" href="https://github.com/payloadcms/payload/discussions/1539" rel="dofollow"><strong>Roadmap</strong></a> · <a target="_blank" href="https://www.g2.com/products/payload-cms/reviews#reviews" rel="dofollow"><strong>View G2 Reviews</strong></a>
|
||||
</h4>
|
||||
<hr/>
|
||||
|
||||
> [!IMPORTANT]
|
||||
> 🎉 <strong>We've released 3.0!</strong> Star this repo or keep an eye on it to follow along.
|
||||
> 🚨 <strong>We're about to release 3.0 stable.</strong> Star this repo or keep an eye on it to follow along.
|
||||
|
||||
Payload is the first-ever Next.js native CMS that can install directly in your existing `/app` folder. It's the start of a new era for headless CMS.
|
||||
|
||||
@@ -40,25 +38,25 @@ Payload is the first-ever Next.js native CMS that can install directly in your e
|
||||
|
||||
## Quickstart
|
||||
|
||||
Before beginning to work with Payload, make sure you have all of the [required software](https://payloadcms.com/docs/getting-started/installation).
|
||||
Before beginning to work with Payload, make sure you have all of the [required software](https://payloadcms.com/docs/beta/getting-started/installation).
|
||||
|
||||
```text
|
||||
pnpx create-payload-app@latest
|
||||
pnpx create-payload-app@beta
|
||||
```
|
||||
|
||||
**If you're new to Payload, you should start with the website template** (`pnpx create-payload-app@latest -t website`). It shows how to do _everything_ - including custom Rich Text blocks, on-demand revalidation, live preview, and more. It comes with a frontend built with Tailwind all in one `/app` folder.
|
||||
**If you're new to Payload, you should start with the 3.0 beta website template** (`pnpx create-payload-app@beta -t website`). It shows how to do _everything_ - including custom Rich Text blocks, on-demand revalidation, live preview, and more. It comes with a frontend built with Tailwind all in one `/app` folder.
|
||||
|
||||
## One-click templates
|
||||
|
||||
Jumpstart your next project by starting with a pre-made template. These are production-ready, end-to-end solutions designed to get you to market as fast as possible.
|
||||
|
||||
### [🌐 Website](https://github.com/payloadcms/payload/tree/main/templates/website)
|
||||
### [🌐 Website](https://github.com/payloadcms/payload/tree/beta/templates/website)
|
||||
|
||||
Build any kind of website, blog, or portfolio from small to enterprise. Comes with a fully functional front-end built with RSCs and Tailwind.
|
||||
|
||||
We're constantly adding more templates to our [Templates Directory](https://github.com/payloadcms/payload/tree/main/templates). If you maintain your own template, consider adding the `payload-template` topic to your GitHub repository for others to find.
|
||||
We're constantly adding more templates to our [Templates Directory](https://github.com/payloadcms/payload/tree/beta/templates). If you maintain your own template, consider adding the `payload-template` topic to your GitHub repository for others to find.
|
||||
|
||||
- [Official Templates](https://github.com/payloadcms/payload/tree/main/templates)
|
||||
- [Official Templates](https://github.com/payloadcms/payload/tree/beta/templates)
|
||||
- [Community Templates](https://github.com/topics/payload-template)
|
||||
|
||||
## ✨ Features
|
||||
@@ -68,15 +66,15 @@ We're constantly adding more templates to our [Templates Directory](https://gith
|
||||
- Use server components to extend Payload UI
|
||||
- Query your database directly in server components, no need for REST / GraphQL
|
||||
- Fully TypeScript with automatic types for your data
|
||||
- [Auth out of the box](https://payloadcms.com/docs/authentication/overview)
|
||||
- [Versions and drafts](https://payloadcms.com/docs/versions/overview)
|
||||
- [Localization](https://payloadcms.com/docs/configuration/localization)
|
||||
- [Block-based layout builder](https://payloadcms.com/docs/fields/blocks)
|
||||
- [Customizable React admin](https://payloadcms.com/docs/admin/overview)
|
||||
- [Lexical rich text editor](https://payloadcms.com/docs/fields/rich-text)
|
||||
- [Conditional field logic](https://payloadcms.com/docs/fields/overview#conditional-logic)
|
||||
- Extremely granular [Access Control](https://payloadcms.com/docs/access-control/overview)
|
||||
- [Document and field-level hooks](https://payloadcms.com/docs/hooks/overview) for every action Payload provides
|
||||
- [Auth out of the box](https://payloadcms.com/docs/beta/authentication/overview)
|
||||
- [Versions and drafts](https://payloadcms.com/docs/beta/versions/overview)
|
||||
- [Localization](https://payloadcms.com/docs/beta/configuration/localization)
|
||||
- [Block-based kayout builder](https://payloadcms.com/docs/beta/fields/blocks)
|
||||
- [Customizable React admin](https://payloadcms.com/docs/beta/admin/overview)
|
||||
- [Lexical rich text editor](https://payloadcms.com/docs/beta/fields/rich-text)
|
||||
- [Conditional field logic](https://payloadcms.com/docs/beta/fields/overview#conditional-logic)
|
||||
- Extremely granular [Access Control](https://payloadcms.com/docs/beta/access-control/overview)
|
||||
- [Document and field-level hooks](https://payloadcms.com/docs/beta/hooks/overview) for every action Payload provides
|
||||
- Intensely fast API
|
||||
- Highly secure thanks to HTTP-only cookies, CSRF protection, and more
|
||||
|
||||
@@ -84,9 +82,9 @@ We're constantly adding more templates to our [Templates Directory](https://gith
|
||||
|
||||
## 🗒️ Documentation
|
||||
|
||||
Check out the [Payload website](https://payloadcms.com/docs/getting-started/what-is-payload) to find in-depth documentation for everything that Payload offers.
|
||||
Check out the [Payload website](https://payloadcms.com/docs/beta/getting-started/what-is-payload) to find in-depth documentation for everything that Payload offers.
|
||||
|
||||
Migrating from v2 to v3? Check out the [3.0 Migration Guide](https://github.com/payloadcms/payload/blob/main/docs/migration-guide/overview.mdx) on how to do it.
|
||||
Migrating from v1 to v2? Check out the [2.0 Release Notes](https://github.com/payloadcms/payload/releases/tag/v2.0.0) on how to do it.
|
||||
|
||||
## 🙋 Contributing
|
||||
|
||||
@@ -96,11 +94,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:
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
{
|
||||
"singleQuote": true,
|
||||
"trailingComma": "all",
|
||||
"printWidth": 80,
|
||||
"semi": false
|
||||
}
|
||||
@@ -6,17 +6,16 @@ desc: With Collection-level Access Control you can define which users can create
|
||||
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.
|
||||
Collection Access Control is [Access Control](../access-control) used to restrict access to Documents within a [Collection](../collections/overview), 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'
|
||||
import type { CollectionConfig } from 'payload';
|
||||
|
||||
export const CollectionWithAccessControl: CollectionConfig = {
|
||||
// ...
|
||||
access: {
|
||||
// highlight-line
|
||||
access: { // highlight-line
|
||||
// ...
|
||||
},
|
||||
}
|
||||
@@ -53,31 +52,31 @@ export const CollectionWithAccessControl: CollectionConfig = {
|
||||
|
||||
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). |
|
||||
| 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). |
|
||||
| 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 |
|
||||
| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| 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):
|
||||
To add create Access Control to a Collection, use the `create` property in the [Collection Config](../collections/overview):
|
||||
|
||||
```ts
|
||||
import type { CollectionConfig } from 'payload'
|
||||
@@ -96,16 +95,16 @@ export const CollectionWithCreateAccess: CollectionConfig = {
|
||||
|
||||
The following arguments are provided to the `create` function:
|
||||
|
||||
| Option | Description |
|
||||
| ---------- | ----------------------------------------------------------------------------------------------------------------------------- |
|
||||
| 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. |
|
||||
| **`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):
|
||||
To add read Access Control to a Collection, use the `read` property in the [Collection Config](../collections/overview):
|
||||
|
||||
```ts
|
||||
import type { CollectionConfig } from 'payload'
|
||||
@@ -123,9 +122,8 @@ export const CollectionWithReadAccess: CollectionConfig = {
|
||||
```
|
||||
|
||||
<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).
|
||||
<strong>Tip:</strong>
|
||||
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:
|
||||
@@ -151,16 +149,16 @@ export const canReadPage: Access = ({ req: { user } }) => {
|
||||
|
||||
The following arguments are provided to the `read` function:
|
||||
|
||||
| Option | Description |
|
||||
| --------- | ----------------------------------------------------------------------------------------------------------------------------- |
|
||||
| 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`. |
|
||||
| **`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):
|
||||
To add update Access Control to a Collection, use the `update` property in the [Collection Config](../collections/overview):
|
||||
|
||||
```ts
|
||||
import type { CollectionConfig } from 'payload'
|
||||
@@ -169,7 +167,7 @@ export const CollectionWithUpdateAccess: CollectionConfig = {
|
||||
// ...
|
||||
access: {
|
||||
// highlight-start
|
||||
update: ({ req: { user } }) => {
|
||||
update: ({ req: { user }}) => {
|
||||
return Boolean(user)
|
||||
},
|
||||
// highlight-end
|
||||
@@ -178,9 +176,8 @@ export const CollectionWithUpdateAccess: CollectionConfig = {
|
||||
```
|
||||
|
||||
<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).
|
||||
<strong>Tip:</strong>
|
||||
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:
|
||||
@@ -201,17 +198,17 @@ export const canUpdateUser: Access = ({ req: { user }, id }) => {
|
||||
|
||||
The following arguments are provided to the `update` function:
|
||||
|
||||
| Option | Description |
|
||||
| ---------- | ----------------------------------------------------------------------------------------------------------------------------- |
|
||||
| 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. |
|
||||
| **`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):
|
||||
To add delete Access Control to a Collection, use the `delete` property in the [Collection Config](../collections/overview):
|
||||
|
||||
```ts
|
||||
import type { CollectionConfig } from 'payload'
|
||||
@@ -220,7 +217,7 @@ export const CollectionWithDeleteAccess: CollectionConfig = {
|
||||
// ...
|
||||
access: {
|
||||
// highlight-start
|
||||
delete: ({ req: { user } }) => {
|
||||
delete: ({ req: { user }}) => {
|
||||
return Boolean(user)
|
||||
},
|
||||
// highlight-end
|
||||
@@ -255,16 +252,16 @@ export const canDeleteCustomer: Access = async ({ req, id }) => {
|
||||
|
||||
The following arguments are provided to the `delete` function:
|
||||
|
||||
| Option | Description |
|
||||
| --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| 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. |
|
||||
| **`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.
|
||||
If the Collection is use 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):
|
||||
To add Admin Access Control to a Collection, use the `admin` property in the [Collection Config](../collections/overview):
|
||||
|
||||
```ts
|
||||
import type { CollectionConfig } from 'payload'
|
||||
@@ -273,7 +270,7 @@ export const CollectionWithAdminAccess: CollectionConfig = {
|
||||
// ...
|
||||
access: {
|
||||
// highlight-start
|
||||
admin: ({ req: { user } }) => {
|
||||
admin: ({ req: { user }}) => {
|
||||
return Boolean(user)
|
||||
},
|
||||
// highlight-end
|
||||
@@ -283,15 +280,15 @@ export const CollectionWithAdminAccess: CollectionConfig = {
|
||||
|
||||
The following arguments are provided to the `admin` function:
|
||||
|
||||
| Option | Description |
|
||||
| --------- | ----------------------------------------------------------------------------------------------------------------------------- |
|
||||
| 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).
|
||||
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#options).
|
||||
|
||||
To add Unlock Access Control to a Collection, use the `unlock` property in the [Collection Config](../configuration/collections):
|
||||
To add Unlock Access Control to a Collection, use the `unlock` property in the [Collection Config](../collections/overview):
|
||||
|
||||
```ts
|
||||
import type { CollectionConfig } from 'payload'
|
||||
@@ -300,7 +297,7 @@ export const CollectionWithUnlockAccess: CollectionConfig = {
|
||||
// ...
|
||||
access: {
|
||||
// highlight-start
|
||||
unlock: ({ req: { user } }) => {
|
||||
unlock: ({ req: { user }}) => {
|
||||
return Boolean(user)
|
||||
},
|
||||
// highlight-end
|
||||
@@ -310,15 +307,15 @@ export const CollectionWithUnlockAccess: CollectionConfig = {
|
||||
|
||||
The following arguments are provided to the `unlock` function:
|
||||
|
||||
| Option | Description |
|
||||
| --------- | ----------------------------------------------------------------------------------------------------------------------------- |
|
||||
| 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):
|
||||
To add Read Versions Access Control to a Collection, use the `readVersions` property in the [Collection Config](../collections/overview):
|
||||
|
||||
```ts
|
||||
import type { CollectionConfig } from 'payload'
|
||||
@@ -327,7 +324,7 @@ export const CollectionWithVersionsAccess: CollectionConfig = {
|
||||
// ...
|
||||
access: {
|
||||
// highlight-start
|
||||
readVersions: ({ req: { user } }) => {
|
||||
readVersions: ({ req: { user }}) => {
|
||||
return Boolean(user)
|
||||
},
|
||||
// highlight-end
|
||||
@@ -337,6 +334,6 @@ export const CollectionWithVersionsAccess: CollectionConfig = {
|
||||
|
||||
The following arguments are provided to the `readVersions` function:
|
||||
|
||||
| Option | Description |
|
||||
| --------- | ----------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Option | Description |
|
||||
| --------- | -------------------------------------------------------------------------- |
|
||||
| **`req`** | The [Request](https://developer.mozilla.org/en-US/docs/Web/API/Request) object containing the currently authenticated `user`. |
|
||||
|
||||
@@ -6,26 +6,24 @@ desc: Field-level Access Control is specified within a field's config, and allow
|
||||
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.
|
||||
Field Access Control is [Access Control](../access-control) 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'
|
||||
import type { Field } from 'payload';
|
||||
|
||||
export const FieldWithAccessControl: Field = {
|
||||
// ...
|
||||
access: {
|
||||
// highlight-line
|
||||
access: { // highlight-line
|
||||
// ...
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
<Banner type="warning">
|
||||
**Note:** Field Access Controls does not support returning
|
||||
[Query](../queries/overview) constraints like [Collection Access
|
||||
Control](./collections) does.
|
||||
<strong>Note:</strong>
|
||||
Field Access Controls does not support returning [Query](../queries/overview) constraints like [Collection Access Control](./collections) does.
|
||||
</Banner>
|
||||
|
||||
## Config Options
|
||||
@@ -57,11 +55,11 @@ export const Posts: CollectionConfig = {
|
||||
|
||||
The following options are available:
|
||||
|
||||
| Function | Purpose |
|
||||
| ------------ | ---------------------------------------------------------------------------------------------------------- |
|
||||
| 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). |
|
||||
| **`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
|
||||
|
||||
@@ -69,11 +67,11 @@ Returns a boolean which allows or denies the ability to set a field's value when
|
||||
|
||||
**Available argument properties:**
|
||||
|
||||
| Option | Description |
|
||||
| ----------------- | ---------------------------------------------------------------------------------------------------------------------------- |
|
||||
| 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. |
|
||||
| **`data`** | The full data passed to create the document. |
|
||||
| **`siblingData`** | Immediately adjacent field data passed to create the document. |
|
||||
|
||||
### Read
|
||||
|
||||
@@ -81,12 +79,12 @@ Returns a boolean which allows or denies the ability to read a field's value. If
|
||||
|
||||
**Available argument properties:**
|
||||
|
||||
| Option | Description |
|
||||
| ----------------- | ---------------------------------------------------------------------------------------------------------------------------- |
|
||||
| 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. |
|
||||
| **`id`** | `id` of the document being read |
|
||||
| **`doc`** | The full document data. |
|
||||
| **`siblingData`** | Immediately adjacent field data of the document being read. |
|
||||
|
||||
### Update
|
||||
|
||||
@@ -96,10 +94,10 @@ If `false` is returned and you attempt to update the field's value, the operatio
|
||||
|
||||
**Available argument properties:**
|
||||
|
||||
| Option | Description |
|
||||
| ----------------- | ---------------------------------------------------------------------------------------------------------------------------- |
|
||||
| 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. |
|
||||
| **`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. |
|
||||
|
||||
@@ -6,17 +6,16 @@ desc: Global-level Access Control is specified within each Global's `access` pro
|
||||
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.
|
||||
Global Access Control is [Access Control](../access-control) used to restrict access to [Global](../globals/overview) 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'
|
||||
import type { GlobalConfig } from 'payload';
|
||||
|
||||
export const GlobalWithAccessControl: GlobalConfig = {
|
||||
// ...
|
||||
access: {
|
||||
// highlight-line
|
||||
access: { // highlight-line
|
||||
// ...
|
||||
},
|
||||
}
|
||||
@@ -26,7 +25,7 @@ export const GlobalWithAccessControl: GlobalConfig = {
|
||||
|
||||
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):
|
||||
To add Access Control to a [Global](../configuration/globals), use the `access` property in the [Global Config](../globals/overview):
|
||||
|
||||
```ts
|
||||
import { GlobalConfig } from 'payload'
|
||||
@@ -49,22 +48,22 @@ export default Header
|
||||
|
||||
The following options are available:
|
||||
|
||||
| Function | Allows/Denies Access |
|
||||
| ------------ | --------------------------------------------------------------- |
|
||||
| **`read`** | Used in the `findOne` Global operation. [More details](#read). |
|
||||
| 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 |
|
||||
| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| 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 `read` property in the [Global Config](../configuration/globals):
|
||||
To add read Access Control to a [Global](../configuration/globals), use the `read` property in the [Global Config](../globals/overview):
|
||||
|
||||
```ts
|
||||
import { GlobalConfig } from 'payload'
|
||||
@@ -76,22 +75,22 @@ const Header: GlobalConfig = {
|
||||
read: ({ req: { user } }) => {
|
||||
return Boolean(user)
|
||||
},
|
||||
},
|
||||
}
|
||||
// highlight-end
|
||||
}
|
||||
```
|
||||
|
||||
The following arguments are provided to the `read` function:
|
||||
|
||||
| Option | Description |
|
||||
| --------- | ----------------------------------------------------------------------------------------------------------------------------- |
|
||||
| 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):
|
||||
To add update Access Control to a [Global](../configuration/globals), use the `access` property in the [Global Config](../globals/overview):
|
||||
|
||||
```ts
|
||||
import { GlobalConfig } from 'payload'
|
||||
@@ -103,23 +102,23 @@ const Header: GlobalConfig = {
|
||||
update: ({ req: { user }, data }) => {
|
||||
return Boolean(user)
|
||||
},
|
||||
},
|
||||
}
|
||||
// highlight-end
|
||||
}
|
||||
```
|
||||
|
||||
The following arguments are provided to the `update` function:
|
||||
|
||||
| Option | Description |
|
||||
| ---------- | ----------------------------------------------------------------------------------------------------------------------------- |
|
||||
| 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. |
|
||||
| **`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):
|
||||
To add Read Versions Access Control to a Collection, use the `readVersions` property in the [Global Config](../globals/overview):
|
||||
|
||||
```ts
|
||||
import type { GlobalConfig } from 'payload'
|
||||
@@ -128,7 +127,7 @@ export const GlobalWithVersionsAccess: GlobalConfig = {
|
||||
// ...
|
||||
access: {
|
||||
// highlight-start
|
||||
readVersions: ({ req: { user } }) => {
|
||||
readVersions: ({ req: { user }}) => {
|
||||
return Boolean(user)
|
||||
},
|
||||
// highlight-end
|
||||
@@ -138,6 +137,6 @@ export const GlobalWithVersionsAccess: GlobalConfig = {
|
||||
|
||||
The following arguments are provided to the `readVersions` function:
|
||||
|
||||
| Option | Description |
|
||||
| --------- | ----------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Option | Description |
|
||||
| --------- | -------------------------------------------------------------------------- |
|
||||
| **`req`** | The [Request](https://developer.mozilla.org/en-US/docs/Web/API/Request) object containing the currently authenticated `user`. |
|
||||
|
||||
@@ -29,7 +29,7 @@ There are three main types of Access Control in Payload:
|
||||
|
||||
## 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.
|
||||
Payload provides default Access Control so that your data is secured behind [Authentication](../authentication) 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:
|
||||
|
||||
@@ -42,10 +42,8 @@ const defaultPayloadAccess = ({ req: { user } }) => {
|
||||
```
|
||||
|
||||
<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.
|
||||
<strong>Important:</strong>
|
||||
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
|
||||
@@ -55,28 +53,8 @@ The Admin Panel responds dynamically to your changes to Access Control. For exam
|
||||
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.
|
||||
<strong>Important:</strong>
|
||||
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
|
||||
}
|
||||
```
|
||||
|
||||
174
docs/admin/collections.mdx
Normal file
174
docs/admin/collections.mdx
Normal file
@@ -0,0 +1,174 @@
|
||||
---
|
||||
title: Collection Admin Config
|
||||
label: Collections
|
||||
order: 20
|
||||
desc:
|
||||
keywords: admin, components, custom, documentation, Content Management System, cms, headless, javascript, node, react, nextjs
|
||||
---
|
||||
|
||||
The behavior of [Collections](../configuration/collections) within the [Admin Panel](./overview) can be fully customized to fit the needs of your application. This includes grouping or hiding their navigation links, adding [Custom Components](./components), 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
|
||||
// ...
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
## Admin Options
|
||||
|
||||
The following options are available:
|
||||
|
||||
| Option | Description |
|
||||
| -------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **`group`** | Text used as a label for grouping Collection and Global links together in the navigation. |
|
||||
| **`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. A field with `virtual: true` cannot be 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. |
|
||||
| **`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. |
|
||||
| **`meta`** | Page metadata overrides to apply to this Collection within the Admin Panel. [More details](./metadata). |
|
||||
| **`preview`** | Function to generate preview URLs within the Admin Panel that can point to your app. [More details](#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. |
|
||||
|
||||
### Custom Components
|
||||
|
||||
Collections can set their own [Custom Components](./components) which only apply to [Collection](../configuration/collections)-specific UI within the [Admin Panel](./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](../configuration/collections):
|
||||
|
||||
```ts
|
||||
import type { SanitizedCollectionConfig } from 'payload'
|
||||
|
||||
export const MyCollection: SanitizedCollectionConfig = {
|
||||
// ...
|
||||
admin: {
|
||||
components: { // highlight-line
|
||||
// ...
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
The following options are available:
|
||||
|
||||
| Path | Description |
|
||||
| -------------------------- | ---------------------------------------------------------------------------------------------------------------------- |
|
||||
| **`beforeList`** | An array of components to inject _before_ the built-in List View |
|
||||
| **`beforeListTable`** | An array of components to inject _before_ the built-in List View's table |
|
||||
| **`afterList`** | An array of components to inject _after_ the built-in List View |
|
||||
| **`afterListTable`** | An array of components to inject _after_ the built-in List View's table
|
||||
| **`Description`** | A component to render below the Collection label in the List View. An alternative to the `admin.description` property. |
|
||||
| **`edit.SaveButton`** | Replace the default Save Button with a Custom Component. [Drafts](../versions/drafts) must be disabled. |
|
||||
| **`edit.SaveDraftButton`** | Replace the default Save Draft Button with a Custom Component. [Drafts](../versions/drafts) must be enabled and autosave must be disabled. |
|
||||
| **`edit.PublishButton`** | Replace the default Publish Button with a Custom Component. [Drafts](../versions/drafts) must be enabled. |
|
||||
| **`edit.PreviewButton`** | Replace the default Preview Button with a Custom Component. [Preview](#preview) must be enabled. |
|
||||
| **`views`** | Override or create new views within the Admin Panel. [More details](./views). |
|
||||
|
||||
<Banner type="success">
|
||||
<strong>Note:</strong>
|
||||
For details on how to build Custom Components, see [Building Custom Components](./components#building-custom-components).
|
||||
</Banner>
|
||||
|
||||
### Preview
|
||||
|
||||
It is possible to display a Preview Button within the Edit View of the Admin Panel. This will allow editors to visit the frontend of your app the corresponds to the document they are actively editing. This way they can preview the latest, potentially unpublished changes.
|
||||
|
||||
To configure the Preview Button, set the `admin.preview` property to a function in your [Collection Config](../configuration/collections):
|
||||
|
||||
```ts
|
||||
import type { CollectionConfig } from 'payload'
|
||||
|
||||
export const Posts: CollectionConfig = {
|
||||
// ...
|
||||
admin: {
|
||||
// highlight-start
|
||||
preview: (doc, { locale }) => {
|
||||
if (doc?.slug) {
|
||||
return `/${doc.slug}?locale=${locale}`
|
||||
}
|
||||
|
||||
return null
|
||||
},
|
||||
// highlight-end
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
The preview function receives two arguments:
|
||||
|
||||
| Argument | Description |
|
||||
| --- | --- |
|
||||
| **`doc`** | The Document being edited. |
|
||||
| **`ctx`** | An object containing `locale` and `token` properties. The `token` is the currently logged-in user's JWT. |
|
||||
|
||||
<Banner type="success">
|
||||
<strong>Note:</strong>
|
||||
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>
|
||||
|
||||
### 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](../configuration/collections):
|
||||
|
||||
```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](../configuration/collections):
|
||||
|
||||
```ts
|
||||
import type { CollectionConfig } from 'payload'
|
||||
|
||||
export const Posts: CollectionConfig = {
|
||||
// ...
|
||||
admin: {
|
||||
// highlight-start
|
||||
listSearchableFields: ['title', 'slug'],
|
||||
// highlight-end
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
<Banner type="warning">
|
||||
<strong>Tip:</strong>
|
||||
If you are adding `listSearchableFields`, make sure you index each of these fields so your admin queries can remain performant.
|
||||
</Banner>
|
||||
550
docs/admin/components.mdx
Normal file
550
docs/admin/components.mdx
Normal file
@@ -0,0 +1,550 @@
|
||||
---
|
||||
title: Swap in your own React components
|
||||
label: Custom Components
|
||||
order: 40
|
||||
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](./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, with the exception of [Custom Providers](#custom-providers). 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">
|
||||
<strong>Note:</strong>
|
||||
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 default, [non-serializable props](https://react.dev/reference/rsc/use-client#serializable-types) 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](./collections#custom-components)
|
||||
- [Global Components](./globals#custom-components)
|
||||
- [Field Components](./fields#custom-components)
|
||||
|
||||
To swap in your own Custom Component, first consult the list of available components, determine the scope that corresponds to what you are trying to accomplish, 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 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">
|
||||
<strong>Note:</strong>
|
||||
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.baseDir`. To simplify Component Paths, you can also configure the base directory using the `admin.importMap.baseDir` property.
|
||||
|
||||
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.
|
||||
|
||||
### Config Options
|
||||
|
||||
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 more 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 `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.
|
||||
|
||||
#### 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](../getting-started/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, with the exception of [Custom Providers](#custom-providers). 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'
|
||||
|
||||
const MyServerComponent = async ({
|
||||
payload // highlight-line
|
||||
}) => {
|
||||
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">
|
||||
<strong>Reminder:</strong>
|
||||
All Custom Components also receive various other props that are specific component being rendered. See [Root Components](#root-components), [Collection Components](#collection-components), [Global Components](#global-components), or [Field Components](#custom-field-components) for a complete list of all default props per component.
|
||||
</Banner>
|
||||
|
||||
### Custom Props
|
||||
|
||||
To pass in custom props from the config, 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
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
```tsx
|
||||
'use client'
|
||||
import React from 'react'
|
||||
|
||||
export const MyComponent = ({ myCustomProp }: { myCustomProp: string }) => {
|
||||
return (
|
||||
<button>{myCustomProp}</button>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
### Client Components
|
||||
|
||||
When [Building Custom Components](#building-custom-components), it's still possible to use client-side code such as `useState` or the `window` object. To do this, simply add the `use client` directive at the top of your file. Payload will automatically detect and remove all default, [non-serializable props](https://react.dev/reference/rsc/use-client#serializable-types) before rendering your component.
|
||||
|
||||
```tsx
|
||||
'use client' // highlight-line
|
||||
import React, { useState } from 'react'
|
||||
|
||||
export const MyClientComponent: React.FC = () => {
|
||||
const [count, setCount] = useState(0)
|
||||
|
||||
return (
|
||||
<button onClick={() => setCount(count + 1)}>
|
||||
Clicked {count} times
|
||||
</button>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
<Banner type="warning">
|
||||
<strong>Reminder:</strong>
|
||||
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, React components, etc. 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`](./hooks#useconfig) hook:
|
||||
|
||||
```tsx
|
||||
'use client'
|
||||
import React from 'react'
|
||||
import { useConfig } from '@payloadcms/ui'
|
||||
|
||||
export const MyClientComponent: React.FC = () => {
|
||||
const { config: { serverURL } } = useConfig() // highlight-line
|
||||
|
||||
return (
|
||||
<Link href={serverURL}>
|
||||
Go Home
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
<Banner type="success">
|
||||
See [Using Hooks](#using-hooks) for more details.
|
||||
</Banner>
|
||||
|
||||
All [Field Components](./fields) automatically receive their respective Field Config through props.
|
||||
|
||||
```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>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
### Getting the Current Language
|
||||
|
||||
All Custom Components can support multiple languages to be consistent with Payload's [Internationalization](../configuration/i18n). To do this, first add your translation resources to the [I18n Config](../configuration/i18n).
|
||||
|
||||
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 const MyClientComponent: React.FC = () => {
|
||||
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](./hooks) documentation for a full list of available hooks.
|
||||
</Banner>
|
||||
|
||||
### Getting the Current Locale
|
||||
|
||||
All [Custom Views](./views) can support multiple locales to be consistent with Payload's [Localization](../configuration/localization). They automatically receive the `locale` object as a prop by default. This can be used to scope API requests, etc.:
|
||||
|
||||
```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'
|
||||
|
||||
const Greeting: React.FC = () => {
|
||||
const locale = useLocale() // highlight-line
|
||||
|
||||
const trans = {
|
||||
en: 'Hello',
|
||||
es: 'Hola',
|
||||
}
|
||||
|
||||
return (
|
||||
<span>{trans[locale.code]}</span>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
<Banner type="success">
|
||||
See the [Hooks](./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](./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 one of the many hooks available depending on your needs.
|
||||
|
||||
```tsx
|
||||
'use client'
|
||||
import React from 'react'
|
||||
import { useDocumentInfo } from '@payloadcms/ui'
|
||||
|
||||
export const MyClientComponent: React.FC = () => {
|
||||
const { slug } = useDocumentInfo() // highlight-line
|
||||
|
||||
return (
|
||||
<p>{`Entity slug: ${slug}`}</p>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
<Banner type="success">
|
||||
See the [Hooks](./hooks) documentation for a full list of available hooks.
|
||||
</Banner>
|
||||
|
||||
### Adding Styles
|
||||
|
||||
Payload has a robust [CSS Library](./customizing-css) that you can use to style your Custom Components similarly to Payload's built-in styling. This will ensure that your Custom Components match the existing design system, and so that 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 const MyComponent: React.FC = () => {
|
||||
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 '~payload/scss';
|
||||
|
||||
.my-component {
|
||||
@include mid-break {
|
||||
background-color: var(--theme-elevation-900);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<Banner type="success">
|
||||
<strong>Note:</strong>
|
||||
You can also drill into Payload's own component styles, or easily apply global, app-wide CSS. More on that [here](./customizing-css).
|
||||
</Banner>
|
||||
|
||||
|
||||
## Root Components
|
||||
|
||||
Root Components are those that effect the [Admin Panel](./overview) generally, such as the logo or the main nav.
|
||||
|
||||
To override Root Components, use the `admin.components` property in your [Payload Config](../getting-started/overview):
|
||||
|
||||
```ts
|
||||
import { buildConfig } from 'payload'
|
||||
|
||||
export default buildConfig({
|
||||
// ...
|
||||
admin: {
|
||||
// highlight-start
|
||||
components: {
|
||||
// ...
|
||||
},
|
||||
// highlight-end
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
_For details on how to build Custom Components, see [Building Custom Components](#building-custom-components)._
|
||||
|
||||
The following options are available:
|
||||
|
||||
| Path | Description |
|
||||
|-----------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **`Nav`** | Contains the sidebar / mobile menu in its entirety. |
|
||||
| **`beforeNavLinks`** | An array of Custom Components to inject into the built-in Nav, _before_ the links themselves. |
|
||||
| **`afterNavLinks`** | An array of Custom Components to inject into the built-in Nav, _after_ the links. |
|
||||
| **`beforeDashboard`** | An array of Custom Components to inject into the built-in Dashboard, _before_ the default dashboard contents. |
|
||||
| **`afterDashboard`** | An array of Custom Components to inject into the built-in Dashboard, _after_ the default dashboard contents. |
|
||||
| **`beforeLogin`** | An array of Custom Components to inject into the built-in Login, _before_ the default login form. |
|
||||
| **`afterLogin`** | An array of Custom Components to inject into the built-in Login, _after_ the default login form. |
|
||||
| **`logout.Button`** | The button displayed in the sidebar that logs the user out. |
|
||||
| **`graphics.Icon`** | The simplified logo used in contexts like the the `Nav` component. |
|
||||
| **`graphics.Logo`** | The full logo used in contexts like the `Login` view. |
|
||||
| **`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). |
|
||||
| **`actions`** | An array of Custom Components to be rendered _within_ the header of the Admin Panel, providing additional interactivity and functionality. |
|
||||
| **`header`** | An array of Custom Components to be injected above the Payload header. |
|
||||
| **`views`** | Override or create new views within the Admin Panel. [More details](./views). |
|
||||
|
||||
<Banner type="success">
|
||||
<strong>Note:</strong>
|
||||
You can also use set [Collection Components](./collections#custom-components) and [Global Components](./globals#custom-components) in their respective configs.
|
||||
</Banner>
|
||||
|
||||
### Custom Providers
|
||||
|
||||
As you add more and more Custom Components to your [Admin Panel](./overview), you may find it helpful to add additional [React Context](https://react.dev/learn/scaling-up-with-reducer-and-context)(s). Payload allows you to inject your own context providers in your app so you can export your own custom hooks, etc.
|
||||
|
||||
To add a Custom Provider, use the `admin.components.providers` property in your [Payload Config](../getting-started/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, useContext } from 'react'
|
||||
|
||||
const MyCustomContext = React.createContext(myCustomValue)
|
||||
|
||||
export const MyProvider: React.FC = ({ children }) => {
|
||||
return (
|
||||
<MyCustomContext.Provider value={myCustomValue}>
|
||||
{children}
|
||||
</MyCustomContext.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
export const useMyCustomContext = () => useContext(MyCustomContext)
|
||||
```
|
||||
|
||||
<Banner type="warning">
|
||||
<strong>Reminder:</strong> Custom Providers are by definition Client Components. This means they must include the `use client` directive at the top of their files and cannot use server-only code.
|
||||
</Banner>
|
||||
@@ -1,14 +1,14 @@
|
||||
---
|
||||
title: Customizing CSS & SCSS
|
||||
label: Customizing CSS
|
||||
order: 50
|
||||
order: 80
|
||||
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. Exposes a [root-level stylesheet](#global-css) for you to easily 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
|
||||
|
||||
@@ -29,10 +29,8 @@ Here is an example of how you might target the Dashboard View and change the bac
|
||||
```
|
||||
|
||||
<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.
|
||||
<strong>Note:</strong>
|
||||
If you are building [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
|
||||
@@ -42,10 +40,9 @@ All Payload CSS is encapsulated inside CSS layers under `@layer payload-default`
|
||||
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
|
||||
// my styles within the payload specificity
|
||||
}
|
||||
```
|
||||
|
||||
@@ -65,13 +62,13 @@ You can also override Payload's built-in [CSS Variables](https://developer.mozil
|
||||
|
||||
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)
|
||||
- [Breakpoints](https://github.com/payloadcms/payload/blob/beta/packages/ui/src/scss/queries.scss)
|
||||
- [Colors](https://github.com/payloadcms/payload/blob/beta/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)
|
||||
- [Sizing](https://github.com/payloadcms/payload/blob/beta/packages/ui/src/scss/app.scss)
|
||||
- Horizontal gutter
|
||||
- Transition speeds
|
||||
- Font sizes
|
||||
@@ -80,8 +77,8 @@ The following variables are defined and can be overridden:
|
||||
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).
|
||||
<strong>Warning:</strong>
|
||||
If you're overriding colors or theme elevations, make sure to consider how [your changes will affect dark mode](#dark-mode).
|
||||
</Banner>
|
||||
|
||||
#### Dark Mode
|
||||
|
||||
480
docs/admin/fields.mdx
Normal file
480
docs/admin/fields.mdx
Normal file
@@ -0,0 +1,480 @@
|
||||
---
|
||||
title: Customizing Fields
|
||||
label: Customizing Fields
|
||||
order: 60
|
||||
desc:
|
||||
keywords:
|
||||
---
|
||||
|
||||
[Fields](../fields/overview) within the [Admin Panel](./overview) 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), [Conditional Logic](#conditional-logic), [Custom Validations](../fields/overview#validation), and more.
|
||||
|
||||
For example, your app might need to render a specific interface that Payload does not inherently support, such as a color picker. To do this, you could replace the default [Text Field](../fields/text) input with your own user-friendly component that formats the data into a valid color value.
|
||||
|
||||
<Banner type="success">
|
||||
<strong>Tip:</strong>
|
||||
Don't see a built-in field type that you need? Build it! Using a combination of [Field Validations](../fields/overview#validation)
|
||||
and [Custom Components](./components), you can override the entirety of how a component functions within the [Admin Panel](./overview) to effectively create your own field type.
|
||||
</Banner>
|
||||
|
||||
## Admin Options
|
||||
|
||||
You can customize the appearance and behavior of fields within the [Admin Panel](./overview) through the `admin` property of any [Field Config](../fields/overview):
|
||||
|
||||
```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](../admin/fields#conditional-logic). |
|
||||
| **`components`** | All Field Components can be swapped out for [Custom Components](../admin/components) that you define. [More details](../admin/fields). |
|
||||
| **`description`** | Helper text to display alongside the field to provide more information for the editor. [More details](../admin/fields#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). |
|
||||
| **`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 easily add a Custom Description to a field, use the `admin.description` property in your [Field Config](../fields/overview):
|
||||
|
||||
```ts
|
||||
import type { SanitizedCollectionConfig } from 'payload'
|
||||
|
||||
export const MyCollectionConfig: SanitizedCollectionConfig = {
|
||||
// ...
|
||||
fields: [
|
||||
// ...
|
||||
{
|
||||
name: 'myField',
|
||||
type: 'text',
|
||||
admin: {
|
||||
description: 'Hello, world!' // highlight-line
|
||||
},
|
||||
},
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
<Banner type="warning">
|
||||
<strong>Reminder:</strong>
|
||||
To replace the Field Description with a [Custom Component](./components), 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 easily add a Description Function to a field, set the `admin.description` property to a _function_ in your [Field Config](../fields/overview):
|
||||
|
||||
```ts
|
||||
import type { SanitizedCollectionConfig } from 'payload'
|
||||
|
||||
export const MyCollectionConfig: SanitizedCollectionConfig = {
|
||||
// ...
|
||||
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) |
|
||||
|
||||
## 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 three arguments:
|
||||
|
||||
- `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
|
||||
- `{ user }` - the final argument is an object containing the currently authenticated user
|
||||
|
||||
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, { user }) => {
|
||||
if (data.enableGreeting) {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
},
|
||||
// highlight-end
|
||||
},
|
||||
},
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Custom Components
|
||||
|
||||
Within the [Admin Panel](./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.
|
||||
|
||||
To easily swap in Field Components with your own, use the `admin.components` property in your [Field Config](../fields/overview):
|
||||
|
||||
```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). |
|
||||
| **`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 easily swap in your own Field Component, use the `admin.components.Field` property in your [Field Config](../fields/overview):
|
||||
|
||||
```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](./components#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](./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](../fields/overview), 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. [More details](../fields/overview). |
|
||||
| **`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`](./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](./hooks) documentation. For additional help, see [Building Custom Components](./components#building-custom-components).
|
||||
</Banner>
|
||||
|
||||
#### TypeScript
|
||||
|
||||
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](../fields/overview) 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'
|
||||
```
|
||||
|
||||
### 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 easily swap in your own Cell Component, use the `admin.components.Cell` property in your [Field Config](../fields/overview):
|
||||
|
||||
```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](./components#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 easily swap in your own Label Component, use the `admin.components.Label` property in your [Field Config](../fields/overview):
|
||||
|
||||
```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](./components#building-custom-components).
|
||||
|
||||
#### TypeScript
|
||||
|
||||
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](../fields/overview) 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](#the-description-property), you can also use a [Custom Component](./components) 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 easily add a Description Component to a field, use the `admin.components.Description` property in your [Field Config](../fields/overview):
|
||||
|
||||
```ts
|
||||
import type { SanitizedCollectionConfig } from 'payload'
|
||||
|
||||
export const MyCollectionConfig: SanitizedCollectionConfig = {
|
||||
// ...
|
||||
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](./components#building-custom-components).
|
||||
|
||||
#### TypeScript
|
||||
|
||||
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](../fields/overview) 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 easily swap in your own Error Component, use the `admin.components.Error` property in your [Field Config](../fields/overview):
|
||||
|
||||
```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](./components#building-custom-components).
|
||||
|
||||
#### TypeScript
|
||||
|
||||
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](../fields/overview) 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'
|
||||
```
|
||||
|
||||
### 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](../fields/overview):
|
||||
|
||||
```ts
|
||||
import type { SanitizedCollectionConfig } from 'payload'
|
||||
|
||||
export const MyCollectionConfig: SanitizedCollectionConfig = {
|
||||
// ...
|
||||
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](./components#building-custom-components).
|
||||
107
docs/admin/globals.mdx
Normal file
107
docs/admin/globals.mdx
Normal file
@@ -0,0 +1,107 @@
|
||||
---
|
||||
title: Global Admin Config
|
||||
label: Globals
|
||||
order: 30
|
||||
desc:
|
||||
keywords: admin, components, custom, documentation, Content Management System, cms, headless, javascript, node, react, nextjs
|
||||
---
|
||||
|
||||
The behavior of [Globals](../configuration/globals) within the [Admin Panel](./overview) can be fully customized to fit the needs of your application. This includes grouping or hiding their navigation links, adding [Custom Components](./components), 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
|
||||
// ...
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
## Admin Options
|
||||
|
||||
The following options are available:
|
||||
|
||||
| Option | Description |
|
||||
| ------------- | --------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **`group`** | Text used as a label for grouping Collection and Global links together in the navigation. |
|
||||
| **`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](#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](./metadata). |
|
||||
|
||||
### Custom Components
|
||||
|
||||
Globals can set their own [Custom Components](./components) which only apply to [Global](../configuration/globals)-specific UI within the [Admin Panel](./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](../configuration/globals):
|
||||
|
||||
```ts
|
||||
import type { SanitizedGlobalConfig } from 'payload'
|
||||
|
||||
export const MyGlobal: SanitizedGlobalConfig = {
|
||||
// ...
|
||||
admin: {
|
||||
components: { // highlight-line
|
||||
// ...
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
The following options are available:
|
||||
|
||||
| Path | Description |
|
||||
| ------------------------------ | ---------------------------------------------------------------------------------------------------------------------- |
|
||||
| **`elements.SaveButton`** | Replace the default Save Button with a Custom Component. [Drafts](../versions/drafts) must be disabled. |
|
||||
| **`elements.SaveDraftButton`** | Replace the default Save Draft Button with a Custom Component. [Drafts](../versions/drafts) must be enabled and autosave must be disabled. |
|
||||
| **`elements.PublishButton`** | Replace the default Publish Button with a Custom Component. [Drafts](../versions/drafts) must be enabled. |
|
||||
| **`elements.PreviewButton`** | Replace the default Preview Button with a Custom Component. [Preview](#preview) must be enabled. |
|
||||
| **`views`** | Override or create new views within the Admin Panel. [More details](./views). |
|
||||
|
||||
<Banner type="success">
|
||||
<strong>Note:</strong>
|
||||
For details on how to build Custom Components, see [Building Custom Components](./components#building-custom-components).
|
||||
</Banner>
|
||||
|
||||
### Preview
|
||||
|
||||
It is possible to display a Preview Button within the Edit View of the Admin Panel. This will allow editors to visit the frontend of your app the corresponds to the document they are actively editing. This way they can preview the latest, potentially unpublished changes.
|
||||
|
||||
To configure the Preview Button, set the `admin.preview` property to a function in your Global Config:
|
||||
|
||||
```ts
|
||||
import { GlobalConfig } from 'payload'
|
||||
|
||||
export const MainMenu: GlobalConfig = {
|
||||
// ...
|
||||
admin: {
|
||||
// highlight-start
|
||||
preview: (doc, { locale }) => {
|
||||
if (doc?.slug) {
|
||||
return `/${doc.slug}?locale=${locale}`
|
||||
}
|
||||
|
||||
return null
|
||||
},
|
||||
// highlight-end
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
The preview function receives two arguments:
|
||||
|
||||
| Argument | Description |
|
||||
| --- | --- |
|
||||
| **`doc`** | The Document being edited. |
|
||||
| **`ctx`** | An object containing `locale` and `token` properties. The `token` is the currently logged-in user's JWT. |
|
||||
|
||||
<Banner type="success">
|
||||
<strong>Note:</strong>
|
||||
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>
|
||||
903
docs/admin/hooks.mdx
Normal file
903
docs/admin/hooks.mdx
Normal file
@@ -0,0 +1,903 @@
|
||||
---
|
||||
title: React Hooks
|
||||
label: React Hooks
|
||||
order: 70
|
||||
desc: Make use of all of the powerful React hooks that Payload provides.
|
||||
keywords: admin, components, custom, documentation, Content Management System, cms, headless, javascript, node, react, nextjs
|
||||
---
|
||||
|
||||
Payload provides a variety of powerful [React Hooks](https://react.dev/reference/react-dom/hooks) that can be used within your own [Custom Components](./components), such as [Custom Fields](./fields). With them, you can interface with Payload itself to build just about any type of complex customization you can think of.
|
||||
|
||||
<Banner type="warning">
|
||||
<strong>Reminder:</strong>
|
||||
All Custom Components are [React Server Components](https://react.dev/reference/rsc/server-components) by default. Hooks, on the other hand, are only available in client-side environments. To use hooks, [ensure your component is a client component](./components#client-components).
|
||||
</Banner>
|
||||
|
||||
## useField
|
||||
|
||||
The `useField` hook is used internally within all field components. It manages sending and receiving a field's state from its parent form. When you build a [Custom Field Component](./fields), you will be responsible for sending and receiving the field's `value` to and from the form yourself.
|
||||
|
||||
To do so, import the `useField` hook as follows:
|
||||
|
||||
```tsx
|
||||
'use client'
|
||||
import type { TextFieldClientComponent } from 'payload'
|
||||
import { useField } from '@payloadcms/ui'
|
||||
|
||||
export const CustomTextField: TextFieldClientComponent = ({ path }) => {
|
||||
const { value, setValue } = useField({ path }) // highlight-line
|
||||
|
||||
return (
|
||||
<div>
|
||||
<p>
|
||||
{path}
|
||||
</p>
|
||||
<input
|
||||
onChange={(e) => { setValue(e.target.value) }}
|
||||
value={value}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
The `useField` hook accepts the following arguments:
|
||||
|
||||
| Property | Description |
|
||||
| ----------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `path` | If you do not provide a `path`, `name` will be used instead. This is the path to the field in the form data. |
|
||||
| `validate` | A validation function executed client-side _before_ submitting the form to the server. Different than [Field-level Validation](../fields/overview#validation) which runs strictly on the server. |
|
||||
| `disableFormData` | If `true`, the field will not be included in the form data when the form is submitted. |
|
||||
| `hasRows` | If `true`, the field will be treated as a field with rows. This is useful for fields like `array` and `blocks`. |
|
||||
|
||||
The `useField` hook returns the following object:
|
||||
|
||||
```ts
|
||||
type FieldType<T> = {
|
||||
errorMessage?: string
|
||||
errorPaths?: string[]
|
||||
filterOptions?: FilterOptionsResult
|
||||
formInitializing: boolean
|
||||
formProcessing: boolean
|
||||
formSubmitted: boolean
|
||||
initialValue?: T
|
||||
path: string
|
||||
permissions: FieldPermissions
|
||||
readOnly?: boolean
|
||||
rows?: Row[]
|
||||
schemaPath: string
|
||||
setValue: (val: unknown, disableModifyingForm?: boolean) => void
|
||||
showError: boolean
|
||||
valid?: boolean
|
||||
value: T
|
||||
}
|
||||
```
|
||||
|
||||
## useFormFields
|
||||
|
||||
There are times when a custom field component needs to have access to data from other fields, and you have a few options to do so. The `useFormFields` hook is a powerful and highly performant way to retrieve a form's field state, as well as to retrieve the `dispatchFields` method, which can be helpful for setting other fields' form states from anywhere within a form.
|
||||
|
||||
<Banner type="success">
|
||||
<strong>This hook is great for retrieving only certain fields from form state</strong> because it
|
||||
ensures that it will only cause a rerender when the items that you ask for change.
|
||||
</Banner>
|
||||
|
||||
Thanks to the awesome package [`use-context-selector`](https://github.com/dai-shi/use-context-selector), you can retrieve a specific field's state easily. This is ideal because you can ensure you have an up-to-date field state, and your component will only re-render when _that field's state_ changes.
|
||||
|
||||
You can pass a Redux-like selector into the hook, which will ensure that you retrieve only the field that you want. The selector takes an argument with type of `[fields: Fields, dispatch: React.Dispatch<Action>]]`.
|
||||
|
||||
```tsx
|
||||
'use client'
|
||||
import { useFormFields } from '@payloadcms/ui'
|
||||
|
||||
const MyComponent: React.FC = () => {
|
||||
// Get only the `amount` field state, and only cause a rerender when that field changes
|
||||
const amount = useFormFields(([fields, dispatch]) => fields.amount)
|
||||
|
||||
// Do the same thing as above, but to the `feePercentage` field
|
||||
const feePercentage = useFormFields(([fields, dispatch]) => fields.feePercentage)
|
||||
|
||||
if (typeof amount?.value !== 'undefined' && typeof feePercentage?.value !== 'undefined') {
|
||||
return <span>The fee is ${(amount.value * feePercentage.value) / 100}</span>
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## useAllFormFields
|
||||
|
||||
**To retrieve more than one field**, you can use the `useAllFormFields` hook. Your component will re-render when _any_ field changes, so use this hook only if you absolutely need to. Unlike the `useFormFields` hook, this hook does not accept a "selector", and it always returns an array with type of `[fields: Fields, dispatch: React.Dispatch<Action>]]`.
|
||||
|
||||
You can do lots of powerful stuff by retrieving the full form state, like using built-in helper functions to reduce field state to values only, or to retrieve sibling data by path.
|
||||
|
||||
```tsx
|
||||
'use client'
|
||||
import { useAllFormFields } from '@payloadcms/ui'
|
||||
import { reduceFieldsToValues, getSiblingData } from 'payload/shared'
|
||||
|
||||
const ExampleComponent: React.FC = () => {
|
||||
// the `fields` const will be equal to all fields' state,
|
||||
// and the `dispatchFields` method is usable to send field state up to the form
|
||||
const [fields, dispatchFields] = useAllFormFields();
|
||||
|
||||
// Pass in fields, and indicate if you'd like to "unflatten" field data.
|
||||
// The result below will reflect the data stored in the form at the given time
|
||||
const formData = reduceFieldsToValues(fields, true);
|
||||
|
||||
// Pass in field state and a path,
|
||||
// and you will be sent all sibling data of the path that you've specified
|
||||
const siblingData = getSiblingData(fields, 'someFieldName');
|
||||
|
||||
return (
|
||||
// return some JSX here if necessary
|
||||
)
|
||||
};
|
||||
```
|
||||
|
||||
#### Updating other fields' values
|
||||
|
||||
If you are building a Custom Component, then you should use `setValue` which is returned from the `useField` hook to programmatically set your field's value. But if you're looking to update _another_ field's value, you can use `dispatchFields` returned from `useFormFields`.
|
||||
|
||||
You can send the following actions to the `dispatchFields` function.
|
||||
|
||||
| Action | Description |
|
||||
| ---------------------- | -------------------------------------------------------------------------- |
|
||||
| **`ADD_ROW`** | Adds a row of data (useful in array / block field data) |
|
||||
| **`DUPLICATE_ROW`** | Duplicates a row of data (useful in array / block field data) |
|
||||
| **`MODIFY_CONDITION`** | Updates a field's conditional logic result (true / false) |
|
||||
| **`MOVE_ROW`** | Moves a row of data (useful in array / block field data) |
|
||||
| **`REMOVE`** | Removes a field from form state |
|
||||
| **`REMOVE_ROW`** | Removes a row of data from form state (useful in array / block field data) |
|
||||
| **`REPLACE_STATE`** | Completely replaces form state |
|
||||
| **`UPDATE`** | Update any property of a specific field's state |
|
||||
|
||||
To see types for each action supported within the `dispatchFields` hook, check out the Form types [here](https://github.com/payloadcms/payload/blob/main/packages/payload/src/admin/components/forms/Form/types.ts).
|
||||
|
||||
## useForm
|
||||
|
||||
The `useForm` hook can be used to interact with the form itself, and sends back many methods that can be used to reactively fetch form state without causing rerenders within your components each time a field is changed. This is useful if you have action-based callbacks that your components fire, and need to interact with form state _based on a user action_.
|
||||
|
||||
<Banner type="warning">
|
||||
<strong>Warning:</strong>
|
||||
<br />
|
||||
This hook is optimized to avoid causing rerenders when fields change, and as such, its `fields`
|
||||
property will be out of date. You should only leverage this hook if you need to perform actions
|
||||
against the form in response to your users' actions. Do not rely on its returned "fields" as being
|
||||
up-to-date. They will be removed from this hook's response in an upcoming version.
|
||||
</Banner>
|
||||
|
||||
The `useForm` hook returns an object with the following properties:
|
||||
|
||||
<TableWithDrawers
|
||||
columns={[
|
||||
'Action',
|
||||
'Description',
|
||||
'Example',
|
||||
]}
|
||||
rows={[
|
||||
[
|
||||
{
|
||||
value: <strong><code>fields</code></strong>,
|
||||
},
|
||||
{
|
||||
value: "Deprecated. This property cannot be relied on as up-to-date.",
|
||||
},
|
||||
{
|
||||
value: ''
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
value: <strong><code>submit</code></strong>,
|
||||
},
|
||||
{
|
||||
value: "Method to trigger the form to submit",
|
||||
},
|
||||
{
|
||||
value: ''
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
value: <strong><code>dispatchFields</code></strong>,
|
||||
},
|
||||
{
|
||||
value: "Dispatch actions to the form field state",
|
||||
},
|
||||
{
|
||||
value: ''
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
value: <strong><code>validateForm</code></strong>,
|
||||
},
|
||||
{
|
||||
value: "Trigger a validation of the form state",
|
||||
},
|
||||
{
|
||||
value: ''
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
value: <strong><code>createFormData</code></strong>,
|
||||
},
|
||||
{
|
||||
value: <>Create a <code>multipart/form-data</code> object from the current form's state</>,
|
||||
},
|
||||
{
|
||||
value: ''
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
value: <strong><code>disabled</code></strong>,
|
||||
},
|
||||
{
|
||||
value: "Boolean denoting whether or not the form is disabled",
|
||||
},
|
||||
{
|
||||
value: ''
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
value: <strong><code>getFields</code></strong>,
|
||||
},
|
||||
{
|
||||
value: 'Gets all fields from state',
|
||||
},
|
||||
{
|
||||
value: '',
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
value: <strong><code>getField</code></strong>,
|
||||
},
|
||||
{
|
||||
value: 'Gets a single field from state by path',
|
||||
},
|
||||
{
|
||||
value: '',
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
value: <strong><code>getData</code></strong>,
|
||||
},
|
||||
{
|
||||
value: 'Returns the data stored in the form',
|
||||
},
|
||||
{
|
||||
value: '',
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
value: <strong><code>getSiblingData</code></strong>,
|
||||
},
|
||||
{
|
||||
value: 'Returns form sibling data for the given field path',
|
||||
},
|
||||
{
|
||||
value: '',
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
value: <strong><code>setModified</code></strong>,
|
||||
},
|
||||
{
|
||||
value: <>Set the form\'s <code>modified</code> state</>,
|
||||
},
|
||||
{
|
||||
value: '',
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
value: <strong><code>setProcessing</code></strong>,
|
||||
},
|
||||
{
|
||||
value: <>Set the form\'s <code>processing</code> state</>,
|
||||
},
|
||||
{
|
||||
value: '',
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
value: <strong><code>setSubmitted</code></strong>,
|
||||
},
|
||||
{
|
||||
value: <>Set the form\'s <code>submitted</code> state</>,
|
||||
},
|
||||
{
|
||||
value: '',
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
value: <strong><code>formRef</code></strong>,
|
||||
},
|
||||
{
|
||||
value: 'The ref from the form HTML element',
|
||||
},
|
||||
{
|
||||
value: '',
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
value: <strong><code>reset</code></strong>,
|
||||
},
|
||||
{
|
||||
value: 'Method to reset the form to its initial state',
|
||||
},
|
||||
{
|
||||
value: '',
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
value: <strong><code>addFieldRow</code></strong>,
|
||||
},
|
||||
{
|
||||
value: "Method to add a row on an array or block field",
|
||||
},
|
||||
{
|
||||
drawerTitle: 'addFieldRow',
|
||||
drawerDescription: 'A useful method to programmatically add a row to an array or block field.',
|
||||
drawerSlug: 'addFieldRow',
|
||||
drawerContent: (
|
||||
<>
|
||||
<TableWithDrawers
|
||||
columns={[
|
||||
'Prop',
|
||||
'Description',
|
||||
]}
|
||||
rows={[
|
||||
[
|
||||
{
|
||||
value: <strong><code>path</code></strong>,
|
||||
},
|
||||
{
|
||||
value: "The path to the array or block field",
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
value: <strong><code>rowIndex</code></strong>,
|
||||
},
|
||||
{
|
||||
value: "The index of the row to add. If omitted, the row will be added to the end of the array.",
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
value: <strong><code>data</code></strong>,
|
||||
},
|
||||
{
|
||||
value: "The data to add to the row",
|
||||
},
|
||||
],
|
||||
]}
|
||||
/>
|
||||
|
||||
{' '}
|
||||
|
||||
<br />
|
||||
|
||||
{' '}
|
||||
|
||||
<pre>
|
||||
{`import { useForm } from "payload/components/forms";
|
||||
|
||||
export const CustomArrayManager = () => {
|
||||
const { addFieldRow } = useForm()
|
||||
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
addFieldRow({
|
||||
path: "arrayField",
|
||||
rowIndex: 0,
|
||||
data: {
|
||||
textField: "text",
|
||||
// blockType: "yourBlockSlug",
|
||||
// ^ if managing a block array, you need to specify the block type
|
||||
},
|
||||
})
|
||||
}}
|
||||
>
|
||||
Add Row
|
||||
</button>
|
||||
)
|
||||
}`}
|
||||
</pre>
|
||||
|
||||
<p>An example config to go along with the Custom Component</p>
|
||||
<pre>
|
||||
{`const ExampleCollection = {
|
||||
slug: "example-collection",
|
||||
fields: [
|
||||
{
|
||||
name: "arrayField",
|
||||
type: "array",
|
||||
fields: [
|
||||
{
|
||||
name: "textField",
|
||||
type: "text",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: "ui",
|
||||
name: "customArrayManager",
|
||||
admin: {
|
||||
components: {
|
||||
Field: '/path/to/CustomArrayManagerField',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
}`}
|
||||
</pre>
|
||||
</>
|
||||
)
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
value: <strong><code>removeFieldRow</code></strong>,
|
||||
},
|
||||
{
|
||||
value: "Method to remove a row from an array or block field",
|
||||
},
|
||||
{
|
||||
drawerTitle: 'removeFieldRow',
|
||||
drawerDescription: 'A useful method to programmatically remove a row from an array or block field.',
|
||||
drawerSlug: 'removeFieldRow',
|
||||
drawerContent: (
|
||||
<>
|
||||
<TableWithDrawers
|
||||
columns={[
|
||||
'Prop',
|
||||
'Description',
|
||||
]}
|
||||
rows={[
|
||||
[
|
||||
{
|
||||
value: <strong><code>path</code></strong>,
|
||||
},
|
||||
{
|
||||
value: "The path to the array or block field",
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
value: <strong><code>rowIndex</code></strong>,
|
||||
},
|
||||
{
|
||||
value: "The index of the row to remove",
|
||||
},
|
||||
],
|
||||
]}
|
||||
/>
|
||||
|
||||
{' '}
|
||||
|
||||
<br />
|
||||
|
||||
{' '}
|
||||
|
||||
<pre>
|
||||
{`import { useForm } from "payload/components/forms";
|
||||
|
||||
export const CustomArrayManager = () => {
|
||||
const { removeFieldRow } = useForm()
|
||||
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
removeFieldRow({
|
||||
path: "arrayField",
|
||||
rowIndex: 0,
|
||||
})
|
||||
}}
|
||||
>
|
||||
Remove Row
|
||||
</button>
|
||||
)
|
||||
}`}
|
||||
</pre>
|
||||
|
||||
<p>An example config to go along with the Custom Component</p>
|
||||
<pre>
|
||||
{`const ExampleCollection = {
|
||||
slug: "example-collection",
|
||||
fields: [
|
||||
{
|
||||
name: "arrayField",
|
||||
type: "array",
|
||||
fields: [
|
||||
{
|
||||
name: "textField",
|
||||
type: "text",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: "ui",
|
||||
name: "customArrayManager",
|
||||
admin: {
|
||||
components: {
|
||||
Field: '/path/to/CustomArrayManagerField',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
}`}
|
||||
</pre>
|
||||
</>
|
||||
)
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
value: <strong><code>replaceFieldRow</code></strong>,
|
||||
},
|
||||
{
|
||||
value: "Method to replace a row from an array or block field",
|
||||
},
|
||||
{
|
||||
drawerTitle: 'replaceFieldRow',
|
||||
drawerDescription: 'A useful method to programmatically replace a row from an array or block field.',
|
||||
drawerSlug: 'replaceFieldRow',
|
||||
drawerContent: (
|
||||
<>
|
||||
<TableWithDrawers
|
||||
columns={[
|
||||
'Prop',
|
||||
'Description',
|
||||
]}
|
||||
rows={[
|
||||
[
|
||||
{
|
||||
value: <strong><code>path</code></strong>,
|
||||
},
|
||||
{
|
||||
value: "The path to the array or block field",
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
value: <strong><code>rowIndex</code></strong>,
|
||||
},
|
||||
{
|
||||
value: "The index of the row to replace",
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
value: <strong><code>data</code></strong>,
|
||||
},
|
||||
{
|
||||
value: "The data to replace within the row",
|
||||
},
|
||||
],
|
||||
]}
|
||||
/>
|
||||
|
||||
{' '}
|
||||
|
||||
<br />
|
||||
|
||||
{' '}
|
||||
|
||||
<pre>
|
||||
{`import { useForm } from "payload/components/forms";
|
||||
|
||||
export const CustomArrayManager = () => {
|
||||
const { replaceFieldRow } = useForm()
|
||||
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
replaceFieldRow({
|
||||
path: "arrayField",
|
||||
rowIndex: 0,
|
||||
data: {
|
||||
textField: "updated text",
|
||||
// blockType: "yourBlockSlug",
|
||||
// ^ if managing a block array, you need to specify the block type
|
||||
},
|
||||
})
|
||||
}}
|
||||
>
|
||||
Replace Row
|
||||
</button>
|
||||
)
|
||||
}`}
|
||||
</pre>
|
||||
|
||||
<p>An example config to go along with the Custom Component</p>
|
||||
<pre>
|
||||
{`const ExampleCollection = {
|
||||
slug: "example-collection",
|
||||
fields: [
|
||||
{
|
||||
name: "arrayField",
|
||||
type: "array",
|
||||
fields: [
|
||||
{
|
||||
name: "textField",
|
||||
type: "text",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: "ui",
|
||||
name: "customArrayManager",
|
||||
admin: {
|
||||
components: {
|
||||
Field: '/path/to/CustomArrayManagerField',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
}`}
|
||||
</pre>
|
||||
</>
|
||||
)
|
||||
}
|
||||
],
|
||||
]}
|
||||
/>
|
||||
|
||||
## useCollapsible
|
||||
|
||||
The `useCollapsible` hook allows you to control parent collapsibles:
|
||||
|
||||
| Property | Description |
|
||||
| ------------------------- | ------------------------------------------------------------------------------------------------------------- |
|
||||
| **`isCollapsed`** | State of the collapsible. `true` if open, `false` if collapsed. |
|
||||
| **`isVisible`** | If nested, determine if the nearest collapsible is visible. `true` if no parent is closed, `false` otherwise. |
|
||||
| **`toggle`** | Toggles the state of the nearest collapsible. |
|
||||
| **`isWithinCollapsible`** | Determine when you are within another collapsible. |
|
||||
|
||||
**Example:**
|
||||
|
||||
```tsx
|
||||
'use client'
|
||||
import React from 'react'
|
||||
|
||||
import { useCollapsible } from '@payloadcms/ui'
|
||||
|
||||
const CustomComponent: React.FC = () => {
|
||||
const { isCollapsed, toggle } = useCollapsible()
|
||||
|
||||
return (
|
||||
<div>
|
||||
<p className="field-type">I am {isCollapsed ? 'closed' : 'open'}</p>
|
||||
<button onClick={toggle} type="button">
|
||||
Toggle
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
## useDocumentInfo
|
||||
|
||||
The `useDocumentInfo` hook provides lots of information about the document currently being edited, including the following:
|
||||
|
||||
| Property | Description |
|
||||
| ------------------------- | ------------------------------------------------------------------------------------------------------------------ |
|
||||
| **`collection`** | If the doc is a collection, its Collection Config will be returned |
|
||||
| **`global`** | If the doc is a global, its Global Config will be returned |
|
||||
| **`id`** | If the doc is a collection, its ID will be returned |
|
||||
| **`preferencesKey`** | The `preferences` key to use when interacting with document-level user preferences |
|
||||
| **`versions`** | Versions of the current doc |
|
||||
| **`unpublishedVersions`** | Unpublished versions of the current doc |
|
||||
| **`publishedDoc`** | The currently published version of the doc being edited |
|
||||
| **`getVersions`** | Method to trigger the retrieval of document versions |
|
||||
| **`docPermissions`** | The current documents permissions. Collection document permissions fallback when no id is present (i.e. on create) |
|
||||
| **`getDocPermissions`** | Method to trigger the retrieval of document level permissions |
|
||||
|
||||
**Example:**
|
||||
|
||||
```tsx
|
||||
'use client'
|
||||
import { useDocumentInfo } from '@payloadcms/ui'
|
||||
|
||||
const LinkFromCategoryToPosts: React.FC = () => {
|
||||
// highlight-start
|
||||
const { id } = useDocumentInfo()
|
||||
// highlight-end
|
||||
|
||||
// id will be undefined on the create form
|
||||
if (!id) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<a href={`/admin/collections/posts?where[or][0][and][0][category][in][0]=[${id}]`}>
|
||||
View posts
|
||||
</a>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
## useLocale
|
||||
|
||||
In any Custom Component you can get the selected locale object with the `useLocale` hook. `useLocale` gives you the full locale object, consisting of a `label`, `rtl`(right-to-left) property, and then `code`. Here is a simple example:
|
||||
|
||||
```tsx
|
||||
'use client'
|
||||
import { useLocale } from '@payloadcms/ui'
|
||||
|
||||
const Greeting: React.FC = () => {
|
||||
// highlight-start
|
||||
const locale = useLocale()
|
||||
// highlight-end
|
||||
|
||||
const trans = {
|
||||
en: 'Hello',
|
||||
es: 'Hola',
|
||||
}
|
||||
|
||||
return <span> {trans[locale.code]} </span>
|
||||
}
|
||||
```
|
||||
|
||||
## useAuth
|
||||
|
||||
Useful to retrieve info about the currently logged in user as well as methods for interacting with it. It sends back an object with the following properties:
|
||||
|
||||
| Property | Description |
|
||||
| ------------------------ | --------------------------------------------------------------------------------------- |
|
||||
| **`user`** | The currently logged in user |
|
||||
| **`logOut`** | A method to log out the currently logged in user |
|
||||
| **`refreshCookie`** | A method to trigger the silent refreshing of a user's auth token |
|
||||
| **`setToken`** | Set the token of the user, to be decoded and used to reset the user and token in memory |
|
||||
| **`token`** | The logged in user's token (useful for creating preview links, etc.) |
|
||||
| **`refreshPermissions`** | Load new permissions (useful when content that effects permissions has been changed) |
|
||||
| **`permissions`** | The permissions of the current user |
|
||||
|
||||
```tsx
|
||||
'use client'
|
||||
import { useAuth } from '@payloadcms/ui'
|
||||
import type { User } from '../payload-types.ts'
|
||||
|
||||
const Greeting: React.FC = () => {
|
||||
// highlight-start
|
||||
const { user } = useAuth<User>()
|
||||
// highlight-end
|
||||
|
||||
return <span>Hi, {user.email}!</span>
|
||||
}
|
||||
```
|
||||
|
||||
## useConfig
|
||||
|
||||
Used to easily retrieve the Payload [Client Config](./components#accessing-the-payload-config).
|
||||
|
||||
```tsx
|
||||
'use client'
|
||||
import { useConfig } from '@payloadcms/ui'
|
||||
|
||||
const MyComponent: React.FC = () => {
|
||||
// highlight-start
|
||||
const { config } = useConfig()
|
||||
// highlight-end
|
||||
|
||||
return <span>{config.serverURL}</span>
|
||||
}
|
||||
```
|
||||
|
||||
## useEditDepth
|
||||
|
||||
Sends back how many editing levels "deep" the current component is. Edit depth is relevant while adding new documents / editing documents in modal windows and other cases.
|
||||
|
||||
```tsx
|
||||
'use client'
|
||||
import { useEditDepth } from '@payloadcms/ui'
|
||||
|
||||
const MyComponent: React.FC = () => {
|
||||
// highlight-start
|
||||
const editDepth = useEditDepth()
|
||||
// highlight-end
|
||||
|
||||
return <span>My component is {editDepth} levels deep</span>
|
||||
}
|
||||
```
|
||||
|
||||
## usePreferences
|
||||
|
||||
Returns methods to set and get user preferences. More info can be found [here](https://payloadcms.com/docs/admin/preferences).
|
||||
|
||||
## useTheme
|
||||
|
||||
Returns the currently selected theme (`light`, `dark` or `auto`), a set function to update it and a boolean `autoMode`, used to determine if the theme value should be set automatically based on the user's device preferences.
|
||||
|
||||
```tsx
|
||||
'use client'
|
||||
import { useTheme } from '@payloadcms/ui'
|
||||
|
||||
const MyComponent: React.FC = () => {
|
||||
// highlight-start
|
||||
const { autoMode, setTheme, theme } = useTheme()
|
||||
// highlight-end
|
||||
|
||||
return (
|
||||
<>
|
||||
<span>
|
||||
The current theme is {theme} and autoMode is {autoMode}
|
||||
</span>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setTheme((prev) => (prev === 'light' ? 'dark' : 'light'))}
|
||||
>
|
||||
Toggle theme
|
||||
</button>
|
||||
</>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
## useTableColumns
|
||||
|
||||
Returns methods to manipulate table columns
|
||||
|
||||
```tsx
|
||||
'use client'
|
||||
import { useTableColumns } from '@payloadcms/ui'
|
||||
|
||||
const MyComponent: React.FC = () => {
|
||||
// highlight-start
|
||||
const { setActiveColumns } = useTableColumns()
|
||||
|
||||
const resetColumns = () => {
|
||||
setActiveColumns(['id', 'createdAt', 'updatedAt'])
|
||||
}
|
||||
// highlight-end
|
||||
|
||||
return (
|
||||
<button type="button" onClick={resetColumns}>
|
||||
Reset columns
|
||||
</button>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
## useDocumentEvents
|
||||
|
||||
The `useDocumentEvents` hook provides a way of subscribing to cross-document events, such as updates made to nested documents within a drawer. This hook will report document events that are outside the scope of the document currently being edited. This hook provides the following:
|
||||
|
||||
| Property | Description |
|
||||
| ---------------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **`mostRecentUpdate`** | An object containing the most recently updated document. It contains the `entitySlug`, `id` (if collection), and `updatedAt` properties |
|
||||
| **`reportUpdate`** | A method used to report updates to documents. It accepts the same arguments as the `mostRecentUpdate` property. |
|
||||
|
||||
**Example:**
|
||||
|
||||
```tsx
|
||||
'use client'
|
||||
import { useDocumentEvents } from '@payloadcms/ui'
|
||||
|
||||
const ListenForUpdates: React.FC = () => {
|
||||
const { mostRecentUpdate } = useDocumentEvents()
|
||||
|
||||
return <span>{JSON.stringify(mostRecentUpdate)}</span>
|
||||
}
|
||||
```
|
||||
|
||||
<Banner type="info">
|
||||
Right now the `useDocumentEvents` hook only tracks recently updated documents, but in the future
|
||||
it will track more document-related events as needed, such as document creation, deletion, etc.
|
||||
</Banner>
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
title: Document Locking
|
||||
label: Document Locking
|
||||
order: 40
|
||||
order: 90
|
||||
desc: Ensure your documents are locked during editing to prevent concurrent changes from multiple users and maintain data integrity.
|
||||
keywords: locking, document locking, edit locking, document, concurrency, Payload, headless, Content Management System, cms, javascript, react, node, nextjs
|
||||
---
|
||||
@@ -20,17 +20,13 @@ When a user starts editing a document, Payload locks it for that user. If anothe
|
||||
|
||||
The lock will automatically expire after a set period of inactivity, configurable using the `duration` property in the `lockDocuments` configuration, after which others can resume editing.
|
||||
|
||||
<Banner type="info">
|
||||
**Note:** If your application does not require document locking, you can
|
||||
disable this feature for any collection or global by setting the
|
||||
`lockDocuments` property to `false`.
|
||||
</Banner>
|
||||
<Banner type="info"> <strong>Note:</strong> If your application does not require document locking, you can disable this feature for any collection or global by setting the <code>lockDocuments</code> property to <code>false</code>. </Banner>
|
||||
|
||||
### Config Options
|
||||
|
||||
The `lockDocuments` property exists on both the Collection Config and the Global Config. Document locking is enabled by default, but you can customize the lock duration or turn off the feature for any collection or global.
|
||||
|
||||
Here's an example configuration for document locking:
|
||||
Here’s an example configuration for document locking:
|
||||
|
||||
```ts
|
||||
import type { CollectionConfig } from 'payload'
|
||||
|
||||
@@ -6,11 +6,7 @@ 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.
|
||||
Every page within the Admin Panel automatically receives dynamic, auto-generated metadata derived from live document data, the user's current locale, and more, without any additional configuration. This includes the page title, description, og:image and everything in between. Metadata is fully configurable at the root level and cascades down to individual collections, documents, and custom views, allowing for the ability to control metadata on any page with high precision.
|
||||
|
||||
Within the Admin Panel, metadata can be customized at the following levels:
|
||||
|
||||
@@ -50,17 +46,19 @@ To customize Root Metadata, use the `admin.meta` key in your Payload Config:
|
||||
|
||||
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). |
|
||||
| Key | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| **`title`** | `string` | The title of the Admin Panel. |
|
||||
| **`description`** | `string` | The description of the Admin Panel. |
|
||||
| **`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. |
|
||||
| **`icons`** | `IconConfig[]` | An array of icon objects. [More details](#icons) |
|
||||
| **`keywords`** | `string` | A comma-separated list of keywords to include in the metadata of the Admin Panel. |
|
||||
| **`openGraph`** | `OpenGraphConfig` | An object containing Open Graph metadata. [More details](#open-graph) |
|
||||
| **`titleSuffix`** | `string` | A suffix to append to the end of the title of every page. Defaults to "- Payload". |
|
||||
|
||||
<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.
|
||||
<strong>Reminder:</strong>
|
||||
These are the _root-level_ options for the Admin Panel. You can also customize [Collection Metadata](./collections), [Global Metadata](./globals), and [Document Metadata](./documents) in their respective configs.
|
||||
</Banner>
|
||||
|
||||
### Icons
|
||||
@@ -69,7 +67,7 @@ The Icons Config corresponds to the `<link>` tags that are used to specify icons
|
||||
|
||||
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:
|
||||
To customize icons, use the `icons` key within the `admin.meta` object in your Payload Config:
|
||||
|
||||
```ts
|
||||
{
|
||||
@@ -95,13 +93,23 @@ To customize icons, use the `admin.meta.icons` property in your Payload Config:
|
||||
}
|
||||
```
|
||||
|
||||
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).
|
||||
The following options are available for Icons:
|
||||
|
||||
| Key | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| **`rel`** | `string` | The HTML `rel` attribute of the icon. |
|
||||
| **`type`** | `string` | The MIME type of the icon. |
|
||||
| **`color`** | `string` | The color of the icon. |
|
||||
| **`fetchPriority`** | `string` | The [fetch priority](https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement/fetchPriority) of the icon. |
|
||||
| **`media`** | `string` | The [media query](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_media_queries/Using_media_queries) of the icon. |
|
||||
| **`sizes`** | `string` | The [sizes](https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement/sizes) of the icon. |
|
||||
| **`url`** | `string` | The URL pointing the resource of the icon. |
|
||||
|
||||
### 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:
|
||||
To customize Open Graph metadata, use the `openGraph` key within the `admin.meta` object in your Payload Config:
|
||||
|
||||
```ts
|
||||
{
|
||||
@@ -127,47 +135,14 @@ To customize Open Graph metadata, use the `admin.meta.openGraph` property in you
|
||||
}
|
||||
```
|
||||
|
||||
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).
|
||||
The following options are available for Open Graph Metadata:
|
||||
|
||||
### 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>
|
||||
| Key | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| **`description`** | `string` | The description of the Admin Panel. |
|
||||
| **`images`** | `OGImageConfig` or `OGImageConfig[]` | An array of image objects. |
|
||||
| **`siteName`** | `string` | The name of the site. |
|
||||
| **`title`** | `string` | The title of the Admin Panel. |
|
||||
|
||||
## Collection Metadata
|
||||
|
||||
@@ -183,7 +158,7 @@ export const MyCollection: CollectionConfig = {
|
||||
admin: {
|
||||
// highlight-start
|
||||
meta: {
|
||||
// highlight-end
|
||||
// highlight-end
|
||||
title: 'My Collection',
|
||||
description: 'The best collection in the world',
|
||||
},
|
||||
@@ -207,9 +182,9 @@ export const MyGlobal: GlobalConfig = {
|
||||
admin: {
|
||||
// highlight-start
|
||||
meta: {
|
||||
// highlight-end
|
||||
// highlight-end
|
||||
title: 'My Global',
|
||||
description: 'The best admin panel in the world',
|
||||
description: 'The best
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -219,7 +194,7 @@ The Global Meta config has the same options as the [Root Metadata](#root-metadat
|
||||
|
||||
## 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.
|
||||
View Metadata is the metadata that is applied to specific [Views](./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:
|
||||
|
||||
@@ -239,4 +214,3 @@ To customize View Metadata, use the `meta` key within your View Config:
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
@@ -6,16 +6,14 @@ desc: Manage your data and customize the Payload Admin Panel by swapping in your
|
||||
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.
|
||||
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, [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 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](./components)—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).
|
||||
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).
|
||||
|
||||
<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).
|
||||
The Payload Admin Panel is designed to be as minimal and straightforward as possible to allow easy customization and control. [Learn more](./components).
|
||||
</Banner>
|
||||
|
||||
<LightDarkImage
|
||||
@@ -50,8 +48,7 @@ app/
|
||||
```
|
||||
|
||||
<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).
|
||||
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.
|
||||
@@ -59,11 +56,8 @@ As shown above, all Payload routes are nested within the `(payload)` route group
|
||||
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.
|
||||
<strong>Note:</strong>
|
||||
If you don't use the [REST API](../rest/overview) or [GraphQL API](../graphql/overview), you can delete the [Next.js files corresponding to those routes](../admin/overview#project-structure), however, the overhead of this API is completely constrained to these endpoints, and will not slow down or affect Payload outside of the endpoints.
|
||||
</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).
|
||||
@@ -84,8 +78,7 @@ import { buildConfig } from 'payload'
|
||||
|
||||
const config = buildConfig({
|
||||
// ...
|
||||
admin: {
|
||||
// highlight-line
|
||||
admin: { // highlight-line
|
||||
// ...
|
||||
},
|
||||
})
|
||||
@@ -93,28 +86,24 @@ const config = buildConfig({
|
||||
|
||||
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). |
|
||||
| **`buildPath`** | Specify an absolute path for where to store the built Admin bundle used in production. Defaults to `path.resolve(process.cwd(), 'build')`. |
|
||||
| **`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). |
|
||||
| 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). |
|
||||
| **`buildPath`** | Specify an absolute path for where to store the built Admin bundle used in production. Defaults to `path.resolve(process.cwd(), 'build')`. |
|
||||
| **`components`** | Component overrides that affect the entirety of the Admin Panel. [More details](./components). |
|
||||
| **`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. |
|
||||
| **`disable`** | If set to `true`, the entire Admin Panel will be disabled. |
|
||||
| **`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). |
|
||||
| **`theme`** | Restrict the Admin Panel theme to use only one of your choice. Default is `all`.
|
||||
| **`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.
|
||||
<strong>Reminder:</strong>
|
||||
These are the _root-level_ options for the Admin Panel. You can also customize [Collection Admin Options](./collections) and [Global Admin Options](./globals) through their respective `admin` keys.
|
||||
</Banner>
|
||||
|
||||
### The Admin User Collection
|
||||
@@ -133,10 +122,9 @@ const config = buildConfig({
|
||||
```
|
||||
|
||||
<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.
|
||||
|
||||
<strong>Important:</strong>
|
||||
<br />
|
||||
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.
|
||||
@@ -146,7 +134,7 @@ You can use whatever Collection you'd like to access the Admin Panel as long as
|
||||
- `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.
|
||||
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
|
||||
|
||||
@@ -155,7 +143,7 @@ It is also possible to allow multiple user types into the Admin Panel with limit
|
||||
- `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).
|
||||
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/payload).
|
||||
|
||||
## Customizing Routes
|
||||
|
||||
@@ -173,24 +161,23 @@ import { buildConfig } from 'payload'
|
||||
const config = buildConfig({
|
||||
// ...
|
||||
routes: {
|
||||
admin: '/custom-admin-route', // highlight-line
|
||||
},
|
||||
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="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).
|
||||
<strong>Tip:</strong>
|
||||
You can easily add _new_ routes to the Admin Panel through [Custom Endpoints](../rest-api/overview#custom-endpoints) and [Custom Views](./views).
|
||||
</Banner>
|
||||
|
||||
#### Customizing Root-level Routes
|
||||
@@ -207,9 +194,8 @@ app/
|
||||
```
|
||||
|
||||
<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.
|
||||
<strong>Note:</strong>
|
||||
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
|
||||
@@ -225,54 +211,34 @@ const config = buildConfig({
|
||||
// ...
|
||||
admin: {
|
||||
routes: {
|
||||
account: '/my-account', // highlight-line
|
||||
},
|
||||
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. |
|
||||
| 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.
|
||||
<strong>Note:</strong>
|
||||
You can also swap out entire _views_ out for your own, using the `admin.views` property of the Payload Config. See [Custom Views](./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.
|
||||
The Payload Admin Panel is translated in over [30 languages and counting](https://github.com/payloadcms/payload/tree/beta/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>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
title: Managing User Preferences
|
||||
label: Preferences
|
||||
order: 60
|
||||
order: 70
|
||||
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
|
||||
---
|
||||
@@ -16,11 +16,10 @@ Out of the box, Payload handles the persistence of your users' preferences in a
|
||||
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.
|
||||
|
||||
<strong>Important:</strong>
|
||||
<br />
|
||||
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
|
||||
@@ -48,7 +47,7 @@ Payload automatically creates an internally used `payload-preferences` Collectio
|
||||
|
||||
## APIs
|
||||
|
||||
Preferences are available to both [GraphQL](/docs/graphql/overview#preferences) and [REST](/docs/rest-api/overview#preferences) APIs.
|
||||
Preferences are available to both [GraphQL](/docs/graphql/overview#preferences) and [REST](/docs/rest-api/overview#) APIs.
|
||||
|
||||
## Adding or reading Preferences in your own components
|
||||
|
||||
@@ -77,70 +76,86 @@ Here is an example for how you can utilize `usePreferences` within your custom A
|
||||
|
||||
```tsx
|
||||
'use client'
|
||||
import React, { Fragment, useState, useEffect, useCallback } from 'react'
|
||||
import React, { Fragment, useState, useEffect, useCallback } from 'react';
|
||||
import { usePreferences } from '@payloadcms/ui'
|
||||
|
||||
const lastUsedColorsPreferenceKey = 'last-used-colors'
|
||||
const lastUsedColorsPreferenceKey = 'last-used-colors';
|
||||
|
||||
export function CustomComponent() {
|
||||
const { getPreference, setPreference } = usePreferences()
|
||||
const CustomComponent = (props) => {
|
||||
const { getPreference, setPreference } = usePreferences();
|
||||
|
||||
// Store the last used colors in local state
|
||||
const [lastUsedColors, setLastUsedColors] = useState([])
|
||||
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
|
||||
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]
|
||||
if (!colorAlreadyExists) {
|
||||
const newLastUsedColors = [
|
||||
...lastUsedColors,
|
||||
color,
|
||||
];
|
||||
|
||||
setLastUsedColors(newLastUsedColors)
|
||||
setPreference(lastUsedColorsPreferenceKey, newLastUsedColors)
|
||||
}
|
||||
},
|
||||
[lastUsedColors, setPreference],
|
||||
)
|
||||
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)
|
||||
}
|
||||
const lastUsedColorsFromPreferences = await getPreference(lastUsedColorsPreferenceKey);
|
||||
setLastUsedColors(lastUsedColorsFromPreferences);
|
||||
};
|
||||
|
||||
asyncGetPreference()
|
||||
}, [getPreference])
|
||||
asyncGetPreference();
|
||||
}, [getPreference]);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<button type="button" onClick={() => updateLastUsedColors('red')}>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => updateLastUsedColors('red')}
|
||||
>
|
||||
Use red
|
||||
</button>
|
||||
<button type="button" onClick={() => updateLastUsedColors('blue')}>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => updateLastUsedColors('blue')}
|
||||
>
|
||||
Use blue
|
||||
</button>
|
||||
<button type="button" onClick={() => updateLastUsedColors('purple')}>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => updateLastUsedColors('purple')}
|
||||
>
|
||||
Use purple
|
||||
</button>
|
||||
<button type="button" onClick={() => updateLastUsedColors('yellow')}>
|
||||
<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>)}
|
||||
{lastUsedColors?.map((color) => (
|
||||
<li key={color}>
|
||||
{color}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</Fragment>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
export default CustomComponent;
|
||||
```
|
||||
|
||||
@@ -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 very 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
372
docs/admin/views.mdx
Normal file
372
docs/admin/views.mdx
Normal file
@@ -0,0 +1,372 @@
|
||||
---
|
||||
title: Customizing Views
|
||||
label: Customizing Views
|
||||
order: 50
|
||||
desc:
|
||||
keywords:
|
||||
---
|
||||
|
||||
Views are the individual pages that make up the [Admin Panel](./overview), such as the Dashboard, List, and Edit views. One of the most powerful ways to customize the Admin Panel is to create Custom Views. These are [Custom Components](./components) that can either replace built-in views or can 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 consult the list of available components, determine the scope that corresponds to what you are trying to accomplish, then [author your React component(s)](#building-custom-views) accordingly.
|
||||
|
||||
## Root Views
|
||||
|
||||
Root Views are the main views of the [Admin Panel](./overview). These are views that are scoped directly under the `/admin` route, such as the Dashboard or Account views.
|
||||
|
||||
To easily swap Root Views with your own, or to [create entirely new ones](#adding-new-root-views), use the `admin.components.views` property of your root [Payload Config](../configuration/overview):
|
||||
|
||||
```ts
|
||||
import { buildConfig } from 'payload'
|
||||
|
||||
const config = buildConfig({
|
||||
// ...
|
||||
admin: {
|
||||
components: {
|
||||
views: {
|
||||
customView: {
|
||||
Component: '/path/to/MyCustomView#MyCustomView', // highlight-line
|
||||
path: '/my-custom-view',
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
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 what that might look like:
|
||||
|
||||
```tsx
|
||||
import type { AdminViewProps } from 'payload'
|
||||
|
||||
import { DefaultTemplate } from '@payloadcms/next/templates'
|
||||
import { Gutter } from '@payloadcms/ui'
|
||||
import React from 'react'
|
||||
|
||||
export const MyCustomView: React.FC<AdminViewProps> = ({
|
||||
initPageResult,
|
||||
params,
|
||||
searchParams,
|
||||
}) => {
|
||||
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>
|
||||
<br />
|
||||
<p>This view uses the Default Template.</p>
|
||||
</Gutter>
|
||||
</DefaultTemplate>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
_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](./overview). |
|
||||
|
||||
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. |
|
||||
| **`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](./metadata). |
|
||||
|
||||
_\* An asterisk denotes that a property is required._
|
||||
|
||||
### Adding New Views
|
||||
|
||||
To add a _new_ views to the [Admin Panel](./overview), simply add your own key to the `views` object with at least a `path` and `Component` property. For example:
|
||||
|
||||
```ts
|
||||
import { buildConfig } from 'payload'
|
||||
|
||||
const config = buildConfig({
|
||||
// ...
|
||||
admin: {
|
||||
components: {
|
||||
views: {
|
||||
// highlight-start
|
||||
myCustomView: {
|
||||
// highlight-end
|
||||
Component: '/path/to/MyCustomView#MyCustomViewComponent',
|
||||
path: '/my-custom-view',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
The above example shows how to add a new [Root View](#root-views), but the pattern is the same for [Collection Views](#collection-views), [Global Views](#global-views), and [Document Views](#document-views). For help on how to build your own Custom Views, see [Building Custom Views](#building-custom-views).
|
||||
|
||||
<Banner type="warning">
|
||||
<strong>Note:</strong>
|
||||
<br />
|
||||
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>
|
||||
|
||||
<Banner type="warning">
|
||||
<strong>Custom views are public</strong>
|
||||
<br />
|
||||
Custom views are public by default. 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.
|
||||
</Banner>
|
||||
|
||||
## Collection Views
|
||||
|
||||
Collection Views are views that are scoped under the `/collections` route, such as the Collection List and Document Edit views.
|
||||
|
||||
To easily swap out Collection Views with your own, or to [create entirely new ones](#adding-new-views), use the `admin.components.views` property of your [Collection Config](../collections/overview):
|
||||
|
||||
```ts
|
||||
import type { SanitizedCollectionConfig } from 'payload'
|
||||
|
||||
export const MyCollectionConfig: SanitizedCollectionConfig = {
|
||||
// ...
|
||||
admin: {
|
||||
components: {
|
||||
views: {
|
||||
edit: {
|
||||
root: {
|
||||
Component: '/path/to/MyCustomEditView', // highlight-line
|
||||
}
|
||||
// other options include:
|
||||
// default
|
||||
// versions
|
||||
// version
|
||||
// api
|
||||
// livePreview
|
||||
// [key: string]
|
||||
// See "Document Views" for more details
|
||||
},
|
||||
list: {
|
||||
Component: '/path/to/MyCustomListView',
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
_For details on how to build Custom Views, including all available props, see [Building Custom Views](#building-custom-views)._
|
||||
|
||||
<Banner type="warning">
|
||||
<strong>Note:</strong>
|
||||
The `root` property will replace the _entire_ Edit View, including the title, tabs, etc., _as well as all nested [Document Views](#document-views)_, such as the API, Live Preview, and Version views. To replace only the Edit View precisely, use the `edit.default` key instead.
|
||||
</Banner>
|
||||
|
||||
The following options are available:
|
||||
|
||||
| Property | Description |
|
||||
| ---------- | ----------------------------------------------------------------------------------------------------------------- |
|
||||
| **`edit`** | The Edit View is used to edit a single document for any given Collection. [More details](#document-views). |
|
||||
| **`list`** | The List View is used to show a list of documents for any given Collection. |
|
||||
|
||||
<Banner type="success">
|
||||
<strong>Note:</strong>
|
||||
You can also add _new_ Collection Views to the config by adding a new key to the `views` object with at least a `path` and `Component` property. See [Adding New Views](#adding-new-views) for more information.
|
||||
</Banner>
|
||||
|
||||
## Global Views
|
||||
|
||||
Global Views are views that are scoped under the `/globals` route, such as the Document Edit View.
|
||||
|
||||
To easily swap out Global Views with your own or [create entirely new ones](#adding-new-views), use the `admin.components.views` property in your [Global Config](../globals/overview):
|
||||
|
||||
```ts
|
||||
import type { SanitizedGlobalConfig } from 'payload'
|
||||
|
||||
export const MyGlobalConfig: SanitizedGlobalConfig = {
|
||||
// ...
|
||||
admin: {
|
||||
components: {
|
||||
views: {
|
||||
edit: {
|
||||
root: {
|
||||
Component: '/path/to/MyCustomEditView', // highlight-line
|
||||
}
|
||||
// other options include:
|
||||
// default
|
||||
// versions
|
||||
// version
|
||||
// api
|
||||
// livePreview
|
||||
// [key: string]
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
_For details on how to build Custom Views, including all available props, see [Building Custom Views](#building-custom-views)._
|
||||
|
||||
<Banner type="warning">
|
||||
<strong>Note:</strong>
|
||||
The `root` property will replace the _entire_ Edit View, including the title, tabs, etc., _as well as all nested [Document Views](#document-views)_, such as the API, Live Preview, and Version views. To replace only the Edit View precisely, use the `edit.default` key instead.
|
||||
</Banner>
|
||||
|
||||
The following options are available:
|
||||
|
||||
| Property | Description |
|
||||
| ---------- | ------------------------------------------------------------------- |
|
||||
| **`edit`** | The Edit View is used to edit a single document for any given Global. [More details](#document-views). |
|
||||
|
||||
<Banner type="success">
|
||||
<strong>Note:</strong>
|
||||
You can also add _new_ Global Views to the config by adding a new key to the `views` object with at least a `path` and `Component` property. See [Adding New Views](#adding-new-views) for more information.
|
||||
</Banner>
|
||||
|
||||
## Document Views
|
||||
|
||||
Document Views are views that are scoped under the `/collections/:collectionSlug/:id` or the `/globals/:globalSlug` route, such as the Edit View or the API View. All Document Views keep their overall structure across navigation changes, such as their title and tabs, and replace only the content below.
|
||||
|
||||
To easily swap out Document Views with your own, or to [create entirely new ones](#adding-new-document-views), use the `admin.components.views.Edit[key]` property in your [Collection Config](../collections/overview) or [Global Config](../globals/overview):
|
||||
|
||||
```ts
|
||||
import type { SanitizedCollectionConfig } from 'payload'
|
||||
|
||||
export const MyCollectionOrGlobalConfig: SanitizedCollectionConfig = {
|
||||
// ...
|
||||
admin: {
|
||||
components: {
|
||||
views: {
|
||||
edit: {
|
||||
api: {
|
||||
Component: '/path/to/MyCustomAPIViewComponent', // highlight-line
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
_For details on how to build Custom Views, including all available props, see [Building Custom Views](#building-custom-views)._
|
||||
|
||||
<Banner type="warning">
|
||||
<strong>Note:</strong>
|
||||
If you need to replace the _entire_ Edit View, including _all_ nested Document Views, use the `root` key. See [Custom Collection Views](#collection-views) or [Custom Global Views](#global-views) for more information.
|
||||
</Banner>
|
||||
|
||||
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. |
|
||||
| **`default`** | The Default View is the primary view in which your document is edited. It is rendered within the "Edit" tab. |
|
||||
| **`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). |
|
||||
| **`version`** | The Version View is used to edit a single version of a document. It is rendered within the "Version" tab. [More details](../versions). |
|
||||
| **`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). |
|
||||
|
||||
### Document Tabs
|
||||
|
||||
Each Document View can be given a new tab in the Edit View, 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 Edit View, use the `tab` key:
|
||||
|
||||
```ts
|
||||
import type { SanitizedCollectionConfig } from 'payload'
|
||||
|
||||
export const MyCollection: SanitizedCollectionConfig = {
|
||||
slug: 'my-collection',
|
||||
admin: {
|
||||
components: {
|
||||
views: {
|
||||
edit: {
|
||||
myCustomTab: {
|
||||
Component: '/path/to/MyCustomTab',
|
||||
path: '/my-custom-tab',
|
||||
tab: {
|
||||
Component: '/path/to/MyCustomTabComponent' // highlight-line
|
||||
}
|
||||
},
|
||||
anotherCustomTab: {
|
||||
Component: '/path/to/AnotherCustomView',
|
||||
path: '/another-custom-view',
|
||||
// highlight-start
|
||||
tab: {
|
||||
label: 'Another Custom View',
|
||||
href: '/another-custom-view',
|
||||
}
|
||||
// highlight-end
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
<Banner type="warning">
|
||||
<strong>Note:</strong>
|
||||
This applies to _both_ Collections _and_ Globals.
|
||||
</Banner>
|
||||
|
||||
## Building Custom Views
|
||||
|
||||
Custom Views are just [Custom Components](./components) rendered at the page-level. To understand how to build Custom Views, first review the [Building Custom Components](./components#building-custom-components) guide. Once you have a Custom Component ready, you can use it as a Custom View.
|
||||
|
||||
```ts
|
||||
import type { SanitizedCollectionConfig } from 'payload'
|
||||
|
||||
export const MyCollectionConfig: SanitizedCollectionConfig = {
|
||||
// ...
|
||||
admin: {
|
||||
components: {
|
||||
views: {
|
||||
edit: {
|
||||
Component: '/path/to/MyCustomView' // highlight-line
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
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](../components#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). |
|
||||
|
||||
<Banner type="success">
|
||||
<strong>Reminder:</strong>
|
||||
All [Custom Server Components](./components) receive `payload` and `i18n` by default. See [Building Custom Components](./components#building-custom-components) for more details.
|
||||
</Banner>
|
||||
|
||||
<Banner type="warning">
|
||||
<strong>Important:</strong>
|
||||
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.
|
||||
</Banner>
|
||||
@@ -14,10 +14,9 @@ For example, if you have a third-party service or external app that needs to be
|
||||
1. Generate a non-expiring API key for that user to request with.
|
||||
|
||||
<Banner type="success">
|
||||
**Tip:**
|
||||
|
||||
This is particularly useful as you can create a "user" that reflects an integration with a specific external service and assign a "role" or specific access only needed by that service/integration.
|
||||
|
||||
<strong>Tip:</strong>
|
||||
<br/>
|
||||
This is particularly useful as you can create a "user" that reflects an integration with a specific external service and assign a "role" or specific access only needed by that service/integration.
|
||||
</Banner>
|
||||
|
||||
Technically, both of these options will work for third-party integrations but the second option with API key is simpler, because it reduces the amount of work that your integrations need to do to be authenticated properly.
|
||||
@@ -40,12 +39,11 @@ User API keys are encrypted within the database, meaning that if your database i
|
||||
your API keys will not be.
|
||||
|
||||
<Banner type="warning">
|
||||
**Important:**
|
||||
<strong>Important:</strong>
|
||||
If you change your `PAYLOAD_SECRET`, you will need to regenerate your API keys.
|
||||
|
||||
The secret key is used to encrypt the API keys, so if you change the secret, existing API keys will
|
||||
no longer be valid.
|
||||
|
||||
<br />
|
||||
The secret key is used to encrypt the API keys, so if you change the secret, existing API keys will
|
||||
no longer be valid.
|
||||
</Banner>
|
||||
|
||||
### HTTP Authentication
|
||||
|
||||
@@ -9,9 +9,8 @@ keywords: authentication, config, configuration, documentation, Content Manageme
|
||||
Payload offers the ability to [Authenticate](./overview) via HTTP-only cookies. These can be read from the responses of `login`, `logout`, `refresh`, and `me` auth operations.
|
||||
|
||||
<Banner type="success">
|
||||
**Tip:** You can access the logged-in user from within [Access
|
||||
Control](../access-control/overview) and [Hooks](../hooks/overview) through
|
||||
the `req.user` argument. [More details](./token-data).
|
||||
<strong>Tip:</strong>
|
||||
You can access the logged-in user from within [Access Control](../access-control/overview) and [Hooks](../hooks/overview) through the `req.user` argument. [More details](./token-data).
|
||||
</Banner>
|
||||
|
||||
### Automatic browser inclusion
|
||||
@@ -35,17 +34,17 @@ const pages = await response.json()
|
||||
For more about including cookies in requests from your app to your Payload API, [read the MDN docs](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch#Sending_a_request_with_credentials_included).
|
||||
|
||||
<Banner type="success">
|
||||
**Tip:** To make sure you have a Payload cookie set properly in your browser
|
||||
after logging in, you can use the browsers Developer Tools > Application >
|
||||
Cookies > [your-domain-here]. The Developer tools will still show HTTP-only
|
||||
cookies.
|
||||
<strong>Tip:</strong>
|
||||
To make sure you have a Payload cookie set properly in your browser after logging in, you can use
|
||||
the browsers Developer Tools > Application > Cookies > [your-domain-here]. The Developer tools
|
||||
will still show HTTP-only cookies.
|
||||
</Banner>
|
||||
|
||||
### CSRF Attacks
|
||||
|
||||
CSRF (cross-site request forgery) attacks are common and dangerous. By using an HTTP-only cookie, Payload removes many XSS vulnerabilities, however, CSRF attacks can still be possible.
|
||||
|
||||
For example, let's say you have a popular app `https://payload-finances.com` that allows users to manage finances, send and receive money. As Payload is using HTTP-only cookies, that means that browsers automatically will include cookies when sending requests to your domain - **no matter what page created the request**.
|
||||
For example, let's say you have a popular app `https://payload-finances.com` that allows users to manage finances, send and receive money. As Payload is using HTTP-only cookies, that means that browsers automatically will include cookies when sending requests to your domain - <strong>no matter what page created the request</strong>.
|
||||
|
||||
So, if a user of `https://payload-finances.com` is logged in and is browsing around on the internet, they might stumble onto a page with malicious intent. Let's look at an example:
|
||||
|
||||
@@ -54,8 +53,8 @@ So, if a user of `https://payload-finances.com` is logged in and is browsing aro
|
||||
// makes an authenticated request as on your behalf
|
||||
|
||||
const maliciousRequest = await fetch(`https://payload-finances.com/api/me`, {
|
||||
credentials: 'include',
|
||||
}).then((res) => await res.json())
|
||||
credentials: 'include'
|
||||
}).then(res => await res.json())
|
||||
```
|
||||
|
||||
In this scenario, if your cookie was still valid, malicious-intent.com would be able to make requests like the one above on your behalf. This is a CSRF attack.
|
||||
@@ -125,8 +124,8 @@ Configuration example:
|
||||
|
||||
If you're configuring [cors](../production/preventing-abuse#cross-origin-resource-sharing-cors) in your Payload config, you won't be able to use a wildcard anymore, you'll need to specify the list of allowed domains.
|
||||
|
||||
|
||||
<Banner type="success">
|
||||
**Good to know:** Setting up `secure: true` will not work if you're developing
|
||||
on `http://localhost` or any non-https domain. For local development you
|
||||
should conditionally set this to `false` based on the environment.
|
||||
<strong>Good to know:</strong>
|
||||
Setting up <code>secure: true</code> will not work if you're developing on <code>http://localhost</code> or any non-https domain. For local development you should conditionally set this to <code>false</code> based on the environment.
|
||||
</Banner>
|
||||
|
||||
@@ -7,9 +7,8 @@ keywords: authentication, config, configuration, overview, documentation, Conten
|
||||
---
|
||||
|
||||
<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.
|
||||
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
|
||||
@@ -18,18 +17,19 @@ At the core, a strategy is a way to authenticate a user making a request. As of
|
||||
|
||||
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. |
|
||||
| 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 |
|
||||
| ---------------- | ------------------------------------------------------------------------------------------------- |
|
||||
| **`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 request was made from a GraphQL endpoint. Default is `false`. |
|
||||
| Argument | Description |
|
||||
| ------------------- | ------------------------------------------------------------------------------------------------- |
|
||||
| **`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 request was made from a GraphQL endpoint. Default is `false`. |
|
||||
|
||||
|
||||
### Example Strategy
|
||||
|
||||
@@ -62,12 +62,9 @@ export const Users: CollectionConfig = {
|
||||
})
|
||||
|
||||
return {
|
||||
// Send the user with the collection slug back to authenticate,
|
||||
// Send the user back to authenticate,
|
||||
// or send null if no user should be authenticated
|
||||
user: usersQuery.docs[0] ? {
|
||||
collection: 'users'
|
||||
...usersQuery.docs[0],
|
||||
} : null,
|
||||
user: usersQuery.docs[0] || null,
|
||||
|
||||
// Optionally, you can return headers
|
||||
// that you'd like Payload to set here when
|
||||
|
||||
@@ -20,22 +20,22 @@ import type { CollectionConfig } from 'payload'
|
||||
export const Customers: CollectionConfig = {
|
||||
// ...
|
||||
auth: {
|
||||
verify: true, // highlight-line
|
||||
verify: true // highlight-line
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
<Banner type="info">
|
||||
**Tip:** Verification emails are fully customizable. [More
|
||||
details](#generateemailhtml).
|
||||
<strong>Tip:</strong>
|
||||
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). |
|
||||
| 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
|
||||
|
||||
@@ -62,11 +62,11 @@ export const Customers: CollectionConfig = {
|
||||
```
|
||||
|
||||
<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.
|
||||
<strong>Important:</strong>
|
||||
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
|
||||
@@ -82,11 +82,11 @@ export const Customers: CollectionConfig = {
|
||||
verify: {
|
||||
// highlight-start
|
||||
generateEmailSubject: ({ req, user }) => {
|
||||
return `Hey ${user.email}, reset your password!`
|
||||
},
|
||||
return `Hey ${user.email}, reset your password!`;
|
||||
}
|
||||
// highlight-end
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@@ -100,8 +100,7 @@ import type { CollectionConfig } from 'payload'
|
||||
export const Customers: CollectionConfig = {
|
||||
// ...
|
||||
auth: {
|
||||
forgotPassword: {
|
||||
// highlight-line
|
||||
forgotPassword: { // highlight-line
|
||||
// ...
|
||||
},
|
||||
},
|
||||
@@ -110,10 +109,9 @@ export const Customers: CollectionConfig = {
|
||||
|
||||
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). |
|
||||
| Option | Description |
|
||||
|----------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| **`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
|
||||
@@ -153,28 +151,28 @@ export const Customers: CollectionConfig = {
|
||||
```
|
||||
|
||||
<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.
|
||||
<strong>Important:</strong>
|
||||
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.
|
||||
<strong>Tip:</strong>
|
||||
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. |
|
||||
| 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
|
||||
|
||||
@@ -189,17 +187,17 @@ export const Customers: CollectionConfig = {
|
||||
forgotPassword: {
|
||||
// highlight-start
|
||||
generateEmailSubject: ({ req, user }) => {
|
||||
return `Hey ${user.email}, reset your password!`
|
||||
},
|
||||
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. |
|
||||
| Argument | Description |
|
||||
|----------|-----------------------------------------------------------------------------------------------|
|
||||
| `req` | The request object. |
|
||||
| `user` | The user document that is attempting to reset their password. |
|
||||
|
||||
@@ -9,9 +9,8 @@ keywords: authentication, config, configuration, documentation, Content Manageme
|
||||
Payload offers the ability to [Authenticate](./overview) via JSON Web Tokens (JWT). These can be read from the responses of `login`, `logout`, `refresh`, and `me` auth operations.
|
||||
|
||||
<Banner type="success">
|
||||
**Tip:** You can access the logged-in user from within [Access
|
||||
Control](../access-control/overview) and [Hooks](../hooks/overview) through
|
||||
the `req.user` argument. [More details](./token-data).
|
||||
<strong>Tip:</strong>
|
||||
You can access the logged-in user from within [Access Control](../access-control/overview) and [Hooks](../hooks/overview) through the `req.user` argument. [More details](./token-data).
|
||||
</Banner>
|
||||
|
||||
### Identifying Users Via The Authorization Header
|
||||
@@ -26,8 +25,8 @@ const user = await fetch('http://localhost:3000/api/users/login', {
|
||||
body: JSON.stringify({
|
||||
email: 'dev@payloadcms.com',
|
||||
password: 'password',
|
||||
}),
|
||||
}).then((req) => await req.json())
|
||||
})
|
||||
}).then(req => await req.json())
|
||||
|
||||
const request = await fetch('http://localhost:3000', {
|
||||
headers: {
|
||||
@@ -46,7 +45,7 @@ import type { CollectionConfig } from 'payload'
|
||||
export const UsersWithoutJWTs: CollectionConfig = {
|
||||
slug: 'users-without-jwts',
|
||||
auth: {
|
||||
removeTokenFromResponse: true, // highlight-line
|
||||
removeTokenFromRepsonse: true, // highlight-line
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
@@ -158,7 +158,7 @@ mutation {
|
||||
|
||||
```ts
|
||||
const result = await payload.login({
|
||||
collection: 'collection-slug',
|
||||
collection: '[collection-slug]',
|
||||
data: {
|
||||
email: 'dev@payloadcms.com',
|
||||
password: 'get-out',
|
||||
@@ -166,13 +166,6 @@ const result = await payload.login({
|
||||
})
|
||||
```
|
||||
|
||||
<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.
|
||||
@@ -196,13 +189,6 @@ mutation {
|
||||
}
|
||||
```
|
||||
|
||||
<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>
|
||||
|
||||
## 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.
|
||||
@@ -214,15 +200,12 @@ If successful, this operation will automatically renew the user's HTTP-only cook
|
||||
**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 res = await fetch('http://localhost:3000/api/[collection-slug]/refresh-token', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
)
|
||||
})
|
||||
|
||||
const json = await res.json()
|
||||
|
||||
@@ -254,13 +237,6 @@ mutation {
|
||||
}
|
||||
```
|
||||
|
||||
<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.
|
||||
@@ -268,15 +244,12 @@ If your collection supports email verification, the Verify operation will be exp
|
||||
**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',
|
||||
},
|
||||
const res = await fetch(`http://localhost:3000/api/[collection-slug]/verify/${TOKEN_HERE}`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
)
|
||||
})
|
||||
```
|
||||
|
||||
**Example GraphQL Mutation**:
|
||||
@@ -291,20 +264,16 @@ mutation {
|
||||
|
||||
```ts
|
||||
const result = await payload.verifyEmail({
|
||||
collection: 'collection-slug',
|
||||
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.
|
||||
To restrict who is allowed to unlock users, you can utilize the [`unlock`](../access-control/overview#unlock) access control function.
|
||||
|
||||
**Example REST API unlock**:
|
||||
|
||||
@@ -329,7 +298,7 @@ mutation {
|
||||
|
||||
```ts
|
||||
const result = await payload.unlock({
|
||||
collection: 'collection-slug',
|
||||
collection: '[collection-slug]',
|
||||
})
|
||||
```
|
||||
|
||||
@@ -339,23 +308,20 @@ Payload comes with built-in forgot password functionality. Submitting an email a
|
||||
|
||||
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).
|
||||
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/overview#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',
|
||||
}),
|
||||
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**:
|
||||
@@ -370,33 +336,22 @@ mutation {
|
||||
|
||||
```ts
|
||||
const token = await payload.forgotPassword({
|
||||
collection: 'collection-slug',
|
||||
collection: '[collection-slug]',
|
||||
data: {
|
||||
email: 'dev@payloadcms.com',
|
||||
},
|
||||
disableEmail: false, // you can disable the auto-generation of email via Local API
|
||||
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.
|
||||
|
||||
<strong>Tip:</strong>
|
||||
<br />
|
||||
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
|
||||
|
||||
@@ -11,7 +11,7 @@ keywords: authentication, config, configuration, overview, documentation, Conten
|
||||
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.
|
||||
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), all 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:
|
||||
|
||||
@@ -22,7 +22,7 @@ Here are some common use cases of Authentication in your own applications:
|
||||
|
||||
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):
|
||||
To enable Authentication on a Collection, use the `auth` property in the [Collection Config](../configuration/collection#auth):
|
||||
|
||||
```ts
|
||||
import type { CollectionConfig } from 'payload'
|
||||
@@ -41,9 +41,8 @@ _Admin Panel screenshot depicting an Admins Collection with Auth enabled_
|
||||
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).
|
||||
<strong>Note:</strong>
|
||||
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):
|
||||
@@ -66,32 +65,31 @@ export const Admins: CollectionConfig = {
|
||||
```
|
||||
|
||||
<Banner type="info">
|
||||
**Tip:** For default auth behavior, set `auth: true`. This is a good starting
|
||||
point for most applications.
|
||||
<strong>Tip:</strong>
|
||||
For default auth behavior, set `auth: true`. This is a good starting point for most applications.
|
||||
</Banner>
|
||||
|
||||
<Banner type="warning">
|
||||
**Note:** Auth-enabled Collections with be automatically injected with the
|
||||
`hash`, `salt`, and `email` fields. [More
|
||||
details](../fields/overview#field-names).
|
||||
<strong>Note:</strong>
|
||||
Auth-enabled Collections with 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). |
|
||||
| **`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). |
|
||||
| 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). |
|
||||
| **`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
|
||||
|
||||
@@ -134,7 +132,7 @@ If set to `true`, an email address is required when creating a new user. If set
|
||||
|
||||
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):
|
||||
To enable auto-login, set the `autoLogin` property in the [Admin Config](../configuration/admin):
|
||||
|
||||
```ts
|
||||
import { buildConfig } from 'payload'
|
||||
@@ -155,15 +153,14 @@ export default buildConfig({
|
||||
```
|
||||
|
||||
<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.
|
||||
<strong>Warning:</strong>
|
||||
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 |
|
||||
@@ -185,7 +182,7 @@ Each of these strategies can work together or independently. You can also create
|
||||
|
||||
### 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).
|
||||
[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 <strong>cannot be read by JavaScript in the browser</strong>, unlike JWT's. [More details](./cookies).
|
||||
|
||||
### JSON Web Tokens
|
||||
|
||||
|
||||
@@ -24,7 +24,10 @@ export const Users: CollectionConfig = {
|
||||
saveToJWT: true,
|
||||
type: 'select',
|
||||
name: 'role',
|
||||
options: ['super-admin', 'user'],
|
||||
options: [
|
||||
'super-admin',
|
||||
'user',
|
||||
]
|
||||
},
|
||||
{
|
||||
// the entire object will be stored in the JWT
|
||||
@@ -43,7 +46,7 @@ export const Users: CollectionConfig = {
|
||||
type: 'text',
|
||||
name: 'omitField',
|
||||
},
|
||||
],
|
||||
]
|
||||
},
|
||||
{
|
||||
type: 'group',
|
||||
@@ -60,19 +63,19 @@ export const Users: CollectionConfig = {
|
||||
type: 'text',
|
||||
name: 'omitField',
|
||||
},
|
||||
],
|
||||
]
|
||||
},
|
||||
],
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
<Banner type="success">
|
||||
**Tip:**
|
||||
|
||||
If you wish to use a different key other than the field `name`, you can define `saveToJWT` as a string.
|
||||
|
||||
<strong>Tip:</strong>
|
||||
<br/>
|
||||
If you wish to use a different key other than the field `name`, you can define `saveToJWT` as a string.
|
||||
</Banner>
|
||||
|
||||
|
||||
### Using Token Data
|
||||
|
||||
This is especially helpful when writing [Hooks](../hooks/overview) and [Access Control](../access-control/overview) that depend on user defined fields.
|
||||
|
||||
@@ -11,10 +11,10 @@ keywords: configuration, config, settings, project, cloud, payload cloud, deploy
|
||||
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.
|
||||
We’ll remind you 7 days before your trial ends and you can cancel anytime.
|
||||
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. We’ll remind you 7 days before your trial ends and you can cancel
|
||||
anytime.
|
||||
</Banner>
|
||||
|
||||
## Project Details
|
||||
@@ -44,8 +44,8 @@ If you are deploying a new project from a template, the following settings will
|
||||
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
|
||||
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>
|
||||
|
||||
@@ -54,9 +54,8 @@ Any of the features in Payload Cloud that require environment variables will aut
|
||||
Payment methods can be set per project and can be updated any time. You can use team’s 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. We’ll remind you 7 days before your trial ends and you can cancel
|
||||
anytime.
|
||||
<strong>Note:</strong> 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. We’ll remind you 7 days before your trial ends and
|
||||
you can cancel anytime.
|
||||
</Banner>
|
||||
|
||||
@@ -13,9 +13,8 @@ Payload Cloud offers various plans tailored to meet your specific needs, includi
|
||||
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.
|
||||
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
|
||||
@@ -32,7 +31,7 @@ Next, select your `GitHub Scope`. If you belong to multiple organizations, they
|
||||
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
|
||||
<strong>Note:</strong> Public repositories can be accessed by anyone online, while private
|
||||
repositories grant access only to you and anyone you explicitly authorize.
|
||||
</Banner>
|
||||
|
||||
@@ -46,8 +45,7 @@ Payload Cloud works for any Node.js + MongoDB app. From the New Project page, se
|
||||
_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.
|
||||
<strong>Note:</strong> 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/plugin-cloud) to your
|
||||
Payload app.
|
||||
</Banner>
|
||||
|
||||
@@ -9,11 +9,10 @@ keywords: cloud, payload cloud, projects, project, overview, database, file stor
|
||||
## 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.
|
||||
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>
|
||||
|
||||

|
||||
@@ -29,7 +28,7 @@ Your Payload Cloud project comes with a MongoDB serverless Atlas DB instance or
|
||||
|
||||
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.
|
||||
AWS Cognito is used for authentication to your S3 bucket. The [Payload Cloud Plugin](https://github.com/payloadcms/plugin-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
|
||||
|
||||
@@ -60,9 +59,7 @@ You can update settings from your Project’s Settings tab. Changes to your buil
|
||||
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).
|
||||
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
|
||||
@@ -70,9 +67,8 @@ From the Environment Variables page of the Settings tab, you can add, update and
|
||||
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
|
||||
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 provider’s 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.
|
||||
@@ -102,7 +98,7 @@ From there, you are ready to make updates to your project. When you are ready to
|
||||
|
||||
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`
|
||||
`yarn add @payloadcms/payload-cloud`
|
||||
|
||||
```js
|
||||
import { payloadCloudPlugin } from '@payloadcms/payload-cloud'
|
||||
@@ -115,14 +111,13 @@ export default buildConfig({
|
||||
```
|
||||
|
||||
<Banner type="warning">
|
||||
**Note:** If your Payload Config already has an email with transport, this
|
||||
will take precedence over Payload Cloud's email service.
|
||||
**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.
|
||||
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**
|
||||
|
||||
@@ -7,8 +7,8 @@ keywords: team, teams, billing, subscription, payment, plan, plans, cloud, paylo
|
||||
---
|
||||
|
||||
<Banner>
|
||||
Within Payload Cloud, the team management feature offers you the ability to
|
||||
manage your organization, team members, billing, and subscription settings.
|
||||
Within Payload Cloud, the team management feature offers you the ability to manage your
|
||||
organization, team members, billing, and subscription settings.
|
||||
</Banner>
|
||||
|
||||

|
||||
|
||||
@@ -19,21 +19,20 @@ import { buildConfig } from 'payload'
|
||||
|
||||
export default buildConfig({
|
||||
// ...
|
||||
collections: [
|
||||
// highlight-line
|
||||
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.
|
||||
<strong>Tip:</strong>
|
||||
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).
|
||||
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:
|
||||
|
||||
@@ -46,45 +45,39 @@ export const Posts: CollectionConfig = {
|
||||
{
|
||||
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.
|
||||
<strong>Reminder:</strong>
|
||||
For a more complex example, see the [Public Demo](https://github.com/payloadcms/public-demo) source code on GitHub, or 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. |
|
||||
| `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 |
|
||||
| Option | Description |
|
||||
| ---------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **`admin`** | The configuration options for the Admin Panel. [More details](../admin/collections). |
|
||||
| **`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`** | An object with `singularName` and `pluralName` strings used in schema generation. Auto-generated from slug if not defined. Set to `false` to disable GraphQL. |
|
||||
| **`hooks`** | Entry point for Hooks. [More details](../hooks/overview#collection-hooks). |
|
||||
| **`labels`** | Singular and plural labels for use in identifying this Collection throughout Payload. 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 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). |
|
||||
|
||||
_\* An asterisk denotes that a property is required._
|
||||
|
||||
@@ -100,178 +93,9 @@ Fields define the schema of the Documents within a Collection. To learn more, go
|
||||
|
||||
[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
|
||||
### 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. A field with `virtual: true` cannot be used as the title, unless it's linked to a relationship'. |
|
||||
| `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. |
|
||||
|
||||
### 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 |
|
||||
| ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `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`. |
|
||||
You can customize the way that the [Admin Panel](../admin/overview) behaves on a Collection-by-Collection basis. To learn more, go to the [Collection Admin Options](../admin/collections) documentation.
|
||||
|
||||
## TypeScript
|
||||
|
||||
|
||||
@@ -42,13 +42,11 @@ export default buildConfig({
|
||||
|
||||
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_`.
|
||||
If you are building a [Custom Component](../admin/components) 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.
|
||||
<strong>Important:</strong>
|
||||
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:
|
||||
@@ -68,7 +66,11 @@ const stripeKey = process.env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY // highlight-li
|
||||
const MyClientComponent = () => {
|
||||
// do something with the key
|
||||
|
||||
return <div>My Client Component</div>
|
||||
return (
|
||||
<div>
|
||||
My Client Component
|
||||
</div>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
@@ -93,7 +95,7 @@ export default buildConfig({
|
||||
```
|
||||
|
||||
<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.
|
||||
<strong>Tip:</strong>
|
||||
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>
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ desc: Set up your Global config for your needs by defining fields, adding slugs
|
||||
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 in many ways similar to [Collections](../configuration/collections), except 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.
|
||||
|
||||
@@ -17,16 +17,15 @@ import { buildConfig } from 'payload'
|
||||
|
||||
export default buildConfig({
|
||||
// ...
|
||||
globals: [
|
||||
// highlight-line
|
||||
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.
|
||||
<strong>Tip:</strong>
|
||||
If you have more than one Global that share the same structure, consider using a [Collection](../configuration/collections) instead.
|
||||
</Banner>
|
||||
|
||||
## Config Options
|
||||
@@ -60,31 +59,28 @@ export const Nav: GlobalConfig = {
|
||||
```
|
||||
|
||||
<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.
|
||||
<strong>Reminder:</strong>
|
||||
For a more complex example, see the [Public Demo](https://github.com/payloadcms/public-demo) source code on GitHub, or 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 |
|
||||
| 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/globals). |
|
||||
| **`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.name`** | Text used in schema generation. Auto-generated from slug if not defined. |
|
||||
| **`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#globals-config). |
|
||||
|
||||
_\* An asterisk denotes that a property is required._
|
||||
|
||||
@@ -100,108 +96,9 @@ Fields define the schema of the Global. To learn more, go to the [Fields](../fie
|
||||
|
||||
[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
|
||||
### 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`. |
|
||||
You can customize the way that the [Admin Panel](../admin/overview) behaves on a Global-by-Global basis. To learn more, go to the [Global Admin Options](../admin/globals) documentation.
|
||||
|
||||
## TypeScript
|
||||
|
||||
|
||||
@@ -6,34 +6,26 @@ desc: Manage and customize internationalization support in your CMS editor exper
|
||||
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.
|
||||
The [Admin Panel](../admin/overview) is translated in over [30 languages and counting](https://github.com/payloadcms/payload/tree/beta/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.
|
||||
By default, Payload comes with 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 was 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):
|
||||
To configure I18n, use the `i18n` key in your [Payload Config](./overview):
|
||||
|
||||
```ts
|
||||
import { buildConfig } from 'payload'
|
||||
|
||||
export default buildConfig({
|
||||
// ...
|
||||
i18n: {
|
||||
// highlight-line
|
||||
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).
|
||||
<strong>Note:</strong>
|
||||
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
|
||||
@@ -48,18 +40,18 @@ 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. |
|
||||
| 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
|
||||
|
||||
@@ -83,8 +75,8 @@ export default buildConfig({
|
||||
```
|
||||
|
||||
<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.
|
||||
<strong>Tip:</strong>
|
||||
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
|
||||
@@ -165,7 +157,7 @@ export const Articles: CollectionConfig = {
|
||||
placeholder: {
|
||||
// highlight-start
|
||||
en: 'Enter title',
|
||||
es: 'Introduce el título',
|
||||
es: 'Introduce el título'
|
||||
// highlight-end
|
||||
},
|
||||
},
|
||||
@@ -174,11 +166,7 @@ export const Articles: CollectionConfig = {
|
||||
}
|
||||
```
|
||||
|
||||
## Changing Languages
|
||||
|
||||
Users can change their preferred language in their account settings or by otherwise manipulating their [User Preferences](../admin/preferences).
|
||||
|
||||
## Node.js#node
|
||||
## 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.
|
||||
|
||||
@@ -186,33 +174,29 @@ Anywhere in your Payload app that you have access to the `req` object, you can a
|
||||
|
||||
## TypeScript
|
||||
|
||||
In order to use [Custom Translations](#custom-translations) in your project, you need to provide the types for the translations.
|
||||
In order to use 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 { Config } from 'payload'
|
||||
import type { NestedKeysStripped } from '@payloadcms/translations'
|
||||
|
||||
export const customTranslations = {
|
||||
export const customTranslations: Config['i18n']['translations'] = {
|
||||
en: {
|
||||
general: {
|
||||
myCustomKey: 'My custom english translation',
|
||||
},
|
||||
fields: {
|
||||
addLabel: 'Add!',
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
export type CustomTranslationsObject = typeof customTranslations.en &
|
||||
typeof enTranslations
|
||||
export type CustomTranslationsKeys =
|
||||
NestedKeysStripped<CustomTranslationsObject>
|
||||
export type CustomTranslationsObject = typeof customTranslations.en
|
||||
export type CustomTranslationsKeys = NestedKeysStripped<CustomTranslationsObject>
|
||||
```
|
||||
|
||||
Import the shared translations object into our Payload config so they are available for use:
|
||||
@@ -233,7 +217,7 @@ export default buildConfig({
|
||||
})
|
||||
```
|
||||
|
||||
Import the shared translation types to use in your [Custom Component](../custom-components/overview):
|
||||
Import the shared translation types to use in your [Custom Component](../admin/components):
|
||||
|
||||
```ts
|
||||
// <rootDir>/components/MyComponent.tsx
|
||||
@@ -242,16 +226,10 @@ Import the shared translation types to use in your [Custom Component](../custom-
|
||||
import type React from 'react'
|
||||
import { useTranslation } from '@payloadcms/ui'
|
||||
|
||||
import type {
|
||||
CustomTranslationsObject,
|
||||
CustomTranslationsKeys,
|
||||
} from '../custom-translations'
|
||||
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
|
||||
const { i18n, t } = useTranslation<CustomTranslationsObject, CustomTranslationsKeys>() // These generics merge your custom translations with the default client translations
|
||||
|
||||
return t('general:myCustomKey')
|
||||
}
|
||||
@@ -262,10 +240,7 @@ Additionally, Payload exposes the `t` function in various places, for example in
|
||||
```ts
|
||||
// <rootDir>/fields/myField.ts
|
||||
|
||||
import type {
|
||||
DefaultTranslationKeys,
|
||||
TFunction,
|
||||
} from '@payloadcms/translations'
|
||||
import type { DefaultTranslationKeys, TFunction } from '@payloadcms/translations'
|
||||
import type { Field } from 'payload'
|
||||
|
||||
import { CustomTranslationsKeys } from '../custom-translations'
|
||||
@@ -273,9 +248,9 @@ 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')
|
||||
},
|
||||
label: (
|
||||
{ t }: { t: TFunction<CustomTranslationsKeys | DefaultTranslationKeys> }, // The generic passed to TFunction does not automatically merge the custom translations with the default translations. We need to merge them ourselves here
|
||||
) => t('fields:addLabel'),
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -17,8 +17,7 @@ import { buildConfig } from 'payload'
|
||||
|
||||
export default buildConfig({
|
||||
// ...
|
||||
localization: {
|
||||
// highlight-line
|
||||
localization: { // highlight-line
|
||||
// ...
|
||||
},
|
||||
})
|
||||
@@ -36,7 +35,7 @@ import { buildConfig } from 'payload'
|
||||
export default buildConfig({
|
||||
// ...
|
||||
localization: {
|
||||
locales: ['en', 'es', 'de'], // required
|
||||
locales: ['en', 'es', 'de'] // required
|
||||
defaultLocale: 'en', // required
|
||||
},
|
||||
})
|
||||
@@ -72,18 +71,17 @@ export default buildConfig({
|
||||
```
|
||||
|
||||
<Banner type="success">
|
||||
**Tip:** Localization works very well alongside
|
||||
[I18n](/docs/configuration/i18n).
|
||||
<strong>Tip:</strong>
|
||||
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). |
|
||||
| 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. |
|
||||
|
||||
### Locales
|
||||
|
||||
@@ -102,35 +100,6 @@ The locale codes do not need to be in any specific format. It's up to you to def
|
||||
|
||||
_\* 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.
|
||||
@@ -152,18 +121,18 @@ With the above configuration, the `title` field will now be saved in the databas
|
||||
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.
|
||||
<strong>Note:</strong>
|
||||
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
|
||||
<strong>Important:</strong>
|
||||
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>
|
||||
|
||||
@@ -214,10 +183,9 @@ query {
|
||||
```
|
||||
|
||||
<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.
|
||||
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
|
||||
@@ -237,8 +205,8 @@ const posts = await payload.find({
|
||||
```
|
||||
|
||||
<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.
|
||||
<strong>Tip:</strong>
|
||||
The REST and Local APIs can return all Localization data in one request by passing 'all' or '*' as
|
||||
the <strong>locale</strong> 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>
|
||||
|
||||
@@ -23,8 +23,8 @@ export default buildConfig({
|
||||
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).
|
||||
<strong>Tip:</strong>
|
||||
The location of your Payload Config can be customized. [More details](#customizing--automating-config-location-detection).
|
||||
</Banner>
|
||||
|
||||
## Config Options
|
||||
@@ -48,27 +48,25 @@ export default buildConfig({
|
||||
fields: [
|
||||
{
|
||||
name: 'title',
|
||||
type: 'text',
|
||||
},
|
||||
],
|
||||
},
|
||||
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.
|
||||
<strong>Note:</strong>
|
||||
For a more complex example, see the [Public Demo](https://github.com/payloadcms/public-demo) source code on GitHub, or 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). |
|
||||
| **`bin`** | Register custom bin scripts for Payload to execute. |
|
||||
| **`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. |
|
||||
@@ -81,19 +79,17 @@ The following options are available:
|
||||
| **`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). |
|
||||
| **`csrf`** | A whitelist array of URLs to allow Payload to accept cookies from. [More details](../authentication/overview#csrf-protection). |
|
||||
| **`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). |
|
||||
| **`rateLimit`** | Control IP-based rate limiting for all Payload resources. Used to prevent DDoS attacks, etc. [More details](../production/preventing-abuse#rate-limiting-requests). |
|
||||
| **`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). |
|
||||
@@ -106,13 +102,14 @@ The following options are available:
|
||||
_\* 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).
|
||||
<strong>Note:</strong>
|
||||
Some properties are removed from the client-side bundle. [More details](../admin/components#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.
|
||||
Payload exposes a variety of TypeScript settings that you can leverage. These settings are used to auto-generate TypeScript interfaces for your [Collections](../configuration/collections) and [Globals](../configuration/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:
|
||||
|
||||
@@ -121,36 +118,33 @@ import { buildConfig } from 'payload'
|
||||
|
||||
export default buildConfig({
|
||||
// ...
|
||||
typescript: {
|
||||
// highlight-line
|
||||
typescript: { // highlight-line
|
||||
// ...
|
||||
},
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
The following options are available:
|
||||
|
||||
| Option | Description |
|
||||
| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| 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. |
|
||||
| **`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\*
|
||||
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.
|
||||
<strong>Important:</strong>
|
||||
Ensure your `tsconfig.json` is properly configured for Payload to auto-detect your config location. If if does not exist, or does not specify the proper `compilerOptions`, Payload will default to the current working directory.
|
||||
</Banner>
|
||||
|
||||
**Development Mode**
|
||||
@@ -188,7 +182,7 @@ 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.
|
||||
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](..environment-variables) to bypass all automatic config detection.
|
||||
|
||||
To use a custom config location, set the `PAYLOAD_CONFIG_PATH` environment variable:
|
||||
|
||||
@@ -201,8 +195,8 @@ To use a custom config location, set the `PAYLOAD_CONFIG_PATH` environment varia
|
||||
```
|
||||
|
||||
<Banner type="info">
|
||||
**Tip:** `PAYLOAD_CONFIG_PATH` can be either an absolute path, or path
|
||||
relative to your current working directory.
|
||||
<strong>Tip:</strong>
|
||||
`PAYLOAD_CONFIG_PATH` can be either an absolute path, or path relative to your current working directory.
|
||||
</Banner>
|
||||
|
||||
## Telemetry
|
||||
@@ -211,14 +205,14 @@ Payload collects **completely anonymous** telemetry data about general usage. Th
|
||||
|
||||
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)
|
||||
|
||||
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 a object with the following properties:
|
||||
|
||||
| Option | Description |
|
||||
| ------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| 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`. |
|
||||
| **`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:
|
||||
|
||||
@@ -227,7 +221,7 @@ import { buildConfig } from 'payload'
|
||||
|
||||
export default buildConfig({
|
||||
// ...
|
||||
cors: '*', // highlight-line
|
||||
cors: '*' // highlight-line
|
||||
})
|
||||
```
|
||||
|
||||
@@ -240,9 +234,9 @@ export default buildConfig({
|
||||
// ...
|
||||
// highlight-start
|
||||
cors: {
|
||||
origins: ['http://localhost:3000'],
|
||||
headers: ['x-custom-header'],
|
||||
},
|
||||
origins: ['http://localhost:3000']
|
||||
headers: ['x-custom-header']
|
||||
}
|
||||
// highlight-end
|
||||
})
|
||||
```
|
||||
@@ -272,46 +266,3 @@ The Payload Config can accept compatibility flags for running the newest version
|
||||
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('Succesffully 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
|
||||
```
|
||||
|
||||
@@ -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>
|
||||
@@ -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. |
|
||||
| `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)._
|
||||
@@ -1,181 +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
|
||||
// - livePreview
|
||||
// - [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](#ocument-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: {
|
||||
myCustomTab: {
|
||||
Component: '/path/to/MyCustomTab',
|
||||
path: '/my-custom-tab',
|
||||
// highlight-start
|
||||
tab: {
|
||||
Component: '/path/to/MyCustomTabComponent',
|
||||
},
|
||||
// highlight-end
|
||||
},
|
||||
anotherCustomTab: {
|
||||
Component: '/path/to/AnotherCustomView',
|
||||
path: '/another-custom-view',
|
||||
// highlight-start
|
||||
tab: {
|
||||
label: 'Another Custom View',
|
||||
href: '/another-custom-view',
|
||||
},
|
||||
// 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`. |
|
||||
| `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>
|
||||
)
|
||||
}
|
||||
```
|
||||
@@ -1,499 +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). |
|
||||
| `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). |
|
||||
| `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>
|
||||
}
|
||||
```
|
||||
|
||||
### 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" />
|
||||
}
|
||||
```
|
||||
@@ -1,332 +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). |
|
||||
| `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>
|
||||
}
|
||||
```
|
||||
@@ -1,507 +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>
|
||||
@@ -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 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.graphic.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>
|
||||
}
|
||||
```
|
||||
@@ -20,9 +20,8 @@ Ensure you have an npm script called "payload" in your `package.json` file.
|
||||
```
|
||||
|
||||
<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.
|
||||
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
|
||||
@@ -52,54 +51,12 @@ export async function down({ payload, req }: MigrateDownArgs): Promise<void> {
|
||||
|
||||
## 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
|
||||
When migrations are run, each migration is performed in a new [transactions](/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
|
||||
@@ -129,11 +86,6 @@ default, migrations will be named using a timestamp.
|
||||
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,
|
||||
@@ -189,8 +141,6 @@ In MongoDB, you'll only ever really need to run migrations for times where you c
|
||||
|
||||
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.
|
||||
|
||||
You can find [here](/database/mongodb#common-migration-scripts) examples of common MongoDB migrations.
|
||||
|
||||
#### Postgres
|
||||
|
||||
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).
|
||||
@@ -207,17 +157,16 @@ You can disable this setting and solely use migrations to manage your local deve
|
||||
|
||||
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).
|
||||
For more information about push mode and prototyping in development, [click here](./postgres#prototyping-in-dev-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.
|
||||
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**
|
||||
@@ -232,10 +181,7 @@ But once you're ready, you can run `pnpm payload migrate:create`, which will per
|
||||
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.
|
||||
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**
|
||||
@@ -288,27 +234,13 @@ export default buildConfig({
|
||||
// your config here
|
||||
db: postgresAdapter({
|
||||
// your adapter config here
|
||||
prodMigrations: migrations,
|
||||
}),
|
||||
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.
|
||||
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.
|
||||
|
||||
@@ -30,17 +30,14 @@ export default buildConfig({
|
||||
|
||||
## 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. |
|
||||
| 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. |
|
||||
| `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/). |
|
||||
|
||||
## Access to Mongoose models
|
||||
|
||||
@@ -52,127 +49,3 @@ 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.
|
||||
|
||||
## Common migration scripts
|
||||
|
||||
### Delete field from the database
|
||||
|
||||
With the MongoDB adapter, even if you delete a field from your Payload config, the existing field data will still be remained in the database.
|
||||
If you want to ensure that the field is fully erased from the database, you can use the following script:
|
||||
|
||||
```ts
|
||||
// pnpx payload migrate:create --name delete-field
|
||||
export async function up({ payload, session }: MigrateUpArgs): Promise<void> {
|
||||
await payload.db.collections.posts.collection.updateMany(
|
||||
{},
|
||||
{
|
||||
$unset: {
|
||||
// delete title field
|
||||
title: true,
|
||||
// nested to array
|
||||
'array.title': true,
|
||||
},
|
||||
},
|
||||
{ session },
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
### Synchronize indexes
|
||||
|
||||
Payload won't automatically replace existing indexes in MongoDB when you change your Payload config.
|
||||
For example, changing `index: true` to `unique: true` won't automatically update the index in MongoDB.
|
||||
You can use the following script to synchronize indexes:
|
||||
|
||||
```ts
|
||||
// pnpx payload migrate:create --name sync-posts-indexes
|
||||
export async function up({ payload, session }: MigrateUpArgs): Promise<void> {
|
||||
await payload.db.collections.posts.syncIndexes()
|
||||
}
|
||||
```
|
||||
|
||||
<Banner type="warning">
|
||||
Note that this will also drop all indexes that aren't in the payload config.
|
||||
If you have custom indexes that you want to keep, they must be added to the collection schema
|
||||
or you can insert them manually after `syncIndexes` with:
|
||||
`payload.db.collections.posts.collection.createIndex({ title: 1 })`
|
||||
</Banner>
|
||||
|
||||
### Making field localized and vice versa
|
||||
|
||||
When you change a field to be localized or vice versa, you can use the following script to update the field in the database:
|
||||
|
||||
```ts
|
||||
// pnpx payload migrate:create --name make-title-localized
|
||||
export async function up({ payload, session }: MigrateUpArgs): Promise<void> {
|
||||
const posts = await payload.db.collections.posts.collection
|
||||
.find({}, { session })
|
||||
.toArray()
|
||||
|
||||
// Make "title" localized
|
||||
await payload.db.collections.posts.collection.bulkWrite(
|
||||
posts.map((post) => ({
|
||||
updateOne: {
|
||||
filter: { _id: post._id },
|
||||
update: { $set: { title: { en: post.title } } },
|
||||
},
|
||||
})),
|
||||
)
|
||||
|
||||
// Make "title" non-localized
|
||||
await payload.db.collections.posts.collection.bulkWrite(
|
||||
posts.map((post) => ({
|
||||
updateOne: {
|
||||
filter: { _id: post._id },
|
||||
update: { $set: { title: post.title.en } },
|
||||
},
|
||||
})),
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
### Example renaming a collection
|
||||
|
||||
The following example renames a collection with slug "pages" to "articles" and it includes migrating the \_versions collection also.
|
||||
|
||||
```ts
|
||||
import { MigrateDownArgs, MigrateUpArgs } from '@payloadcms/db-mongodb'
|
||||
// import type { Db } from 'mongodb' // you will need to add this package as a devDependency in the package.json if you want db to be typed as Db
|
||||
|
||||
export async function up({
|
||||
payload,
|
||||
req,
|
||||
session,
|
||||
}: MigrateUpArgs): Promise<void> {
|
||||
const db = payload.db.connection.db as any
|
||||
|
||||
await db.renameCollection('pages', 'articles', { session, dropTarget: true })
|
||||
await db.renameCollection('_pages_versions', '_articles_versions', {
|
||||
session,
|
||||
dropTarget: true,
|
||||
}) // remove this line if you do not have versions enabled
|
||||
}
|
||||
|
||||
export async function down({
|
||||
payload,
|
||||
req,
|
||||
session,
|
||||
}: MigrateDownArgs): Promise<void> {
|
||||
const db = payload.db.connection.db as any
|
||||
|
||||
await db.renameCollection('articles', 'pages', { session, dropTarget: true })
|
||||
await db.renameCollection('_articles_versions', '_pages_versions', {
|
||||
session,
|
||||
dropTarget: true,
|
||||
}) // remove this line if you do not have versions enabled
|
||||
}
|
||||
```
|
||||
|
||||
@@ -31,10 +31,8 @@ export default buildConfig({
|
||||
```
|
||||
|
||||
<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.
|
||||
<strong>Reminder:</strong>
|
||||
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
|
||||
|
||||
@@ -44,24 +44,12 @@ export default buildConfig({
|
||||
// Optionally, can accept the same options as the @vercel/postgres package.
|
||||
db: vercelPostgresAdapter({
|
||||
pool: {
|
||||
connectionString: process.env.DATABASE_URL,
|
||||
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 |
|
||||
@@ -72,41 +60,21 @@ export default buildConfig({
|
||||
| `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'. |
|
||||
| `disableCreateDatabase` | Pass `true` to disale 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. |
|
||||
|
||||
## 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:
|
||||
You can access Drizzle as follows:
|
||||
|
||||
```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'`),
|
||||
)
|
||||
```text
|
||||
payload.db.drizzle
|
||||
```
|
||||
|
||||
## Tables, relations, and enums
|
||||
@@ -141,11 +109,7 @@ Runs before the schema is built. You can use this hook to extend your database s
|
||||
|
||||
```ts
|
||||
import { postgresAdapter } from '@payloadcms/db-postgres'
|
||||
import {
|
||||
integer,
|
||||
pgTable,
|
||||
serial,
|
||||
} from '@payloadcms/db-postgres/drizzle/pg-core'
|
||||
import { integer, pgTable, serial } from 'drizzle-orm/pg-core'
|
||||
|
||||
postgresAdapter({
|
||||
beforeSchemaInit: [
|
||||
@@ -169,13 +133,7 @@ To quickly generate the Drizzle schema from your database you can use [Drizzle I
|
||||
You should get the `schema.ts` file which may look like this:
|
||||
|
||||
```ts
|
||||
import {
|
||||
pgTable,
|
||||
uniqueIndex,
|
||||
serial,
|
||||
varchar,
|
||||
text,
|
||||
} from 'drizzle-orm/pg-core'
|
||||
import { pgTable, uniqueIndex, serial, varchar, text } from 'drizzle-orm/pg-core'
|
||||
|
||||
export const users = pgTable('users', {
|
||||
id: serial('id').primaryKey(),
|
||||
@@ -195,6 +153,7 @@ export const countries = pgTable(
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
```
|
||||
|
||||
You can import them into your config and append to the schema with the `beforeSchemaInit` hook like this:
|
||||
@@ -211,7 +170,7 @@ postgresAdapter({
|
||||
tables: {
|
||||
...schema.tables,
|
||||
users,
|
||||
countries,
|
||||
countries
|
||||
},
|
||||
}
|
||||
},
|
||||
@@ -219,7 +178,8 @@ postgresAdapter({
|
||||
})
|
||||
```
|
||||
|
||||
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.
|
||||
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
|
||||
|
||||
@@ -229,7 +189,7 @@ The following example adds the `extra_integer_column` column and a composite ind
|
||||
|
||||
```ts
|
||||
import { postgresAdapter } from '@payloadcms/db-postgres'
|
||||
import { index, integer } from '@payloadcms/db-postgres/drizzle/pg-core'
|
||||
import { index, integer } from 'drizzle-orm/pg-core'
|
||||
import { buildConfig } from 'payload'
|
||||
|
||||
export default buildConfig({
|
||||
@@ -257,9 +217,10 @@ export default buildConfig({
|
||||
extraIntegerColumn: integer('extra_integer_column'),
|
||||
},
|
||||
extraConfig: (table) => ({
|
||||
country_city_composite_index: index(
|
||||
'country_city_composite_index',
|
||||
).on(table.country, table.city),
|
||||
country_city_composite_index: index('country_city_composite_index').on(
|
||||
table.country,
|
||||
table.city,
|
||||
),
|
||||
}),
|
||||
})
|
||||
|
||||
@@ -268,47 +229,5 @@ export default buildConfig({
|
||||
],
|
||||
}),
|
||||
})
|
||||
```
|
||||
|
||||
### 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
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
```
|
||||
|
||||
@@ -27,57 +27,34 @@ export default buildConfig({
|
||||
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. |
|
||||
| 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. |
|
||||
| `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) |
|
||||
|
||||
## 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:
|
||||
You can access Drizzle as follows:
|
||||
|
||||
```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'`),
|
||||
)
|
||||
```text
|
||||
payload.db.drizzle
|
||||
```
|
||||
|
||||
## Tables and relations
|
||||
@@ -111,7 +88,7 @@ Runs before the schema is built. You can use this hook to extend your database s
|
||||
|
||||
```ts
|
||||
import { sqliteAdapter } from '@payloadcms/db-sqlite'
|
||||
import { integer, sqliteTable } from '@payloadcms/db-sqlite/drizzle/sqlite-core'
|
||||
import { integer, sqliteTable } from 'drizzle-orm/sqlite-core'
|
||||
|
||||
sqliteAdapter({
|
||||
beforeSchemaInit: [
|
||||
@@ -135,17 +112,12 @@ To quickly generate the Drizzle schema from your database you can use [Drizzle I
|
||||
You should get the `schema.ts` file which may look like this:
|
||||
|
||||
```ts
|
||||
import {
|
||||
sqliteTable,
|
||||
text,
|
||||
uniqueIndex,
|
||||
integer,
|
||||
} from 'drizzle-orm/sqlite-core'
|
||||
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 }),
|
||||
phone: text('phone', {length: 256}),
|
||||
})
|
||||
|
||||
export const countries = sqliteTable(
|
||||
@@ -160,6 +132,7 @@ export const countries = sqliteTable(
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
```
|
||||
|
||||
You can import them into your config and append to the schema with the `beforeSchemaInit` hook like this:
|
||||
@@ -176,7 +149,7 @@ sqliteAdapter({
|
||||
tables: {
|
||||
...schema.tables,
|
||||
users,
|
||||
countries,
|
||||
countries
|
||||
},
|
||||
}
|
||||
},
|
||||
@@ -184,7 +157,8 @@ sqliteAdapter({
|
||||
})
|
||||
```
|
||||
|
||||
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.
|
||||
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
|
||||
|
||||
@@ -194,7 +168,7 @@ The following example adds the `extra_integer_column` column and a composite ind
|
||||
|
||||
```ts
|
||||
import { sqliteAdapter } from '@payloadcms/db-sqlite'
|
||||
import { index, integer } from '@payloadcms/db-sqlite/drizzle/sqlite-core'
|
||||
import { index, integer } from 'drizzle-orm/sqlite-core'
|
||||
import { buildConfig } from 'payload'
|
||||
|
||||
export default buildConfig({
|
||||
@@ -222,9 +196,10 @@ export default buildConfig({
|
||||
extraIntegerColumn: integer('extra_integer_column'),
|
||||
},
|
||||
extraConfig: (table) => ({
|
||||
country_city_composite_index: index(
|
||||
'country_city_composite_index',
|
||||
).on(table.country, table.city),
|
||||
country_city_composite_index: index('country_city_composite_index').on(
|
||||
table.country,
|
||||
table.city,
|
||||
),
|
||||
}),
|
||||
})
|
||||
|
||||
@@ -233,47 +208,5 @@ export default buildConfig({
|
||||
],
|
||||
}),
|
||||
})
|
||||
```
|
||||
|
||||
### 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
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
```
|
||||
|
||||
@@ -11,17 +11,9 @@ Database transactions allow your application to make a series of database change
|
||||
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.
|
||||
|
||||
<strong>Note:</strong>
|
||||
<br />
|
||||
MongoDB requires a connection to a replicaset in order to make use of transactions.
|
||||
</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:
|
||||
@@ -69,7 +61,7 @@ const afterChange: CollectionAfterChangeHook = async ({ req }) => {
|
||||
|
||||
## 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.
|
||||
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:
|
||||
|
||||
@@ -77,7 +69,7 @@ The following functions can be used for managing transactions:
|
||||
- `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.
|
||||
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
|
||||
@@ -91,14 +83,14 @@ const standalonePayloadScript = async () => {
|
||||
const transactionID = await payload.db.beginTransaction()
|
||||
|
||||
try {
|
||||
// Make an update using the Local API
|
||||
// Make an update using the local API
|
||||
await payload.update({
|
||||
collection: 'posts',
|
||||
data: {
|
||||
some: 'data',
|
||||
},
|
||||
where: {
|
||||
slug: { equals: 'my-slug' },
|
||||
slug: { equals: 'my-slug' }
|
||||
},
|
||||
req: { transactionID },
|
||||
})
|
||||
@@ -123,7 +115,7 @@ standalonePayloadScript()
|
||||
|
||||
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:
|
||||
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({
|
||||
@@ -132,7 +124,7 @@ await payload.update({
|
||||
some: 'data',
|
||||
},
|
||||
where: {
|
||||
slug: { equals: 'my-slug' },
|
||||
slug: { equals: 'my-slug' }
|
||||
},
|
||||
disableTransaction: true,
|
||||
})
|
||||
|
||||
@@ -16,7 +16,7 @@ The email adapter should be passed into the `email` property of the Payload Conf
|
||||
|
||||
### 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.
|
||||
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
|
||||
|
||||
@@ -27,6 +27,7 @@ An email adapter will require at least the following fields:
|
||||
| **`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 |
|
||||
@@ -96,11 +97,13 @@ export default buildConfig({
|
||||
|
||||
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',
|
||||
|
||||
@@ -6,22 +6,36 @@ 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.
|
||||
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 what is going on.
|
||||
|
||||
Examples are changing every day, so be sure to check back often to see what new examples have been added. If you have a specific example you would like to see, 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.
|
||||
|
||||
- [Auth](https://github.com/payloadcms/payload/tree/main/examples/auth)
|
||||
- [Custom Components](https://github.com/payloadcms/payload/tree/main/examples/custom-components)
|
||||
- [Custom Server](https://github.com/payloadcms/payload/tree/main/examples/custom-server)
|
||||
- [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)
|
||||
- [Hierarchy](https://github.com/payloadcms/payload/tree/main/examples/hierarchy)
|
||||
- [Live Preview](https://github.com/payloadcms/payload/tree/main/examples/live-preview)
|
||||
- [Multi-tenant](https://github.com/payloadcms/payload/tree/main/examples/multi-tenant)
|
||||
- [Nested Docs](https://github.com/payloadcms/payload/tree/main/examples/nested-docs)
|
||||
- [Redirects](https://github.com/payloadcms/payload/tree/main/examples/redirects)
|
||||
- [Tailwind / Shadcn-ui](https://github.com/payloadcms/payload/tree/main/examples/tailwind-shadcn-ui)
|
||||
- [Tests](https://github.com/payloadcms/payload/tree/main/examples/testing)
|
||||
- [Virtual Fields](https://github.com/payloadcms/payload/tree/main/examples/virtual-fields)
|
||||
- [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:
|
||||
When necessary, some examples include a front-end. Examples that require a front-end share this folder structure:
|
||||
|
||||
```sh
|
||||
npx create-payload-app --example example_name
|
||||
```plaintext
|
||||
example/
|
||||
├── payload/
|
||||
├── next-app/
|
||||
├── next-pages/
|
||||
├── react-router/
|
||||
├── vue/
|
||||
├── svelte/
|
||||
```
|
||||
|
||||
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.
|
||||
Where `payload` is your Payload project, and the other directories are dedicated to their respective front-end framework. We are adding new examples every day, so if your framework of choice is not yet supported in any particular 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.
|
||||
|
||||
@@ -39,28 +39,28 @@ export const MyArrayField: Field = {
|
||||
|
||||
## Config Options
|
||||
|
||||
| Option | Description |
|
||||
| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **`name`** \* | To be used as the property name when stored and retrieved from the database. [More](/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](/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](/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) |
|
||||
| Option | Description |
|
||||
| ---------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **`name`** \* | To be used as the property name when stored and retrieved from the database. [More](/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](/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](/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. 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._
|
||||
|
||||
@@ -73,20 +73,19 @@ import type { Field } from 'payload'
|
||||
|
||||
export const MyArrayField: Field = {
|
||||
// ...
|
||||
admin: {
|
||||
// highlight-line
|
||||
admin: { // highlight-line
|
||||
// ...
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
The Array Field inherits all of the default options from the base [Field Admin Config](./overview#admin-options), plus the following additional options:
|
||||
The Array Field inherits all of the default options from the base [Field Admin Config](../admin/fields#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` |
|
||||
| Option | Description |
|
||||
| ------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **`initCollapsed`** | Set the initial collapsed state |
|
||||
| **`components.RowLabel`** | React component to be rendered as the label on the array row. [Example](#example-of-a-custom-rowlabel-component) |
|
||||
| **`isSortable`** | Disable order sorting by setting this value to `false` |
|
||||
|
||||
## Example
|
||||
|
||||
@@ -126,99 +125,18 @@ export const ExampleCollection: CollectionConfig = {
|
||||
type: 'text',
|
||||
},
|
||||
],
|
||||
admin: {
|
||||
components: {
|
||||
RowLabel: '/path/to/ArrayRowLabel#ArrayRowLabel',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
```
|
||||
|
||||
## Custom Components
|
||||
### Example of a custom RowLabel component
|
||||
|
||||
### 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'
|
||||
|
||||
@@ -6,7 +6,7 @@ desc: The Blocks Field is a great layout build and can be used to construct any
|
||||
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.
|
||||
The Blocks Field is <strong>incredibly powerful</strong>, storing an array of objects based on the fields that your 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:
|
||||
|
||||
@@ -39,57 +39,54 @@ export const MyBlocksField: Field = {
|
||||
|
||||
## Config Options
|
||||
|
||||
| Option | Description |
|
||||
| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **`name`** \* | To be used as the property name when stored and retrieved from the database. [More](/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](/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](/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) |
|
||||
| Option | Description |
|
||||
| ---------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **`name`** \* | To be used as the property name when stored and retrieved from the database. [More](/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](/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](/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. 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:
|
||||
The 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
|
||||
admin: { // highlight-line
|
||||
// ...
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
The Blocks Field inherits all of the default options from the base [Field Admin Config](./overview#admin-options), plus the following additional options:
|
||||
The Blocks Field inherits all of the default options from the base [Field Admin Config](../admin/fields#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` |
|
||||
| Option | Description |
|
||||
| ------------------- | ---------------------------------- |
|
||||
| **`initCollapsed`** | Set the initial collapsed state |
|
||||
| **`isSortable`** | Disable order sorting by setting this value to `false` |
|
||||
|
||||
#### 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.
|
||||
If you're using this block within the [Lexical editor](/docs/lexical/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
|
||||
@@ -99,29 +96,28 @@ This is super handy if you'd like to present your editors with a very deliberate
|
||||
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.
|
||||
<strong>Tip:</strong><br/>
|
||||
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 {
|
||||
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
|
||||
// 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,
|
||||
@@ -129,6 +125,7 @@ import {
|
||||
// The default "collapsible" UI that is rendered for a regular block
|
||||
// if you want to re-use it
|
||||
BlockCollapsible,
|
||||
|
||||
} from '@payloadcms/richtext-lexical/client'
|
||||
```
|
||||
|
||||
@@ -137,12 +134,13 @@ import {
|
||||
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.
|
||||
<strong>Tip:</strong>
|
||||
<br />
|
||||
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 |
|
||||
@@ -154,7 +152,7 @@ Blocks are defined as separate configs of their own.
|
||||
| **`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. |
|
||||
| **`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
|
||||
@@ -167,7 +165,7 @@ 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`.
|
||||
The Admin Panel provides each block with a `blockName` field which optionally allows editors to label their blocks for better editability and readability.
|
||||
|
||||
## Example
|
||||
|
||||
@@ -212,175 +210,6 @@ export const ExampleCollection: CollectionConfig = {
|
||||
}
|
||||
```
|
||||
|
||||
## 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:
|
||||
|
||||
@@ -28,23 +28,23 @@ export const MyCheckboxField: Field = {
|
||||
|
||||
## Config Options
|
||||
|
||||
| Option | Description |
|
||||
| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **`name`** \* | To be used as the property name when stored and retrieved from the database. [More](/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](/docs/fields/overview#validation) |
|
||||
| **`index`** | Build an [index](/docs/database/overview) 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](/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) |
|
||||
| Option | Description |
|
||||
| ---------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **`name`** \* | To be used as the property name when stored and retrieved from the database. [More](/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](/docs/fields/overview#validation) |
|
||||
| **`index`** | Build an [index](/docs/database/overview) 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](/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/fields#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. 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._
|
||||
|
||||
@@ -67,87 +67,3 @@ export const ExampleCollection: CollectionConfig = {
|
||||
],
|
||||
}
|
||||
```
|
||||
|
||||
## 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}
|
||||
/>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
@@ -29,46 +29,45 @@ export const MyBlocksField: Field = {
|
||||
|
||||
## Config Options
|
||||
|
||||
| Option | Description |
|
||||
| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **`name`** \* | To be used as the property name when stored and retrieved from the database. [More](/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](/docs/database/overview) 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](/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](/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) |
|
||||
| Option | Description |
|
||||
| ---------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **`name`** \* | To be used as the property name when stored and retrieved from the database. [More](/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](/docs/database#overview) 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](/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](/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. 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:
|
||||
The 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
|
||||
admin: { // highlight-line
|
||||
// ...
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
The Code Field inherits all of the default options from the base [Field Admin Config](./overview#admin-options), plus the following additional options:
|
||||
The Code Field inherits all of the default options from the base [Field Admin Config](../admin/fields#admin-options), plus the following additional options:
|
||||
|
||||
| Option | Description |
|
||||
| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
@@ -96,89 +95,3 @@ export const ExampleCollection: CollectionConfig = {
|
||||
],
|
||||
}
|
||||
```
|
||||
|
||||
## 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}
|
||||
/>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
@@ -37,28 +37,27 @@ export const MyCollapsibleField: Field = {
|
||||
| --------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **`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). |
|
||||
| **`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:
|
||||
The 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
|
||||
admin: { // highlight-line
|
||||
// ...
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
The Collapsible Field inherits all of the default options from the base [Field Admin Config](./overview#admin-options), plus the following additional options:
|
||||
The Collapsible Field inherits all of the default options from the base [Field Admin Config](../admin/fields#admin-options), plus the following additional options:
|
||||
|
||||
| Option | Description |
|
||||
| ------------------- | ------------------------------- |
|
||||
|
||||
@@ -28,44 +28,42 @@ export const MyDateField: Field = {
|
||||
|
||||
## Config Options
|
||||
|
||||
| Option | Description |
|
||||
| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **`name`** \* | To be used as the property name when stored and retrieved from the database. [More](/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](/docs/database/overview) 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](/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](/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) |
|
||||
| Option | Description |
|
||||
| ---------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **`name`** \* | To be used as the property name when stored and retrieved from the database. [More](/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](/docs/database/overview) 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](/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](/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. 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:
|
||||
The 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
|
||||
admin: { // highlight-line
|
||||
// ...
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
The Date Field inherits all of the default options from the base [Field Admin Config](./overview#admin-options), plus the following additional options:
|
||||
The Date Field inherits all of the default options from the base [Field Admin Config](../admin/fields#admin-options), plus the following additional options:
|
||||
|
||||
| Property | Description |
|
||||
| ------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
@@ -82,13 +80,13 @@ The Date Field inherits all of the default options from the base [Field Admin Co
|
||||
| **`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)._
|
||||
_\* 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).
|
||||
`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`.
|
||||
|
||||
@@ -137,113 +135,3 @@ export const ExampleCollection: CollectionConfig = {
|
||||
],
|
||||
}
|
||||
```
|
||||
|
||||
## 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>
|
||||
|
||||
@@ -28,49 +28,48 @@ export const MyEmailField: Field = {
|
||||
|
||||
## Config Options
|
||||
|
||||
| Option | Description |
|
||||
| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **`name`** \* | To be used as the property name when stored and retrieved from the database. [More](/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](/docs/database/overview) 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](/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](/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) |
|
||||
| Option | Description |
|
||||
| ---------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **`name`** \* | To be used as the property name when stored and retrieved from the database. [More](/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](/docs/database/overview) 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](/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](/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. 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:
|
||||
The 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
|
||||
admin: { // highlight-line
|
||||
// ...
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
The Email Field inherits all of the default options from the base [Field Admin Config](./overview#admin-options), plus the following additional options:
|
||||
The Email Field inherits all of the default options from the base [Field Admin Config](../admin/fields#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. |
|
||||
| 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
|
||||
|
||||
@@ -91,89 +90,3 @@ export const ExampleCollection: CollectionConfig = {
|
||||
],
|
||||
}
|
||||
```
|
||||
|
||||
## 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}
|
||||
/>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
@@ -35,9 +35,9 @@ export const MyGroupField: Field = {
|
||||
|
||||
| Option | Description |
|
||||
| ---------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **`name`** | To be used as the property name when stored and retrieved from the database. [More](/docs/fields/overview#field-names) |
|
||||
| **`name`** \* | To be used as the property name when stored and retrieved from the database. [More](/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. Required when name is undefined, defaults to name converted to words. |
|
||||
| **`label`** | Used as a heading in the Admin Panel and to name the generated GraphQL type. |
|
||||
| **`validate`** | Provide a custom validation function that will be executed on both the Admin Panel and the backend. [More](/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). |
|
||||
@@ -55,25 +55,24 @@ _\* 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:
|
||||
The 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
|
||||
admin: { // highlight-line
|
||||
// ...
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
The Group Field inherits all of the default options from the base [Field Admin Config](./overview#admin-options), plus the following additional options:
|
||||
The Group Field inherits all of the default options from the base [Field Admin Config](../admin/fields#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. |
|
||||
| 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
|
||||
|
||||
@@ -86,7 +85,7 @@ export const ExampleCollection: CollectionConfig = {
|
||||
slug: 'example-collection',
|
||||
fields: [
|
||||
{
|
||||
name: 'pageMeta',
|
||||
name: 'pageMeta', // required
|
||||
type: 'group', // required
|
||||
interfaceName: 'Meta', // optional
|
||||
fields: [
|
||||
@@ -110,38 +109,3 @@ export const ExampleCollection: CollectionConfig = {
|
||||
],
|
||||
}
|
||||
```
|
||||
|
||||
## Presentational group fields
|
||||
|
||||
You can also use the Group field to create a presentational group of fields. This is useful when you want to group fields together visually without affecting the data structure.
|
||||
The label will be required when a `name` is not provided.
|
||||
|
||||
```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,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
```
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user