Compare commits
1 Commits
poc-realti
...
beta-issue
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6ce5d1fef6 |
7
.github/ISSUE_TEMPLATE/1.bug_report_v3.yml
vendored
7
.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
|
||||
|
||||
@@ -39,7 +39,6 @@ body:
|
||||
- 'db-postgres'
|
||||
- 'db-sqlite'
|
||||
- 'db-vercel-postgres'
|
||||
- 'email-nodemailer'
|
||||
- 'plugin: cloud'
|
||||
- 'plugin: cloud-storage'
|
||||
- 'plugin: form-builder'
|
||||
@@ -57,7 +56,7 @@ body:
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Environment Info
|
||||
description: Paste output from `pnpm payload info` _or_ Payload, Node.js, and Next.js versions.
|
||||
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.
|
||||
description: Paste output from `pnpm payload info` (>= beta.92) _or_ Payload, Node.js, and Next.js versions.
|
||||
render: text
|
||||
placeholder: |
|
||||
Payload:
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2022",
|
||||
"target": "es5",
|
||||
"lib": ["es2020.string"],
|
||||
"noEmit": true,
|
||||
"strict": true,
|
||||
|
||||
2
.github/actions/triage/tsconfig.json
vendored
2
.github/actions/triage/tsconfig.json
vendored
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2022",
|
||||
"target": "es5",
|
||||
"lib": ["es2020.string"],
|
||||
"noEmit": true,
|
||||
"strict": true,
|
||||
|
||||
15
.github/dependabot.yml
vendored
15
.github/dependabot.yml
vendored
@@ -8,7 +8,7 @@ updates:
|
||||
- /.github/workflows
|
||||
- /.github/actions/* # Not working until resolved: https://github.com/dependabot/dependabot-core/issues/6345
|
||||
- /.github/actions/setup
|
||||
target-branch: main
|
||||
target-branch: beta
|
||||
schedule:
|
||||
interval: monthly
|
||||
timezone: America/Detroit
|
||||
@@ -20,8 +20,7 @@ updates:
|
||||
|
||||
- package-ecosystem: npm
|
||||
directory: /
|
||||
target-branch: main
|
||||
open-pull-requests-limit: 0 # Only allow security updates
|
||||
target-branch: beta
|
||||
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
|
||||
target-branch: main
|
||||
schedule:
|
||||
interval: weekly
|
||||
day: sunday
|
||||
@@ -67,3 +70,5 @@ updates:
|
||||
- patch
|
||||
patterns:
|
||||
- '*'
|
||||
exclude-patterns:
|
||||
- 'drizzle*'
|
||||
|
||||
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
|
||||
|
||||
|
||||
29
.github/workflows/main.yml
vendored
29
.github/workflows/main.yml
vendored
@@ -9,6 +9,7 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- beta
|
||||
|
||||
concurrency:
|
||||
# <workflow_name>-<branch_name>-<true || commit_sha if branch is protected>
|
||||
@@ -274,22 +275,16 @@ jobs:
|
||||
if: matrix.database == 'supabase'
|
||||
|
||||
- name: Integration Tests
|
||||
uses: nick-fields/retry@v3
|
||||
run: pnpm test:int
|
||||
env:
|
||||
NODE_OPTIONS: --max-old-space-size=8096
|
||||
PAYLOAD_DATABASE: ${{ matrix.database }}
|
||||
POSTGRES_URL: ${{ env.POSTGRES_URL }}
|
||||
with:
|
||||
retry_on: any
|
||||
max_attempts: 5
|
||||
timeout_minutes: 15
|
||||
command: pnpm test:int
|
||||
on_retry_command: pnpm clean:all && pnpm install
|
||||
|
||||
tests-e2e:
|
||||
runs-on: ubuntu-latest
|
||||
needs: build
|
||||
name: e2e-${{ matrix.suite }}
|
||||
name: ${{ matrix.suite }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
@@ -299,10 +294,8 @@ jobs:
|
||||
- access-control
|
||||
- admin__e2e__1
|
||||
- admin__e2e__2
|
||||
- admin__e2e__3
|
||||
- admin-root
|
||||
- auth
|
||||
- auth-basic
|
||||
- field-error-states
|
||||
- fields-relationship
|
||||
- fields
|
||||
@@ -380,13 +373,7 @@ jobs:
|
||||
run: pnpm exec playwright install-deps chromium
|
||||
|
||||
- name: E2E Tests
|
||||
uses: nick-fields/retry@v3
|
||||
with:
|
||||
retry_on: any
|
||||
max_attempts: 5
|
||||
timeout_minutes: 20
|
||||
command: PLAYWRIGHT_JSON_OUTPUT_NAME=results_${{ matrix.suite }}.json pnpm test:e2e:prod:ci ${{ matrix.suite }}
|
||||
on_retry_command: pnpm clean:all && pnpm install && pnpm build:all
|
||||
run: PLAYWRIGHT_JSON_OUTPUT_NAME=results_${{ matrix.suite }}.json pnpm test:e2e:prod:ci ${{ matrix.suite }}
|
||||
env:
|
||||
PLAYWRIGHT_JSON_OUTPUT_NAME: results_${{ matrix.suite }}.json
|
||||
NEXT_TELEMETRY_DISABLED: 1
|
||||
@@ -427,10 +414,6 @@ jobs:
|
||||
- template: with-vercel-postgres
|
||||
database: postgres
|
||||
|
||||
# Re-enable once PG conncection is figured out
|
||||
# - template: with-vercel-website
|
||||
# database: postgres
|
||||
|
||||
name: ${{ matrix.template }}-${{ matrix.database }}
|
||||
|
||||
env:
|
||||
@@ -542,7 +525,7 @@ jobs:
|
||||
publish-canary:
|
||||
name: Publish Canary
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ needs.all-green.result == 'success' && github.ref_name == 'main' }}
|
||||
if: ${{ needs.all-green.result == 'success' && github.ref_name == 'beta' }}
|
||||
needs:
|
||||
- all-green
|
||||
|
||||
@@ -550,4 +533,4 @@ jobs:
|
||||
# debug github.ref output
|
||||
- run: |
|
||||
echo github.ref: ${{ github.ref }}
|
||||
echo isV3: ${{ github.ref == 'refs/heads/main' }}
|
||||
echo isBeta: ${{ github.ref == 'refs/heads/beta' }}
|
||||
|
||||
98
.github/workflows/post-release.yml
vendored
98
.github/workflows/post-release.yml
vendored
@@ -5,24 +5,16 @@ on:
|
||||
types:
|
||||
- published
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
tag:
|
||||
description: 'Release tag to process (optional)'
|
||||
required: false
|
||||
default: ''
|
||||
|
||||
env:
|
||||
NODE_VERSION: 22.6.0
|
||||
PNPM_VERSION: 9.7.1
|
||||
DO_NOT_TRACK: 1 # Disable Turbopack telemetry
|
||||
NEXT_TELEMETRY_DISABLED: 1 # Disable Next telemetry
|
||||
|
||||
jobs:
|
||||
post_release:
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.event_name != 'workflow_dispatch' }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
# Only needed if debugging on a branch other than default
|
||||
# ref: ${{ github.event.release.target_commitish || github.ref }}
|
||||
- uses: ./.github/actions/release-commenter
|
||||
continue-on-error: true
|
||||
env:
|
||||
@@ -36,85 +28,3 @@ jobs:
|
||||
|
||||
comment-template: |
|
||||
🚀 This is included in version {release_link}
|
||||
|
||||
github-releases-to-discord:
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.event_name != 'workflow_dispatch' }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Github Releases To Discord
|
||||
uses: SethCohen/github-releases-to-discord@v1
|
||||
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'
|
||||
|
||||
update_templates:
|
||||
if: false # Still needs some troubleshooting
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
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: 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 }}'
|
||||
|
||||
10
.github/workflows/pr-title.yml
vendored
10
.github/workflows/pr-title.yml
vendored
@@ -59,7 +59,6 @@ jobs:
|
||||
richtext-\*
|
||||
richtext-lexical
|
||||
richtext-slate
|
||||
sdk
|
||||
storage-\*
|
||||
storage-azure
|
||||
storage-gcs
|
||||
@@ -110,8 +109,13 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.event.action == 'opened'
|
||||
steps:
|
||||
- name: Tag with 2.x branch with v2
|
||||
if: github.event.pull_request.base.ref == '2.x'
|
||||
- name: Tag with main branch with v2
|
||||
if: github.event.pull_request.base.ref == 'main'
|
||||
uses: actions-ecosystem/action-add-labels@v1
|
||||
with:
|
||||
labels: v2
|
||||
- name: Tag with beta branch with v3
|
||||
if: github.event.pull_request.base.ref == 'beta'
|
||||
uses: actions-ecosystem/action-add-labels@v1
|
||||
with:
|
||||
labels: v3
|
||||
|
||||
3
.github/workflows/release-canary.yml
vendored
3
.github/workflows/release-canary.yml
vendored
@@ -2,6 +2,8 @@ name: release-canary
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
branches:
|
||||
- beta
|
||||
|
||||
env:
|
||||
NODE_VERSION: 22.6.0
|
||||
@@ -11,7 +13,6 @@ env:
|
||||
|
||||
jobs:
|
||||
release:
|
||||
name: release-canary-${{ github.ref_name }}-${{ github.sha }}
|
||||
permissions:
|
||||
id-token: write
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
4463
CHANGELOG.md
Normal file
4463
CHANGELOG.md
Normal file
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
|
||||
|
||||
42
README.md
42
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
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ 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](../overview) 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.
|
||||
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):
|
||||
|
||||
@@ -315,7 +315,7 @@ The following arguments are provided to the `unlock` function:
|
||||
|
||||
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'
|
||||
|
||||
@@ -6,7 +6,7 @@ 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](../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):
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ 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](../overview) 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.
|
||||
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):
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ keywords: admin, components, custom, documentation, Content Management System, c
|
||||
|
||||
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. 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.
|
||||
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>
|
||||
@@ -151,7 +151,7 @@ export default buildConfig({
|
||||
|
||||
## 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.
|
||||
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
|
||||
|
||||
@@ -185,7 +185,7 @@ Each Custom Component receives the following props by default:
|
||||
|
||||
<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](./collections#custom-components), [Global Components](./globals#custom-components), or [Field Components](./fields#custom-components) for a complete list of all default props per component.
|
||||
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
|
||||
@@ -546,5 +546,5 @@ export const useMyCustomContext = () => useContext(MyCustomContext)
|
||||
```
|
||||
|
||||
<Banner type="warning">
|
||||
<strong>Reminder:</strong>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.
|
||||
<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>
|
||||
|
||||
@@ -8,7 +8,7 @@ keywords: admin, css, scss, documentation, Content Management System, cms, headl
|
||||
|
||||
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
|
||||
|
||||
@@ -30,7 +30,7 @@ Here is an example of how you might target the Dashboard View and change the bac
|
||||
|
||||
<Banner type="warning">
|
||||
<strong>Note:</strong>
|
||||
If you are building [Custom Components](./components), 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.
|
||||
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
|
||||
@@ -62,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
|
||||
|
||||
@@ -66,7 +66,7 @@ A description can be configured in three ways:
|
||||
- As a function which returns a string. [More details](#description-functions).
|
||||
- As a React component. [More details](#description).
|
||||
|
||||
To add a Custom Description to a field, use the `admin.description` property in your [Field Config](../fields/overview):
|
||||
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'
|
||||
@@ -95,7 +95,7 @@ export const MyCollectionConfig: SanitizedCollectionConfig = {
|
||||
|
||||
Custom Descriptions can also be defined as a function. Description Functions are executed on the server and can be used to format simple descriptions based on the user's current [Locale](../configuration/localization).
|
||||
|
||||
To add a Description Function to a field, set the `admin.description` property to a _function_ in your [Field Config](../fields/overview):
|
||||
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'
|
||||
@@ -121,11 +121,6 @@ All Description Functions receive the following arguments:
|
||||
| -------------- | ---------------------------------------------------------------- |
|
||||
| **`t`** | The `t` function used to internationalize the Admin Panel. [More details](../configuration/i18n) |
|
||||
|
||||
<Banner type="info">
|
||||
<strong>Note:</strong>
|
||||
If you need to subscribe to live updates within your form, use a Description Component instead. [More details](#description).
|
||||
</Banner>
|
||||
|
||||
## Conditional Logic
|
||||
|
||||
You can show and hide fields based on what other fields are doing by utilizing conditional logic on a field by field basis. The `condition` property on a field's admin config accepts a function which takes three arguments:
|
||||
@@ -173,7 +168,7 @@ Within the [Admin Panel](./overview), fields are represented in three distinct p
|
||||
- [Cell](#cell) - The table cell component rendered in the List View.
|
||||
- [Filter](#filter) - The filter component rendered in the List View.
|
||||
|
||||
To swap in Field Components with your own, use the `admin.components` property in your [Field Config](../fields/overview):
|
||||
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'
|
||||
@@ -211,7 +206,7 @@ The following options are available:
|
||||
|
||||
The Field Component is the actual form field rendered in the Edit View. This is the input that user's will interact with when editing a document.
|
||||
|
||||
To swap in your own Field Component, use the `admin.components.Field` property in your [Field Config](../fields/overview):
|
||||
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'
|
||||
@@ -312,7 +307,7 @@ import type {
|
||||
|
||||
The Cell Component is rendered in the table of the List View. It represents the value of the field when displayed in a table cell.
|
||||
|
||||
To swap in your own Cell Component, use the `admin.components.Cell` property in your [Field Config](../fields/overview):
|
||||
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'
|
||||
@@ -337,35 +332,11 @@ All Cell Components receive the same [Default Field Component Props](#field), pl
|
||||
|
||||
For details on how to build Custom Components themselves, see [Building Custom Components](./components#building-custom-components).
|
||||
|
||||
### Filter
|
||||
|
||||
The Filter Component is the actual input element rendered within the "Filter By" dropdown of the List View used to represent this field when building filters.
|
||||
|
||||
To swap in your own Filter Component, use the `admin.components.Filter` property in your [Field Config](../fields/overview):
|
||||
|
||||
```ts
|
||||
import type { Field } from 'payload'
|
||||
|
||||
export const myField: Field = {
|
||||
name: 'myField',
|
||||
type: 'text',
|
||||
admin: {
|
||||
components: {
|
||||
Filter: '/path/to/MyCustomFilterComponent', // highlight-line
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
All Custom Filter Components receive the same [Default Field Component Props](#field).
|
||||
|
||||
For details on how to build Custom Components themselves, see [Building Custom Components](./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 swap in your own Label Component, use the `admin.components.Label` property in your [Field Config](../fields/overview):
|
||||
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'
|
||||
@@ -401,7 +372,7 @@ import type {
|
||||
|
||||
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 add a Description Component to a field, use the `admin.components.Description` property in your [Field Config](../fields/overview):
|
||||
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'
|
||||
@@ -443,7 +414,7 @@ import type {
|
||||
|
||||
The Error Component is rendered when a field fails validation. It is typically displayed beneath the field input in a visually-compelling style.
|
||||
|
||||
To swap in your own Error Component, use the `admin.components.Error` property in your [Field Config](../fields/overview):
|
||||
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'
|
||||
|
||||
@@ -21,11 +21,10 @@ 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
|
||||
const CustomTextField: React.FC = () => {
|
||||
const { value, setValue, path } = useField() // highlight-line
|
||||
|
||||
return (
|
||||
<div>
|
||||
@@ -785,7 +784,7 @@ const Greeting: React.FC = () => {
|
||||
|
||||
## useConfig
|
||||
|
||||
Used to retrieve the Payload [Client Config](./components#accessing-the-payload-config).
|
||||
Used to easily retrieve the Payload [Client Config](./components#accessing-the-payload-config).
|
||||
|
||||
```tsx
|
||||
'use client'
|
||||
|
||||
@@ -143,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
|
||||
|
||||
@@ -177,7 +177,7 @@ The following options are available:
|
||||
|
||||
<Banner type="success">
|
||||
<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).
|
||||
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
|
||||
@@ -237,7 +237,7 @@ The following options are available:
|
||||
|
||||
## 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
|
||||
|
||||
|
||||
@@ -47,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
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ To swap in your own Custom View, first consult the list of available components,
|
||||
|
||||
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 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):
|
||||
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'
|
||||
@@ -143,7 +143,7 @@ The above example shows how to add a new [Root View](#root-views), but the patte
|
||||
|
||||
Collection Views are views that are scoped under the `/collections` route, such as the Collection List and Document Edit views.
|
||||
|
||||
To 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):
|
||||
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'
|
||||
@@ -198,7 +198,7 @@ The following options are available:
|
||||
|
||||
Global Views are views that are scoped under the `/globals` route, such as the Document Edit View.
|
||||
|
||||
To 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):
|
||||
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'
|
||||
@@ -248,7 +248,7 @@ The following options are available:
|
||||
|
||||
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 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):
|
||||
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'
|
||||
@@ -350,8 +350,6 @@ export const MyCollectionConfig: SanitizedCollectionConfig = {
|
||||
}
|
||||
```
|
||||
|
||||
### Default Props
|
||||
|
||||
Your Custom Views will be provided with the following props:
|
||||
|
||||
| Prop | Description |
|
||||
@@ -361,7 +359,6 @@ Your Custom Views will be provided with the following props:
|
||||
| **`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). |
|
||||
|
||||
<Banner type="success">
|
||||
<strong>Reminder:</strong>
|
||||
|
||||
@@ -34,8 +34,8 @@ The following options are available:
|
||||
|
||||
| Option | Description |
|
||||
|----------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| **`generateEmailHTML`** | Allows for overriding the HTML within emails that are sent to users indicating how to validate their account. [More details](#generateemailhtml). |
|
||||
| **`generateEmailSubject`** | Allows for overriding the subject of the email that is sent to users indicating how to validate their account. [More details](#generateemailsubject). |
|
||||
| **`generateEmailHTML`** | 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
|
||||
|
||||
|
||||
@@ -273,7 +273,7 @@ const result = await payload.verifyEmail({
|
||||
|
||||
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**:
|
||||
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -46,6 +46,6 @@ _Creating a new project from an existing repository._
|
||||
|
||||
<Banner type="warning">
|
||||
<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/payload/tree/main/packages/payload-cloud) to your
|
||||
you will need to add the [Cloud Plugin](https://github.com/payloadcms/plugin-cloud) to your
|
||||
Payload app.
|
||||
</Banner>
|
||||
|
||||
@@ -28,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
|
||||
|
||||
|
||||
@@ -52,7 +52,7 @@ export const Posts: CollectionConfig = {
|
||||
|
||||
<Banner type="success">
|
||||
<strong>Reminder:</strong>
|
||||
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.
|
||||
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:
|
||||
|
||||
@@ -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](../configuration/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.
|
||||
|
||||
@@ -60,7 +60,7 @@ export const Nav: GlobalConfig = {
|
||||
|
||||
<Banner type="success">
|
||||
<strong>Reminder:</strong>
|
||||
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.
|
||||
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:
|
||||
|
||||
@@ -6,9 +6,9 @@ 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 configure I18n, use the `i18n` key in your [Payload Config](./overview):
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ import { buildConfig } from 'payload'
|
||||
export default buildConfig({
|
||||
// ...
|
||||
localization: {
|
||||
locales: ['en', 'es', 'de'], // required
|
||||
locales: ['en', 'es', 'de'] // required
|
||||
defaultLocale: 'en', // required
|
||||
},
|
||||
})
|
||||
|
||||
@@ -8,7 +8,7 @@ keywords: overview, config, configuration, documentation, Content Management Sys
|
||||
|
||||
Payload is a _config-based_, code-first CMS and application framework. The Payload Config is central to everything that Payload does, allowing for deep configuration of your application through a simple and intuitive API. The Payload Config is a fully-typed JavaScript object that can be infinitely extended upon.
|
||||
|
||||
Everything from your [Database](../database/overview) choice to the appearance of the [Admin Panel](../admin/overview) is fully controlled through the Payload Config. From here you can define [Fields](../fields/overview), add [Localization](./localization), enable [Authentication](../authentication/overview), configure [Access Control](../access-control/overview), and so much more.
|
||||
Everything from your [Database](../database/overview) choice, to the appearance of the [Admin Panel](../admin/overview), is fully controlled through the Payload Config. From here you can define [Fields](../fields/overview), add [Localization](./localization), enable [Authentication](../authentication/overview), configure [Access Control](../access-control/overview), and so much more.
|
||||
|
||||
The Payload Config is a `payload.config.ts` file typically located in the root of your project:
|
||||
|
||||
@@ -29,7 +29,7 @@ The Payload Config is strongly typed and ties directly into Payload's TypeScript
|
||||
|
||||
## Config Options
|
||||
|
||||
To author your Payload Config, first determine which [Database](../database/overview) you'd like to use, then use [Collections](./collections) or [Globals](./globals) to define the schema of your data through [Fields](../fields/overview).
|
||||
To author your Payload Config, first determine which [Database](../database/overview) you'd like to use, then use [Collections](./collections) or [Globals](./globals) to define the schema of your data.
|
||||
|
||||
Here is one of the simplest possible Payload configs:
|
||||
|
||||
@@ -58,7 +58,7 @@ export default buildConfig({
|
||||
|
||||
<Banner type="success">
|
||||
<strong>Note:</strong>
|
||||
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.
|
||||
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:
|
||||
|
||||
@@ -60,7 +60,7 @@ 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`. |
|
||||
| `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'. |
|
||||
|
||||
@@ -6,17 +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)
|
||||
|
||||
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.
|
||||
When necessary, some examples include a front-end. Examples that require a front-end share this folder structure:
|
||||
|
||||
```plaintext
|
||||
example/
|
||||
├── payload/
|
||||
├── next-app/
|
||||
├── next-pages/
|
||||
├── react-router/
|
||||
├── vue/
|
||||
├── svelte/
|
||||
```
|
||||
|
||||
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.
|
||||
|
||||
@@ -84,51 +84,6 @@ The Blocks Field inherits all of the default options from the base [Field Admin
|
||||
| **`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/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
|
||||
|
||||
This is super handy if you'd like to present your editors with a very deliberate and nicely designed block "preview" right in your rich text.
|
||||
|
||||
For example, if you have a `gallery` block, you might want to actually render the gallery of images directly in your Lexical block. With the `admin.components.Block` property, you can do exactly that!
|
||||
|
||||
<Banner type="success">
|
||||
<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 {
|
||||
// Edit block buttons (choose the one that corresponds to your usage)
|
||||
// When clicked, this will open a drawer with your block's fields
|
||||
// so your editors can edit them
|
||||
InlineBlockEditButton,
|
||||
BlockEditButton,
|
||||
|
||||
// Buttons that will remove this block from Lexical
|
||||
// (choose the one that corresponds to your usage)
|
||||
InlineBlockRemoveButton,
|
||||
BlockRemoveButton,
|
||||
|
||||
// The label that should be rendered for an inline block
|
||||
InlineBlockLabel,
|
||||
|
||||
// The default "container" that is rendered for an inline block
|
||||
// if you want to re-use it
|
||||
InlineBlockContainer,
|
||||
|
||||
// The default "collapsible" UI that is rendered for a regular block
|
||||
// if you want to re-use it
|
||||
BlockCollapsible,
|
||||
|
||||
} from '@payloadcms/richtext-lexical/client'
|
||||
```
|
||||
|
||||
## Block Configs
|
||||
|
||||
Blocks are defined as separate configs of their own.
|
||||
|
||||
@@ -86,7 +86,7 @@ _\* This property is passed directly to [react-datepicker](https://github.com/Ha
|
||||
|
||||
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/v2.29.3/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`.
|
||||
|
||||
|
||||
@@ -19,8 +19,8 @@ Payload's rich text field is built on an "adapter pattern" which lets you specif
|
||||
|
||||
Right now, Payload is officially supporting two rich text editors:
|
||||
|
||||
1. [SlateJS](/docs/rich-text/slate) - legacy, backwards-compatible with 1.0
|
||||
2. [Lexical](/docs/lexical/overview) - recommended
|
||||
1. [SlateJS](/docs/rich-text/slate) - stable, backwards-compatible with 1.0
|
||||
2. [Lexical](/docs/lexical/overview) - beta, where things will be moving
|
||||
|
||||
<Banner type="success">
|
||||
<strong>
|
||||
|
||||
@@ -108,7 +108,7 @@ called with an argument object with the following properties:
|
||||
| `id` | The `id` of the current document being edited. `id` is `undefined` during the `create` operation |
|
||||
| `user` | An object containing the currently authenticated user |
|
||||
|
||||
### Example#filter-options-example
|
||||
## Example
|
||||
|
||||
```ts
|
||||
const uploadField = {
|
||||
|
||||
@@ -34,7 +34,7 @@ Hooks allow you to execute your own side effects during specific events of the D
|
||||
|
||||
## Authentication
|
||||
|
||||
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, as well as your own external applications. [More details](../authentication/overview).
|
||||
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, all well as your own external applications. [More details](../authentication/overview).
|
||||
|
||||
## Access Control
|
||||
|
||||
@@ -68,10 +68,15 @@ Here's a quick example of a React Server Component fetching data using the Local
|
||||
```tsx
|
||||
import React from 'react'
|
||||
import config from '@payload-config'
|
||||
import { getPayload } from 'payload'
|
||||
import { getPayloadHMR } from '@payloadcms/next/utilities'
|
||||
|
||||
const MyServerComponent: React.FC = () => {
|
||||
const payload = await getPayload({ config })
|
||||
// If you're working in Next.js, and you want HMR,
|
||||
// you should get Payload via the `getPayloadHMR` function.
|
||||
const payload = await getPayloadHMR({ config })
|
||||
|
||||
// If you are writing a standalone script and do not need HMR,
|
||||
// you can get Payload via import { getPayload } from 'payload' instead.
|
||||
|
||||
// The `findResult` here will be fully typed as `PaginatedDocs<Page>`,
|
||||
// where you will have the `docs` that are returned as well as
|
||||
@@ -151,7 +156,7 @@ Whereas Payload itself is responsible for direct database access, and control ov
|
||||
|
||||
`@payloadcms/graphql`
|
||||
|
||||
All of Payload's GraphQL functionality is abstracted into a separate package. Payload, its Admin UI, and REST API have absolutely no overlap with GraphQL, and you will incur no performance overhead from GraphQL if you are not using it. However, it's installed within the `@payloadcms/next` package so you don't have to install it manually. You do, however, need to have GraphQL installed separately in your `package.json` if you are using GraphQL.
|
||||
All of Payload's GraphQL functionality is abstracted into a separate package. Payload, its Admin UI, and REST API have absolutely no overlap with GraphQL, and you will incur no performance overhead from GraphQL if you are not using it. However, it's installed within in the `@payloadcms/next` package so you don't have to install it manually. You do, however, need to have GraphQL installed separately in your `package.json` if you are using GraphQL.
|
||||
|
||||
`@payloadcms/ui`
|
||||
|
||||
|
||||
@@ -24,14 +24,14 @@ Payload requires the following software:
|
||||
To quickly scaffold a new Payload app in the fastest way possible, you can use [create-payload-app](https://npmjs.com/package/create-payload-app). To do so, run the following command:
|
||||
|
||||
```
|
||||
npx create-payload-app
|
||||
npx create-payload-app@beta
|
||||
```
|
||||
|
||||
Then just follow the prompts! You'll get set up with a new folder and a functioning Payload app inside. You can then start [configuring your application](../configuration/overview).
|
||||
|
||||
## Adding to an existing app
|
||||
|
||||
Adding Payload to an existing Next.js app is super straightforward. You can either run the `npx create-payload-app` command inside your Next.js project's folder, or manually install Payload by following the steps below.
|
||||
Adding Payload to an existing Next.js app is super straightforward. You can either run the `npx create-payload-app@beta` command inside your Next.js project's folder, or manually install Payload by following the steps below.
|
||||
|
||||
If you don't have a Next.js app already, but you still want to start a project from a blank Next.js app, you can create a new Next.js app using `npx create-next-app` - and then just follow the steps below to install Payload.
|
||||
|
||||
@@ -44,7 +44,7 @@ If you don't have a Next.js app already, but you still want to start a project f
|
||||
First, you'll want to add the required Payload packages to your project and can do so by running the command below:
|
||||
|
||||
```bash
|
||||
pnpm i payload @payloadcms/next @payloadcms/richtext-lexical sharp graphql
|
||||
pnpm i payload@beta @payloadcms/next@beta @payloadcms/richtext-lexical@beta sharp graphql
|
||||
```
|
||||
|
||||
<Banner type="warning">
|
||||
@@ -58,12 +58,12 @@ To install a Database Adapter, you can run **one** of the following commands:
|
||||
|
||||
- To install the [MongoDB Adapter](../database/mongodb), run:
|
||||
```bash
|
||||
pnpm i @payloadcms/db-mongodb
|
||||
pnpm i @payloadcms/db-mongodb@beta
|
||||
```
|
||||
|
||||
- To install the [Postgres Adapter](../database/postgres), run:
|
||||
```bash
|
||||
pnpm i @payloadcms/db-postgres
|
||||
pnpm i @payloadcms/db-postgres@beta
|
||||
```
|
||||
|
||||
<Banner type="success">
|
||||
@@ -73,7 +73,7 @@ To install a Database Adapter, you can run **one** of the following commands:
|
||||
|
||||
#### 2. Copy Payload files into your Next.js app folder
|
||||
|
||||
Payload installs directly in your Next.js `/app` folder, and you'll need to place some files into that folder for Payload to run. You can copy these files from the [Blank Template](https://github.com/payloadcms/payload/tree/main/templates/blank/src/app/(payload)) on GitHub. Once you have the required Payload files in place in your `/app` folder, you should have something like this:
|
||||
Payload installs directly in your Next.js `/app` folder, and you'll need to place some files into that folder for Payload to run. You can copy these files from the [Blank Template](https://github.com/payloadcms/payload/tree/beta/templates/blank/src/app/(payload)) on GitHub. Once you have the required Payload files in place in your `/app` folder, you should have something like this:
|
||||
|
||||
```plaintext
|
||||
app/
|
||||
@@ -181,6 +181,6 @@ Once you have a Payload Config, update your `tsconfig` to include a `path` that
|
||||
|
||||
#### 5. Fire it up!
|
||||
|
||||
After you've reached this point, it's time to boot up Payload. Start your project in your application's folder to get going. By default, the Next.js dev script is `pnpm dev` (or `npm run dev` if using NPM).
|
||||
After you've gotten this far, it's time to boot up Payload. Start your project in your application's folder to get going. By default, the Next.js dev script is `pnpm dev` (or `npm run dev` if using NPM).
|
||||
|
||||
After it starts, you can go to `http://localhost:3000/admin` to create your first Payload user!
|
||||
|
||||
@@ -3,7 +3,7 @@ title: What is Payload?
|
||||
label: What is Payload?
|
||||
order: 10
|
||||
desc: Payload is a next-gen application framework that can be used as a Content Management System, enterprise tool framework, headless commerce platform, or digital asset management tool.
|
||||
keywords: documentation, getting started, guide, Content Management System, cms, headless, javascript, node, react
|
||||
keywords: documentation, getting started, guide, Content Management System, cms, headless, javascript, node, react, express
|
||||
---
|
||||
|
||||
<YouTube
|
||||
@@ -24,7 +24,7 @@ keywords: documentation, getting started, guide, Content Management System, cms,
|
||||
|
||||
### Instant backend superpowers
|
||||
|
||||
No matter what you're building, Payload will give you backend superpowers. Your entire Payload config can be installed in one line into any existing Next.js app, and is designed to catapult your development process. Payload takes the most complex and time-consuming parts of any modern web app and makes them simple.
|
||||
No matter what you're building, Payload will give you backend superpowers. It can be installed in one line into any existing Next.js app, and is designed to catapult your development process. Payload takes the most complex and time-consuming parts of any modern web app and makes them simple.
|
||||
|
||||
### Open source - deploy anywhere, including Vercel
|
||||
|
||||
@@ -95,6 +95,12 @@ Payload can integrate with any payment processor like Stripe and its content aut
|
||||
|
||||
If you can build your storefront with a single backend, and only offload things like payment processing, the code will be simpler and the editing experience will be significantly streamlined. Manage products, catalogs, page content, media, and more—all in one spot.
|
||||
|
||||
Payload's official Ecommerce template gives you everything you need for a storefront out of the box, including a Next.js frontend, product variations, and a full Stripe implementation:
|
||||
|
||||
```
|
||||
npx create-payload-app@latest -t ecommerce
|
||||
```
|
||||
|
||||
### Digital Asset Management
|
||||
|
||||
Payload's API-first tagging, sorting, and querying engine lends itself perfectly to all types of content that a CMS might ordinarily store, but these strong fundamentals also make it a formidable Digital Asset Management (DAM) tool as well.
|
||||
|
||||
@@ -13,7 +13,7 @@ In Payload the schema is controlled by your collections and globals. All you nee
|
||||
Install `@payloadcms/graphql` as a dev dependency:
|
||||
|
||||
```bash
|
||||
pnpm add @payloadcms/graphql -D
|
||||
pnpm add @payloadcms/graphql@beta -D
|
||||
```
|
||||
|
||||
Run the following command to generate the schema:
|
||||
|
||||
@@ -32,7 +32,7 @@ Examples:
|
||||
|
||||
**Periodic sync or similar scheduled action**
|
||||
|
||||
Some applications may need to perform a regularly scheduled operation of some type. Jobs are perfect for this because you can execute their logic using `cron`, scheduled nightly, every twelve hours, or some similar time period.
|
||||
Some applications may need to perform a regularly scheduled operation of some type. Jobs are perfect for this because you can execute their logic using `cron`, scheduled nightly, every twelve hours, or some similar time period.
|
||||
|
||||
Examples:
|
||||
|
||||
@@ -49,22 +49,22 @@ Examples:
|
||||
- You need to create (and then keep in sync) vector embeddings of your documents as they change, but you use an open source model to generate embeddings
|
||||
- You have a PDF generator that needs to dynamically build and send PDF versions of documents to customers
|
||||
- You need to use a headless browser to perform some type of logic
|
||||
- You need to perform a series of actions, each of which depends on a prior action and should be run in as "durable" of a fashion as possible
|
||||
- You need to perform a series of actions, each of which depends on a prior action and should be run in as "durable" of a fashion as possible
|
||||
|
||||
### How it works
|
||||
|
||||
There are a few concepts that you should become familiarized with before using Payload's Jobs Queue. We recommend learning what each of these does in order to fully understand how to leverage the power of Payload's Jobs Queue.
|
||||
|
||||
1. [Tasks](/docs/jobs-queue/tasks)
|
||||
1. [Workflows](/docs/jobs-queue/workflows)
|
||||
1. [Jobs](/docs/jobs-queue/jobs)
|
||||
1. [Queues](/docs/jobs-queue/queues)
|
||||
1. [Tasks](/docs/beta/jobs-queue/tasks)
|
||||
1. [Workflows](/docs/beta/jobs-queue/workflows)
|
||||
1. [Jobs](/docs/beta/jobs-queue/jobs)
|
||||
1. [Queues](/docs/beta/jobs-queue/queues)
|
||||
|
||||
All of these pieces work together in order to allow you to offload long-running, expensive, or future scheduled work from your main APIs.
|
||||
All of these pieces work together in order to allow you to offload long-running, expensive, or future scheduled work from your main APIs.
|
||||
|
||||
Here's a quick overview:
|
||||
|
||||
- A Task is a specific function that performs business logic
|
||||
- Workflows are groupings of specific tasks which should be run in-order, and can be retried from a specific point of failure
|
||||
- A Job is an instance of a single task or workflow which will be executed
|
||||
- A Queue is a way to segment your jobs into different "groups" - for example, some to run nightly, and others to run every 10 minutes
|
||||
- A Queue is a way to segment your jobs into different "groups" - for example, some to run nightly, and others to run every 10 minutes
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
title: Tasks
|
||||
label: Tasks
|
||||
order: 20
|
||||
desc: A Task is a distinct function declaration that can be run within Payload's Jobs Queue.
|
||||
desc: A Task is a distinct function declaration that can be run within Payload's Jobs Queue.
|
||||
keywords: jobs queue, application framework, typescript, node, react, nextjs
|
||||
---
|
||||
|
||||
@@ -10,7 +10,7 @@ keywords: jobs queue, application framework, typescript, node, react, nextjs
|
||||
A <strong>"Task"</strong> is a function definition that performs business logic and whose input and output are both strongly typed.
|
||||
</Banner>
|
||||
|
||||
You can register Tasks on the Payload config, and then create [Jobs](/docs/jobs-queue/jobs) or [Workflows](/docs/jobs-queue/workflows) that use them. Think of Tasks like tidy, isolated "functions that do one specific thing".
|
||||
You can register Tasks on the Payload config, and then create [Jobs](/docs/beta/jobs-queue/jobs) or [Workflows](/docs/beta/jobs-queue/workflows) that use them. Think of Tasks like tidy, isolated "functions that do one specific thing".
|
||||
|
||||
Payload Tasks can be configured to automatically retried if they fail, which makes them valuable for "durable" workflows like AI applications where LLMs can return non-deterministic results, and might need to be retried.
|
||||
|
||||
|
||||
@@ -77,13 +77,13 @@ export const MyFeature = createServerFeature({
|
||||
|
||||
This allows you to add i18n translations scoped to your feature. This specific example translation will be available under `lexical:myFeature:label` - `myFeature` being your feature key.
|
||||
|
||||
### Markdown Transformers#server-feature-markdown-transformers
|
||||
### Markdown Transformers
|
||||
|
||||
The Server Feature, just like the Client Feature, allows you to add markdown transformers. Markdown transformers on the server are used when [converting the editor from or to markdown](/docs/lexical/converters#markdown-lexical).
|
||||
|
||||
```ts
|
||||
import { createServerFeature } from '@payloadcms/richtext-lexical';
|
||||
import type { ElementTransformer } from '@payloadcms/richtext-lexical/lexical/markdown'
|
||||
import type { ElementTransformer } from '@lexical/markdown'
|
||||
import {
|
||||
$createMyNode,
|
||||
$isMyNode,
|
||||
@@ -120,7 +120,7 @@ export const MyFeature = createServerFeature({
|
||||
|
||||
In this example, the node will be outputted as `+++` in Markdown, and the markdown `+++` will be converted to a `MyNode` node in the editor.
|
||||
|
||||
### Nodes#server-feature-nodes
|
||||
### Nodes
|
||||
|
||||
While nodes added to the server feature do not control how the node is rendered in the editor, they control other aspects of the node:
|
||||
- HTML conversion
|
||||
@@ -266,7 +266,7 @@ export const MyClientFeature = createClientFeature({
|
||||
|
||||
Explore the APIs available through ClientFeature to add the specific functionality you need. Remember, do not import directly from `'@payloadcms/richtext-lexical'` when working on the client-side, as it will cause errors with webpack or turbopack. Instead, use `'@payloadcms/richtext-lexical/client'` for all client-side imports. Type-imports are excluded from this rule and can always be imported.
|
||||
|
||||
### Nodes#client-feature-nodes
|
||||
### Nodes
|
||||
|
||||
Add nodes to the `nodes` array in **both** your client & server feature. On the server side, nodes are utilized for backend operations like HTML conversion in a headless editor. On the client side, these nodes are integral to how content is displayed and managed in the editor, influencing how they are rendered, behave, and saved in the database.
|
||||
|
||||
@@ -299,9 +299,9 @@ import type {
|
||||
EditorConfig,
|
||||
LexicalNode,
|
||||
SerializedLexicalNode,
|
||||
} from '@payloadcms/richtext-lexical/lexical'
|
||||
} from 'lexical'
|
||||
|
||||
import { $applyNodeReplacement, DecoratorNode } from '@payloadcms/richtext-lexical/lexical'
|
||||
import { $applyNodeReplacement, DecoratorNode } from 'lexical'
|
||||
|
||||
// SerializedLexicalNode is the default lexical node.
|
||||
// By setting your SerializedMyNode type to SerializedLexicalNode,
|
||||
@@ -448,17 +448,17 @@ Example plugin.tsx:
|
||||
'use client'
|
||||
import type {
|
||||
LexicalCommand,
|
||||
} from '@payloadcms/richtext-lexical/lexical'
|
||||
} from 'lexical'
|
||||
|
||||
import {
|
||||
createCommand,
|
||||
$getSelection,
|
||||
$isRangeSelection,
|
||||
COMMAND_PRIORITY_EDITOR
|
||||
} from '@payloadcms/richtext-lexical/lexical'
|
||||
} from 'lexical'
|
||||
|
||||
import { useLexicalComposerContext } from '@payloadcms/richtext-lexical/lexical/react/LexicalComposerContext.js'
|
||||
import { $insertNodeToNearestRoot } from '@payloadcms/richtext-lexical/lexical/utils'
|
||||
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext.js'
|
||||
import { $insertNodeToNearestRoot } from '@lexical/utils'
|
||||
import { useEffect } from 'react'
|
||||
|
||||
import type { PluginComponent } from '@payloadcms/richtext-lexical' // type imports can be imported from @payloadcms/richtext-lexical - even on the client
|
||||
@@ -589,7 +589,7 @@ import { createClientFeature, toolbarAddDropdownGroupWithItems } from '@payloadc
|
||||
import { IconComponent } from './icon';
|
||||
import { $isHorizontalRuleNode } from './nodes/MyNode';
|
||||
import { INSERT_MYNODE_COMMAND } from './plugin';
|
||||
import { $isNodeSelection } from '@payloadcms/richtext-lexical/lexical'
|
||||
import { $isNodeSelection } from 'lexical'
|
||||
|
||||
export const MyClientFeature = createClientFeature({
|
||||
toolbarFixed: {
|
||||
@@ -705,13 +705,13 @@ export const MyClientFeature = createClientFeature({
|
||||
| **`keywords`** | Keywords are used to match the item for different texts typed after the '/'. E.g. you might want to show a horizontal rule item if you type both /hr, /separator, /horizontal etc. In addition to the keywords, the label and key will be used to find the right slash menu item. |
|
||||
|
||||
|
||||
### Markdown Transformers#client-feature-markdown-transformers
|
||||
### Markdown Transformers
|
||||
|
||||
The Client Feature, just like the Server Feature, allows you to add markdown transformers. Markdown transformers on the client are used to create new nodes when a certain markdown pattern is typed in the editor.
|
||||
|
||||
```ts
|
||||
import { createClientFeature } from '@payloadcms/richtext-lexical/client';
|
||||
import type { ElementTransformer } from '@payloadcms/richtext-lexical/lexical/markdown'
|
||||
import type { ElementTransformer } from '@lexical/markdown'
|
||||
import {
|
||||
$createMyNode,
|
||||
$isMyNode,
|
||||
@@ -836,4 +836,4 @@ The reason the client feature does not have the same props available as the serv
|
||||
|
||||
## More information
|
||||
|
||||
Have a look at the [features we've already built](https://github.com/payloadcms/payload/tree/main/packages/richtext-lexical/src/features) - understanding how they work will help you understand how to create your own. There is no difference between the features included by default and the ones you create yourself - since those features are all isolated from the "core", you have access to the same APIs, whether the feature is part of Payload or not!
|
||||
Have a look at the [features we've already built](https://github.com/payloadcms/payload/tree/beta/packages/richtext-lexical/src/features) - understanding how they work will help you understand how to create your own. There is no difference between the features included by default and the ones you create yourself - since those features are all isolated from the "core", you have access to the same APIs, whether the feature is part of Payload or not!
|
||||
|
||||
@@ -6,67 +6,14 @@ desc: Conversion between lexical, markdown and html
|
||||
keywords: lexical, rich text, editor, headless cms, convert, html, mdx, markdown, md, conversion, export
|
||||
---
|
||||
|
||||
Lexical saves data in JSON - this is great for storage and flexibility and allows you to easily to convert it to other formats like JSX, HTML or Markdown.
|
||||
|
||||
## Lexical => JSX
|
||||
|
||||
If you have a React-based frontend, converting lexical to JSX is the recommended way to render rich text content in your frontend. To do that, import the `RichText` component from `@payloadcms/richtext-lexical/react` and pass the lexical content to it:
|
||||
|
||||
```tsx
|
||||
import React from 'react'
|
||||
import { RichText } from '@payloadcms/richtext-lexical/react'
|
||||
|
||||
export const MyComponent = ({ lexicalData }) => {
|
||||
return (
|
||||
<RichText data={lexicalData} />
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
The `RichText` component will come with the most common serializers built-in, though you can also pass in your own serializers if you need to.
|
||||
|
||||
<Banner type="default">
|
||||
The JSX converter expects the input data to be fully populated. When fetching data, ensure the `depth` setting is high enough, to ensure that lexical nodes such as uploads are populated.
|
||||
</Banner>
|
||||
|
||||
### Converting Lexical Blocks to JSX
|
||||
|
||||
In order to convert lexical blocks or inline blocks to JSX, you will have to pass the converter for your block to the RichText component. This converter is not included by default, as Payload doesn't know how to render your custom blocks.
|
||||
|
||||
```tsx
|
||||
import React from 'react'
|
||||
import {
|
||||
type JSXConvertersFunction,
|
||||
RichText,
|
||||
} from '@payloadcms/richtext-lexical/react'
|
||||
import type { SerializedEditorState } from '@payloadcms/richtext-lexical/lexical'
|
||||
|
||||
const jsxConverters: JSXConvertersFunction = ({ defaultConverters }) => ({
|
||||
...defaultConverters,
|
||||
blocks: {
|
||||
// myTextBlock is the slug of the block
|
||||
myTextBlock: ({ node }) => <div style={{ backgroundColor: 'red' }}>{node.fields.text}</div>,
|
||||
},
|
||||
})
|
||||
|
||||
export const MyComponent = ({ lexicalData }) => {
|
||||
return (
|
||||
<RichText
|
||||
converters={jsxConverters}
|
||||
data={lexicalData.lexicalWithBlocks as SerializedEditorState}
|
||||
/>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
## Lexical => HTML
|
||||
|
||||
If you don't have a React-based frontend, or if you need to send the content to a third-party service, you can convert lexical to HTML. There are two ways to do this:
|
||||
Lexical saves data in JSON, but can also generate its HTML representation via two main methods:
|
||||
|
||||
1. **Outputting HTML from the Collection:** Create a new field in your collection to convert saved JSON content to HTML. Payload generates and outputs the HTML for use in your frontend.
|
||||
2. **Generating HTML on any server** Convert JSON to HTML on-demand on the server.
|
||||
|
||||
In both cases, the conversion needs to happen on a server, as the HTML converter will automatically fetch data for nodes that require it (e.g. uploads and internal links). The editor comes with built-in HTML serializers, simplifying the process of converting JSON to HTML.
|
||||
The editor comes with built-in HTML serializers, simplifying the process of converting JSON to HTML.
|
||||
|
||||
### Outputting HTML from the Collection
|
||||
|
||||
@@ -253,7 +200,7 @@ Lexical provides a seamless way to perform conversions between various other for
|
||||
A headless editor can perform such conversions outside of the main editor instance. Follow this method to initiate a headless editor:
|
||||
|
||||
```ts
|
||||
import { createHeadlessEditor } from '@payloadcms/richtext-lexical/lexical/headless'
|
||||
import { createHeadlessEditor } from '@lexical/headless' // <= make sure this package is installed
|
||||
import { getEnabledNodes, sanitizeServerEditorConfig } from '@payloadcms/richtext-lexical'
|
||||
|
||||
const yourEditorConfig // <= your editor config here
|
||||
@@ -290,7 +237,7 @@ If you have access to the sanitized collection config, you can get access to the
|
||||
|
||||
```ts
|
||||
import type { CollectionConfig, RichTextField } from 'payload'
|
||||
import { createHeadlessEditor } from '@payloadcms/richtext-lexical/lexical/headless'
|
||||
import { createHeadlessEditor } from '@lexical/headless'
|
||||
import type { LexicalRichTextAdapter, SanitizedServerEditorConfig } from '@payloadcms/richtext-lexical'
|
||||
import {
|
||||
getEnabledNodes,
|
||||
@@ -345,8 +292,8 @@ export const MyCollection: CollectionConfig = {
|
||||
Once you have your headless editor instance, you can use it to convert HTML to Lexical:
|
||||
|
||||
```ts
|
||||
import { $generateNodesFromDOM } from '@payloadcms/richtext-lexical/lexical/html'
|
||||
import { $getRoot, $getSelection } from '@payloadcms/richtext-lexical/lexical'
|
||||
import { $generateNodesFromDOM } from '@lexical/html'
|
||||
import { $getRoot, $getSelection } from 'lexical'
|
||||
import { JSDOM } from 'jsdom'
|
||||
|
||||
headlessEditor.update(
|
||||
@@ -387,7 +334,8 @@ This has been taken from the [lexical serialization & deserialization docs](http
|
||||
Convert markdown content to the Lexical editor format with the following:
|
||||
|
||||
```ts
|
||||
import { sanitizeServerEditorConfig, $convertFromMarkdownString } from '@payloadcms/richtext-lexical'
|
||||
import { $convertFromMarkdownString } from '@lexical/markdown'
|
||||
import { sanitizeServerEditorConfig } from '@payloadcms/richtext-lexical'
|
||||
|
||||
const yourSanitizedEditorConfig = sanitizeServerEditorConfig(yourEditorConfig, payloadConfig) // <= your editor config & Payload Config here
|
||||
const markdown = `# Hello World`
|
||||
@@ -413,9 +361,9 @@ Export content from the Lexical editor into Markdown format using these steps:
|
||||
Here's the code for it:
|
||||
|
||||
```ts
|
||||
import { $convertToMarkdownString } from '@payloadcms/richtext-lexical/lexical/markdown'
|
||||
import { $convertToMarkdownString } from '@lexical/markdown'
|
||||
import { sanitizeServerEditorConfig } from '@payloadcms/richtext-lexical'
|
||||
import type { SerializedEditorState } from '@payloadcms/richtext-lexical/lexical'
|
||||
import type { SerializedEditorState } from 'lexical'
|
||||
|
||||
const yourSanitizedEditorConfig = sanitizeServerEditorConfig(yourEditorConfig, payloadConfig) // <= your editor config & Payload Config here
|
||||
const yourEditorState: SerializedEditorState // <= your current editor state here
|
||||
@@ -449,8 +397,8 @@ Export content from the Lexical editor into plain text using these steps:
|
||||
Here's the code for it:
|
||||
|
||||
```ts
|
||||
import type { SerializedEditorState } from '@payloadcms/richtext-lexical/lexical'
|
||||
import { $getRoot } from '@payloadcms/richtext-lexical/lexical'
|
||||
import type { SerializedEditorState } from 'lexical'
|
||||
import { $getRoot } from 'lexical'
|
||||
|
||||
const yourEditorState: SerializedEditorState // <= your current editor state here
|
||||
|
||||
|
||||
@@ -10,6 +10,12 @@ One of Payload's goals is to build the best rich text editor experience that we
|
||||
|
||||
Classically, we've used SlateJS to work toward this goal, but building custom elements into Slate has proven to be more difficult than we'd like, and we've been keeping our options open.
|
||||
|
||||
<Banner type="warning">
|
||||
Payload's Lexical rich text editor is currently in beta. It's stable enough to use as you build on
|
||||
Payload, so if you're up for helping us fine-tune it, you should use it. But if you're looking for
|
||||
stability, use Slate instead.
|
||||
</Banner>
|
||||
|
||||
Lexical is extremely impressive and trivializes a lot of the hard parts of building new elements into a rich text editor. It has a few distinct advantages over Slate, including the following:
|
||||
|
||||
1. A "/" menu, which allows editors to easily add new elements while never leaving their keyboard
|
||||
|
||||
@@ -34,11 +34,11 @@ Then, render the `RefreshRouteOnSave` component anywhere in your `page.tsx`. Her
|
||||
|
||||
```tsx
|
||||
import { RefreshRouteOnSave } from './RefreshRouteOnSave.tsx'
|
||||
import { getPayload } from 'payload'
|
||||
import { getPayloadHMR } from '@payloadcms/next/utilities'
|
||||
import config from '../payload.config'
|
||||
|
||||
export default async function Page() {
|
||||
const payload = await getPayload({ config })
|
||||
const payload = await getPayloadHMR({ config })
|
||||
|
||||
const page = await payload.findByID({
|
||||
collection: 'pages',
|
||||
|
||||
@@ -18,9 +18,12 @@ Payload can be used completely outside of Next.js which is helpful in cases like
|
||||
|
||||
Payload provides a convenient way to run standalone scripts, which can be useful for tasks like seeding your database or performing one-off operations.
|
||||
|
||||
In standalone scripts, you can simply import the Payload Config and use it right away. If you need an initialized copy of Payload, you can then use the `getPayload` function. This can be useful for tasks like seeding your database or performing other one-off operations.
|
||||
In standalone scripts, can simply import the Payload Config and use it right away. If you need an initialized copy of Payload, you can then use the `getPayload` function. This can be useful for tasks like seeding your database or performing other one-off operations.
|
||||
|
||||
```ts
|
||||
// We are importing `getPayload` because we don't need HMR
|
||||
// for a standalone script. For usage of Payload inside Next.js,
|
||||
// you should always use `import { getPayloadHMR } from '@payloadcms/next/utilities'` instead.
|
||||
import { getPayload } from 'payload'
|
||||
import config from '@payload-config'
|
||||
|
||||
|
||||
@@ -43,6 +43,27 @@ const afterChangeHook: CollectionAfterChangeHook = async ({ req: { payload } })
|
||||
|
||||
If you want to import Payload in places where you don't have the option to access it from function arguments or `req`, you can import it and initialize it.
|
||||
|
||||
There are two places to import Payload:
|
||||
|
||||
**Option 1 - using HMR, within Next.js**
|
||||
|
||||
```ts
|
||||
import { getPayloadHMR } from '@payloadcms/next/utilities'
|
||||
import config from '@payload-config'
|
||||
|
||||
const payload = await getPayloadHMR({ config })
|
||||
```
|
||||
|
||||
You should import Payload using the first option (`getPayloadHMR`) if you are using Payload inside of Next.js (like route handlers, server components, and similar.)
|
||||
|
||||
This way, in Next.js development mode, Payload will work with Hot Module Replacement (HMR), and as you make changes to your Payload Config, your usage of Payload will always be in sync with your changes. In production, `getPayloadHMR` simply disables all HMR functionality so you don't need to write your code any differently. We handle optimization for you in production mode.
|
||||
|
||||
If you are accessing Payload via function arguments or `req.payload`, HMR is automatically supported if you are using it within Next.js.
|
||||
|
||||
**Option 2 - outside of Next.js**
|
||||
|
||||
If you are using Payload outside of Next.js, for example in standalone scripts or in other frameworks, you can import Payload with no HMR functionality. Instead of using `getPayloadHMR`, you can use `getPayload`.
|
||||
|
||||
```ts
|
||||
import { getPayload } from 'payload'
|
||||
import config from '@payload-config'
|
||||
@@ -50,11 +71,7 @@ import config from '@payload-config'
|
||||
const payload = await getPayload({ config })
|
||||
```
|
||||
|
||||
If you're working in Next.js' development mode, Payload will work with Hot Module Replacement (HMR), and as you make changes to your Payload Config, your usage of Payload will always be in sync with your changes. In production, `getPayload` simply disables all HMR functionality so you don't need to write your code any differently. We handle optimization for you in production mode.
|
||||
|
||||
If you are accessing Payload via function arguments or `req.payload`, HMR is automatically supported if you are using it within Next.js.
|
||||
|
||||
For more information about using Payload outside of Next.js, [click here](./outside-nextjs).
|
||||
Both options function in exactly the same way outside of one having HMR support and the other not. For more information about using Payload outside of Next.js, [click here](./outside-nextjs).
|
||||
|
||||
## Local options available
|
||||
|
||||
@@ -102,7 +119,7 @@ const post = await payload.find({
|
||||
|
||||
The following Collection operations are available through the Local API:
|
||||
|
||||
### Create#collection-create
|
||||
### Create
|
||||
|
||||
```js
|
||||
// The created Post document is returned
|
||||
@@ -134,7 +151,7 @@ const post = await payload.create({
|
||||
})
|
||||
```
|
||||
|
||||
### Find#collection-find
|
||||
### Find
|
||||
|
||||
```js
|
||||
// Result will be a paginated set of Posts.
|
||||
@@ -155,7 +172,7 @@ const result = await payload.find({
|
||||
})
|
||||
```
|
||||
|
||||
### Find by ID#collection-find-by-id
|
||||
### Find by ID
|
||||
|
||||
```js
|
||||
// Result will be a Post document.
|
||||
@@ -171,7 +188,7 @@ const result = await payload.findByID({
|
||||
})
|
||||
```
|
||||
|
||||
### Count#collection-count
|
||||
### Count
|
||||
|
||||
```js
|
||||
// Result will be an object with:
|
||||
@@ -187,7 +204,7 @@ const result = await payload.count({
|
||||
})
|
||||
```
|
||||
|
||||
### Update by ID#collection-update-by-id
|
||||
### Update by ID
|
||||
|
||||
```js
|
||||
// Result will be the updated Post document.
|
||||
@@ -219,7 +236,7 @@ const result = await payload.update({
|
||||
})
|
||||
```
|
||||
|
||||
### Update Many#collection-update-many
|
||||
### Update Many
|
||||
|
||||
```js
|
||||
// Result will be an object with:
|
||||
@@ -258,7 +275,7 @@ const result = await payload.update({
|
||||
})
|
||||
```
|
||||
|
||||
### Delete#collection-delete
|
||||
### Delete
|
||||
|
||||
```js
|
||||
// Result will be the now-deleted Post document.
|
||||
@@ -275,7 +292,7 @@ const result = await payload.delete({
|
||||
})
|
||||
```
|
||||
|
||||
### Delete Many#collection-delete-many
|
||||
### Delete Many
|
||||
|
||||
```js
|
||||
// Result will be an object with:
|
||||
@@ -394,7 +411,7 @@ const result = await payload.verifyEmail({
|
||||
|
||||
The following Global operations are available through the Local API:
|
||||
|
||||
### Find#global-find
|
||||
### Find
|
||||
|
||||
```js
|
||||
// Result will be the Header Global.
|
||||
@@ -409,7 +426,7 @@ const result = await payload.findGlobal({
|
||||
})
|
||||
```
|
||||
|
||||
### Update#global-update
|
||||
### Update
|
||||
|
||||
```js
|
||||
// Result will be the updated Header Global.
|
||||
|
||||
@@ -1,127 +1,15 @@
|
||||
---
|
||||
title: 2.0 to 3.0 Migration Guide
|
||||
label: 2.0 to 3.0 Migration Guide
|
||||
order: 10
|
||||
desc: Upgrade guide for Payload 2.x projects migrating to 3.0.
|
||||
keywords: local api, config, configuration, documentation, Content Management System, cms, headless, javascript, node, react
|
||||
---
|
||||
# 🚧 **DRAFT:** 3.0 Migration Guide / Breaking Changes
|
||||
|
||||
# Payload 2.0 to 3.0 Migration Guide
|
||||
|
||||
Payload 3.0 completely replatforms the Admin Panel from a React Router single-page application onto the Next.js App Router with full support for React Server Components. This change completely separates Payload "core" from its rendering and HTTP layers, making it truly Node-safe and portable.
|
||||
|
||||
If you are upgrading from a _previous beta_, please see the [Upgrade From Previous Beta](#upgrade-from-previous-beta) section.
|
||||
> [!IMPORTANT]
|
||||
> This document will continue to be updated and cleaned up until the 3.0 release.
|
||||
|
||||
## What has changed?
|
||||
|
||||
The core logic and principles of Payload remain the same from 2.0 to 3.0, with the majority of changes affecting specifically the HTTP layer and the Admin Panel, which is now built upon Next.js. With this change, your entire application can be served within a single repo, with Payload endpoints are now opened within your own Next.js application, directly alongside your frontend. Payload is still headless, you will still be able to leverage it completely headlessly just as you do now with Sveltekit, etc. All Payload APIs remain exactly the same (with a few new features), and the Payload Config is generally the same, with the breaking changes detailed below.
|
||||
The core logic and principles of Payload remain the same from 2.0 to 3.0, with the majority of changes affecting specifically the HTTP layer and the Admin Panel, which is now built upon Next.js. With this change, your entire application can be served within a single repo. Payload endpoints are now opened directly within your own Next.js application, right alongside your frontend. All Payload APIs remain exactly the same (with a few new features), and the Payload Config is generally the same, with the breaking changes detailed below.
|
||||
|
||||
### Table of Contents
|
||||
Payload is still headless, you will still be able to leverage it completely headlessly just as you do now with Sveltekit, etc. In fact, Payload itself is now _truly_ portable because it is fully Node.js compatible and completely separate from the HTTP layer. The entire Payload suite of packages has been modularized to make this possible.
|
||||
|
||||
All breaking changes are listed below. If you encounter changes that are not explicitly listed here, please consider contributing to this documentation by submitting a PR.
|
||||
|
||||
- [Installation](#installation)
|
||||
- [Breaking Changes](#breaking-changes)
|
||||
- [Custom Components](#custom-components)
|
||||
- [Endpoints](#endpoints)
|
||||
- [React Hooks](#react-hooks)
|
||||
- [Types](#types)
|
||||
- [Email Adapters](#email-adapters)
|
||||
- [Plugins](#plugins)
|
||||
- [Upgrade From Previous Beta](#upgrade-from-previous-beta)
|
||||
|
||||
## Installation
|
||||
|
||||
Payload 3.0 requires a set of auto-generated files that you will need to bring into your existing project. The easiest way of acquiring these is by initializing a new project via `create-payload-app`, then replace the provided Payload Config with your own.
|
||||
|
||||
```bash
|
||||
npx create-payload-app
|
||||
```
|
||||
|
||||
For more details, see the [Documentation](https://payloadcms.com/docs/getting-started/installation).
|
||||
|
||||
1. **Install new dependencies of payload, next.js and react**:
|
||||
|
||||
Refer to the package.json file made in the create-payload-app, including peerDependencies, devDependencies, and dependencies. The core package and plugins require all versions to be synced. Previously, on 2.x it was possible to be running the latest version of payload 2.x with an older version of db-mongodb for example. This is no longer the case.
|
||||
|
||||
```bash
|
||||
pnpm i next react react-dom payload @payloadcms/ui @payloadcms/next
|
||||
```
|
||||
|
||||
Also install the other @payloadcms packages specific to the plugins and adapters you are using. Depending on your project, these may include:
|
||||
- @payloadcms/db-mongodb
|
||||
- @payloadcms/db-postgres
|
||||
- @payloadcms/richtext-slate
|
||||
- @payloadcms/richtext-lexical
|
||||
- @payloadcms/plugin-form-builder
|
||||
- @payloadcms/plugin-nested-docs
|
||||
- @payloadcms/plugin-redirects
|
||||
- @payloadcms/plugin-relationship
|
||||
- @payloadcms/plugin-search
|
||||
- @payloadcms/plugin-sentry
|
||||
- @payloadcms/plugin-seo
|
||||
- @payloadcms/plugin-stripe
|
||||
- @payloadcms/plugin-cloud-storage - Read [More](#@payloadcms/plugin-cloud-storage).
|
||||
|
||||
1. Uninstall deprecated packages:
|
||||
|
||||
```bash
|
||||
pnpm remove express nodemon @payloadcms/bundler-webpack @payloadcms/bundler-vite
|
||||
```
|
||||
|
||||
1. Database Adapter Migrations
|
||||
|
||||
_If you have existing data_ and are using the MongoDB or Postgres adapters, you will need to run the database migrations to ensure your database schema is up-to-date.
|
||||
|
||||
- [postgres](https://github.com/payloadcms/payload/releases/tag/v3.0.0-beta.39)
|
||||
- [mongodb](https://github.com/payloadcms/payload/releases/tag/v3.0.0-beta.131)
|
||||
|
||||
2. For Payload Cloud users, the plugin has changed.
|
||||
|
||||
Uninstall the old package:
|
||||
|
||||
```bash
|
||||
pnpm remove @payloadcms/plugin-cloud
|
||||
```
|
||||
|
||||
Install the new package:
|
||||
|
||||
```bash
|
||||
pnpm i @payloadcms/payload-cloud
|
||||
```
|
||||
|
||||
```diff
|
||||
// payload.config.ts
|
||||
- import { payloadCloud } from '@payloadcms/plugin-cloud'
|
||||
+ import { payloadCloudPlugin } from '@payloadcms/payload-cloud'
|
||||
|
||||
buildConfig({
|
||||
// ...
|
||||
plugins: [
|
||||
- payloadCloud()
|
||||
+ payloadCloudPlugin()
|
||||
]
|
||||
})
|
||||
```
|
||||
|
||||
3. **Optional** sharp dependency
|
||||
|
||||
If you have upload enabled collections that use `formatOptions`, `imageSizes`, or `resizeOptions`—payload expects to have `sharp` installed. In 2.0 this was a dependency was installed for you. Now it is only installed if needed. If you have any of these options set, you will need to install `sharp` and add it to your payload.config.ts:
|
||||
|
||||
```bash
|
||||
pnpm i sharp
|
||||
```
|
||||
|
||||
```diff
|
||||
// payload.config.ts
|
||||
import sharp from 'sharp'
|
||||
buildConfig({
|
||||
// ...
|
||||
+ sharp,
|
||||
})
|
||||
```
|
||||
|
||||
## Breaking Changes
|
||||
## To migrate from Payload 2.0 to 3.0:
|
||||
|
||||
1. Delete the `admin.bundler` property from your Payload Config. Payload no longer bundles the Admin Panel. Instead, we rely directly on Next.js for bundling.
|
||||
|
||||
@@ -151,23 +39,6 @@ For more details, see the [Documentation](https://payloadcms.com/docs/getting-st
|
||||
})
|
||||
```
|
||||
|
||||
1. Environment variables prefixed with `PAYLOAD_PUBLIC` will no longer be available on the client. In order to access them on the client, those will now have to be prefixed with `NEXT_PUBLIC` instead.
|
||||
|
||||
```diff
|
||||
'use client'
|
||||
- const var = process.env.PAYLOAD_PUBLIC_MY_ENV_VAR
|
||||
+ const var = process.env.NEXT_PUBLIC_MY_ENV_VAR
|
||||
```
|
||||
|
||||
For more details, see the [Documentation](https://payloadcms.com/docs/configuration/environment-vars).
|
||||
|
||||
1. The `req` object used to extend the [Express Request](https://expressjs.com/), but now extends the [Web Request](https://developer.mozilla.org/en-US/docs/Web/API/Request). You may need to update your code accordingly to reflect this change. For example:
|
||||
|
||||
```diff
|
||||
- req.headers['content-type']
|
||||
+ req.headers.get('content-type')
|
||||
```
|
||||
|
||||
1. The `admin.css` and `admin.scss` properties in the Payload Config have been removed.
|
||||
|
||||
```diff
|
||||
@@ -262,160 +133,6 @@ For more details, see the [Documentation](https://payloadcms.com/docs/getting-st
|
||||
}
|
||||
```
|
||||
|
||||
1. Fields with `unique: true` now automatically be appended with “- Copy” through the new `admin.beforeDuplicate` field hooks (see previous bullet).
|
||||
|
||||
1. The `upload.staticDir` property must now be an absolute path. Before it would attempt to use the location of the Payload Config and merge the relative path set for staticDir.
|
||||
|
||||
```diff
|
||||
// collections/Media.ts
|
||||
import type { CollectionConfig } from 'payload'
|
||||
import path from 'path'
|
||||
+ import { fileURLToPath } from 'url'
|
||||
|
||||
+ const filename = fileURLToPath(import.meta.url)
|
||||
+ const dirname = path.dirname(filename)
|
||||
|
||||
export const MediaCollection: CollectionConfig = {
|
||||
slug: 'media',
|
||||
upload: {
|
||||
- staticDir: path.resolve(__dirname, './uploads'),
|
||||
+ staticDir: path.resolve(dirname, '../uploads'),
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
1. The `upload.staticURL` property has been removed. If you were using this format URLs when using an external provider, you can leverage the `generateFileURL` functions in order to do the same.
|
||||
|
||||
```diff
|
||||
// collections/Media.ts
|
||||
import type { CollectionConfig } from 'payload'
|
||||
|
||||
export const MediaCollection: CollectionConfig = {
|
||||
slug: 'media',
|
||||
upload: {
|
||||
- staticURL: '',
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
1. The `admin.favicon` property is now `admin.icons` and the types have changed:
|
||||
|
||||
```diff
|
||||
// payload.config.ts
|
||||
import { buildConfig } from 'payload'
|
||||
|
||||
const config = buildConfig({
|
||||
// ...
|
||||
admin: {
|
||||
- favicon: 'path-to-favicon.svg',
|
||||
+ icons: [{
|
||||
+ path: 'path-to-favicon.svg',
|
||||
+ sizes: '32x32'
|
||||
+ }]
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
For more details, see the [Documentation](https://payloadcms.com/docs/admin/metadata#icons).
|
||||
|
||||
1. The `admin.meta.ogImage` property has been replaced by `admin.meta.openGraph.images`:
|
||||
|
||||
```diff
|
||||
// payload.config.ts
|
||||
import { buildConfig } from 'payload'
|
||||
|
||||
const config = buildConfig({
|
||||
// ...
|
||||
admin: {
|
||||
meta: {
|
||||
- ogImage: '',
|
||||
+ openGraph: {
|
||||
+ images: []
|
||||
+ }
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
For more details, see the [Documentation](https://payloadcms.com/docs/admin/metadata#open-graph).
|
||||
|
||||
1. The args of the `admin.livePreview.url` function have changed. It no longer receives `documentInfo` as an arg, and instead, now has `collectionConfig` and `globalConfig`.
|
||||
|
||||
```diff
|
||||
// payload.config.ts
|
||||
import { buildConfig } from 'payload'
|
||||
|
||||
export default buildConfig({
|
||||
// ...
|
||||
admin: {
|
||||
// ...
|
||||
livePreview: ({
|
||||
- documentInfo,
|
||||
+ collectionConfig,
|
||||
+ globalConfig
|
||||
}) => ''
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
1. The `admin.logoutRoute` and `admin.inactivityRoute` properties have been consolidated into a single `admin.routes` property. To migrate, simply move those two keys as follows:
|
||||
|
||||
```diff
|
||||
// payload.config.ts
|
||||
import { buildConfig } from 'payload'
|
||||
|
||||
const config = buildConfig({
|
||||
// ...
|
||||
admin: {
|
||||
- logoutRoute: '/custom-logout',
|
||||
+ inactivityRoute: '/custom-inactivity'
|
||||
+ routes: {
|
||||
+ logout: '/custom-logout',
|
||||
+ inactivity: '/custom-inactivity'
|
||||
+ }
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
1. The `custom` property in the Payload Config, i.e. Collections, Globals, and Fields is now **server only** and will **not** appear in the client-side bundle. To add custom properties to the client bundle, use the new `admin.custom` property, which will be available on _both_ the server and the client.
|
||||
|
||||
```diff
|
||||
// payload.config.ts
|
||||
import { buildConfig } from 'payload'
|
||||
|
||||
export default buildConfig({
|
||||
custom: {
|
||||
someProperty: 'My Server Prop' // Now server only!
|
||||
},
|
||||
admin: {
|
||||
+ custom: {
|
||||
+ name: 'My Client Prop' // Available in server AND client
|
||||
+ }
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
1. `hooks.afterError` is now an array of functions instead of a single function. The args have also been expanded. Read [More](https://payloadcms.com/docs/hooks/overview#root-hooks).
|
||||
|
||||
```diff
|
||||
// payload.config.ts
|
||||
import { buildConfig } from 'payload'
|
||||
|
||||
export default buildConfig({
|
||||
hooks: {
|
||||
- afterError: async ({ error }) => {
|
||||
+ afterError: [
|
||||
+ async ({ error, req, res }) => {
|
||||
+ // ...
|
||||
+ }
|
||||
+ ]
|
||||
}
|
||||
})
|
||||
```
|
||||
1. The `./src/public` directory is now located directly at root level `./public` [see nextJS docs for details](https://nextjs.org/docs/pages/building-your-application/optimizing/static-assets)
|
||||
|
||||
## Custom Components
|
||||
|
||||
1. All Payload React components have been moved from the `payload` package to `@payloadcms/ui`. If you were previously importing components into your app from the `payload` package, for example to create Custom Components, you will need to change your import paths:
|
||||
|
||||
```diff
|
||||
@@ -424,6 +141,13 @@ For more details, see the [Documentation](https://payloadcms.com/docs/getting-st
|
||||
```
|
||||
*Note: for brevity, not _all_ modules are listed here*
|
||||
|
||||
1. The `BlockField` and related types have been renamed to `BlocksField` for semantic accuracy.
|
||||
|
||||
```diff
|
||||
- import type { BlockField, BlockFieldProps } from 'payload'
|
||||
+ import type { BlocksField, BlocksFieldProps } from 'payload'
|
||||
```
|
||||
|
||||
1. All Custom Components are now defined as _file paths_ instead of direct imports. If you are using Custom Components in your Payload Config, remove the imported module and point to the file's path instead:
|
||||
|
||||
```diff
|
||||
@@ -443,7 +167,7 @@ For more details, see the [Documentation](https://payloadcms.com/docs/getting-st
|
||||
})
|
||||
```
|
||||
|
||||
For more details, see the [Documentation](https://payloadcms.com/docs/admin/components#component-paths).
|
||||
For more details, see the [Documentation](https://payloadcms.com/docs/beta/admin/components#component-paths).
|
||||
|
||||
1. All Custom Components are now server-rendered by default, and therefore, cannot use state or hooks directly. If you’re using Custom Components in your app that requires state or hooks, add the `'use client'` directive at the top of the file.
|
||||
|
||||
@@ -463,186 +187,102 @@ For more details, see the [Documentation](https://payloadcms.com/docs/getting-st
|
||||
}
|
||||
```
|
||||
|
||||
For more details, see the [Documentation](https://payloadcms.com/docs/admin/components#client-components).
|
||||
For more details, see the [Documentation](https://payloadcms.com/docs/beta/admin/components#client-components).
|
||||
|
||||
1. The `admin.description` property within Collection, Globals, and Fields no longer accepts a React Component. Instead, you must define it as a Custom Component.
|
||||
|
||||
1. For Collections, use the `admin.components.edit.Description` key:
|
||||
1. The `custom` property in the Payload Config, i.e. Collections, Globals, and Fields is now **server only** and will **not** appear in the client-side bundle. To add custom properties to the client bundle, use the new `admin.custom` property, which will be available on _both_ the server and the client.
|
||||
|
||||
```diff
|
||||
// collections/Posts.ts
|
||||
import type { CollectionConfig } from 'payload'
|
||||
- import { MyCustomDescription } from '../components/MyCustomDescription'
|
||||
|
||||
export const PostsCollection: CollectionConfig = {
|
||||
slug: 'posts',
|
||||
admin: {
|
||||
- description: MyCustomDescription,
|
||||
+ components: {
|
||||
+ edit: {
|
||||
+ Description: 'path/to/MyCustomDescription'
|
||||
+ }
|
||||
+ }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
2. For Globals, use the `admin.components.elements.Description` key:
|
||||
|
||||
```diff
|
||||
// globals/Site.ts
|
||||
import type { GlobalConfig } from 'payload'
|
||||
- import { MyCustomDescription } from '../components/MyCustomDescription'
|
||||
|
||||
export const SiteGlobal: GlobalConfig = {
|
||||
slug: 'site',
|
||||
admin: {
|
||||
- description: MyCustomDescription,
|
||||
+ components: {
|
||||
+ elements: {
|
||||
+ Description: 'path/to/MyCustomDescription'
|
||||
+ }
|
||||
+ }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
3. For Fields, use the `admin.components.Description` key:
|
||||
|
||||
```diff
|
||||
// fields/MyField.ts
|
||||
import type { FieldConfig } from 'payload'
|
||||
- import { MyCustomDescription } from '../components/MyCustomDescription'
|
||||
|
||||
export const MyField: FieldConfig = {
|
||||
type: 'text',
|
||||
admin: {
|
||||
- description: MyCustomDescription,
|
||||
+ components: {
|
||||
+ Description: 'path/to/MyCustomDescription'
|
||||
+ }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
1. Array Field row labels and Collapsible Field label now _only_ accepts a React Component, and no longer accepts a plain string or record:
|
||||
|
||||
```diff
|
||||
// file: Collection.tsx
|
||||
import type { CollectionConfig } from 'payload'
|
||||
- import { MyCustomRowLabel } from './components/MyCustomRowLabel.tsx'
|
||||
|
||||
export const MyCollection: CollectionConfig = {
|
||||
slug: 'my-collection',
|
||||
fields: [
|
||||
{
|
||||
name: 'my-array',
|
||||
type: 'array',
|
||||
admin: {
|
||||
components: {
|
||||
- RowLabel: 'My Array Row Label,
|
||||
+ RowLabel: './components/RowLabel.ts'
|
||||
}
|
||||
},
|
||||
fields: [...]
|
||||
},
|
||||
{
|
||||
name: 'my-collapsible',
|
||||
type: 'collapsible',
|
||||
admin: {
|
||||
components: {
|
||||
- Label: 'My Collapsible Label',
|
||||
+ Label: './components/RowLabel.ts'
|
||||
}
|
||||
},
|
||||
fields: [...]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
1. All default view keys are now camelcase:
|
||||
|
||||
For example, for Root Views:
|
||||
|
||||
```diff
|
||||
// file: payload.config.ts
|
||||
|
||||
// payload.config.ts
|
||||
import { buildConfig } from 'payload'
|
||||
|
||||
export default buildConfig({
|
||||
admin: {
|
||||
views: {
|
||||
- Account: ...
|
||||
+ account: ...
|
||||
}
|
||||
custom: {
|
||||
someProperty: 'My Server Prop' // Now server only!
|
||||
},
|
||||
admin: {
|
||||
+ custom: {
|
||||
+ name: 'My Client Prop' // Available in server AND client
|
||||
+ }
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
Or Document Views:
|
||||
1. The `useTitle` hook has been consolidated into the `useDocumentInfo` hook. Instead, you can get title directly from document info context:
|
||||
|
||||
```diff
|
||||
// file: Collection.tsx
|
||||
'use client'
|
||||
- import { useTitle } from 'payload'
|
||||
+ import { useDocumentInfo } from '@payloadcms/ui'
|
||||
|
||||
import type { CollectionConfig } from 'payload'
|
||||
export const MyComponent = () => {
|
||||
- const title = useTitle()
|
||||
+ const { title } = useDocumentInfo()
|
||||
|
||||
export const MyCollection: CollectionConfig = {
|
||||
slug: 'my-collection',
|
||||
admin: {
|
||||
views: {
|
||||
- Edit: {
|
||||
- Default: ...
|
||||
- }
|
||||
+ edit: {
|
||||
+ default: ...
|
||||
+ }
|
||||
}
|
||||
}
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
1. Custom Views within the config no longer accept React Components directly, instead, you must use their `Component` property:
|
||||
1. The `Fields` type was renamed to `FormState` for improved semantics. If you were previously importing this type in your own application, simply change the import name:
|
||||
|
||||
```diff
|
||||
// file: Collection.tsx
|
||||
import type { CollectionConfig } from 'payload'
|
||||
- import { MyCustomView } from './components/MyCustomView.tsx'
|
||||
- import type { Fields } from 'payload'
|
||||
+ import type { FormState } from 'payload'
|
||||
```
|
||||
|
||||
export const MyCollection: CollectionConfig = {
|
||||
slug: 'my-collection',
|
||||
admin: {
|
||||
views: {
|
||||
- Edit: MyCustomView
|
||||
+ edit: {
|
||||
+ Component: './components/MyCustomView.tsx'
|
||||
+ }
|
||||
}
|
||||
}
|
||||
1. The `useDocumentInfo` hook no longer returns `collection` or `global`. Instead, various properties of the config are passed, like `collectionSlug` and `globalSlug`. You can use these to access a client-side config, if needed, through the `useConfig` hook (see next bullet).
|
||||
|
||||
```diff
|
||||
'use client'
|
||||
import { useDocumentInfo } from '@payloadcms/ui'
|
||||
|
||||
export const MyComponent = () => {
|
||||
const {
|
||||
- collection,
|
||||
- global,
|
||||
+ collectionSlug,
|
||||
+ globalSlug
|
||||
} = useDocumentInfo()
|
||||
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
This also means that Custom Root Views are no longer defined on the `edit` key. Instead, use the new `views.root` key:
|
||||
1. The `useConfig` hook now returns a `ClientConfig` and not a `SanitizedConfig`. This is because the config itself is not serializable and so it is not able to be thread through to the client. This means that all non-serializable props have been omitted from the Client Config, such as `db`, `bundler`, etc.
|
||||
|
||||
```diff
|
||||
// file: Collection.tsx
|
||||
import type { CollectionConfig } from 'payload'
|
||||
- import { MyCustomRootView } from './components/MyCustomRootView.tsx'
|
||||
'use client'
|
||||
- import { useConfig } from 'payload'
|
||||
+ import { useConfig } from '@payloadcms/ui'
|
||||
|
||||
export const MyCollection: CollectionConfig = {
|
||||
slug: 'my-collection',
|
||||
admin: {
|
||||
views: {
|
||||
- Edit: MyCustomRootView
|
||||
edit: {
|
||||
+ root: {
|
||||
+ Component: './components/MyCustomRootView.tsx'
|
||||
+ }
|
||||
}
|
||||
}
|
||||
}
|
||||
export const MyComponent = () => {
|
||||
- const config = useConfig() // used to be a 'SanitizedConfig'
|
||||
+ const { config } = useConfig() // now is a 'ClientConfig'
|
||||
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
For more details, see the [Documentation](https://payloadcms.com/docs/beta/admin/components#accessing-the-payload-config).
|
||||
|
||||
1. The args of the `admin.livePreview.url` function have changed. It no longer receives `documentInfo` as an arg, and instead, now has `collectionConfig` and `globalConfig`.
|
||||
|
||||
```diff
|
||||
// payload.config.ts
|
||||
import { buildConfig } from 'payload'
|
||||
|
||||
export default buildConfig({
|
||||
// ...
|
||||
admin: {
|
||||
// ...
|
||||
livePreview: ({
|
||||
- documentInfo,
|
||||
+ collectionConfig,
|
||||
+ globalConfig
|
||||
}) => ''
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
1. The `href` and `isActive` functions on View Tabs no longer includes the `match` or `location` arguments. This is is a property specific to React Router, not Next.js. If you need to do URL matching similar to this, use a custom tab that fires of some hooks, i.e. `usePathname()` and run it through your own utility functions:
|
||||
|
||||
```diff
|
||||
@@ -663,8 +303,8 @@ For more details, see the [Documentation](https://payloadcms.com/docs/getting-st
|
||||
+ edit: {
|
||||
+ tab: {
|
||||
+ isActive: ({ href }) => true,
|
||||
+ href: ({ href }) => '' // or use a Custom Component (see below)
|
||||
+ // Component: './path/to/CustomComponent.tsx'
|
||||
+ href: ({ href }) => ''
|
||||
+ Component: './path/to/CustomComponent.tsx', // Or use a Custom Component
|
||||
+ }
|
||||
+ },
|
||||
},
|
||||
@@ -684,12 +324,12 @@ For more details, see the [Documentation](https://payloadcms.com/docs/getting-st
|
||||
admin: {
|
||||
components: {
|
||||
- views: {
|
||||
- Edit: {
|
||||
- edit: {
|
||||
- Tab: {
|
||||
- pillLabel: 'Hello, world!',
|
||||
- },
|
||||
- },
|
||||
+ edit: {
|
||||
+ Edit: {
|
||||
+ tab: {
|
||||
+ Pill: './path/to/CustomPill.tsx',
|
||||
+ }
|
||||
@@ -700,6 +340,106 @@ For more details, see the [Documentation](https://payloadcms.com/docs/getting-st
|
||||
}
|
||||
```
|
||||
|
||||
1. Unique fields will now automatically be appended with “- Copy” through the new `admin.beforeDuplicate` field hooks (detailed aboved).
|
||||
|
||||
1. The `useCollapsible` hook has had slight changes to its property names. `collapsed` is now `isCollapsed` and `withinCollapsible` is now `isWithinCollapsible`.
|
||||
|
||||
```diff
|
||||
'use client'
|
||||
import { useCollapsible } from '@payloadcms/ui'
|
||||
|
||||
export const MyComponent = () => {
|
||||
- const { collapsed, withinCollapsible } = useCollapsible()
|
||||
+ const { isCollapsed, isWithinCollapsible } = useCollapsible()
|
||||
}
|
||||
```
|
||||
|
||||
1. The `admin.favicon` property is now `admin.icons` and the types have changed:
|
||||
|
||||
```diff
|
||||
// payload.config.ts
|
||||
import { buildConfig } from 'payload'
|
||||
|
||||
const config = buildConfig({
|
||||
// ...
|
||||
admin: {
|
||||
- favicon: 'path-to-favicon.svg',
|
||||
+ icons: [{
|
||||
+ path: 'path-to-favicon.svg',
|
||||
+ sizes: '32x32'
|
||||
+ }]
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
For more details, see the [Documentation](https://payloadcms.com/docs/beta/admin/metadata#icons).
|
||||
|
||||
1. The `admin.meta.ogImage` property has been replaced by `admin.meta.openGraph.images`:
|
||||
|
||||
```diff
|
||||
// payload.config.ts
|
||||
import { buildConfig } from 'payload'
|
||||
|
||||
const config = buildConfig({
|
||||
// ...
|
||||
admin: {
|
||||
meta: {
|
||||
- ogImage: '',
|
||||
+ openGraph: {
|
||||
+ images: []
|
||||
+ }
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
For more details, see the [Documentation](https://payloadcms.com/docs/beta/admin/metadata#open-graph).
|
||||
|
||||
1. The `admin.logoutRoute` and `admin.inactivityRoute` properties have been consolidated into a single `admin.routes` property. To migrate, simply move those two keys as follows:
|
||||
|
||||
```diff
|
||||
// payload.config.ts
|
||||
import { buildConfig } from 'payload'
|
||||
|
||||
const config = buildConfig({
|
||||
// ...
|
||||
admin: {
|
||||
- logoutRoute: '/custom-logout',
|
||||
+ inactivityRoute: '/custom-inactivity'
|
||||
+ routes: {
|
||||
+ logout: '/custom-logout',
|
||||
+ inactivity: '/custom-inactivity'
|
||||
+ }
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
1. Environment variables prefixed with `PAYLOAD_PUBLIC` will no longer be available on the client. In order to access them on the client, those will now have to be prefixed with `NEXT_PUBLIC` instead.
|
||||
|
||||
```diff
|
||||
'use client'
|
||||
- const var = process.env.PAYLOAD_PUBLIC_MY_ENV_VAR
|
||||
+ const var = process.env.NEXT_PUBLIC_MY_ENV_VAR
|
||||
```
|
||||
|
||||
For more details, see the [Documentation](https://payloadcms.com/docs/beta/configuration/environment-vars).
|
||||
|
||||
1. The `useTranslation` hook no longer takes any options, any translations using shorthand accessors will need to use the entire `group:key`
|
||||
|
||||
```diff
|
||||
'use client'
|
||||
- import { useTranslation } from 'payload'
|
||||
+ import { useTranslation } from '@payloadcms/ui'
|
||||
|
||||
export const MyComponent = () => {
|
||||
- const { i18n, t } = useTranslation('general')
|
||||
+ const { i18n, t } = useTranslation()
|
||||
|
||||
- return <p>{t('cancel')}</p>
|
||||
+ return <p>{t('general:cancel')}</p>
|
||||
}
|
||||
```
|
||||
|
||||
1. `react-i18n` was removed, the `Trans` component from `react-i18n` has been replaced with a Payload-provided solution:
|
||||
|
||||
```diff
|
||||
@@ -727,9 +467,21 @@ For more details, see the [Documentation](https://payloadcms.com/docs/getting-st
|
||||
}
|
||||
```
|
||||
|
||||
## Endpoints
|
||||
1. `root` endpoints no longer exist on the config. Instead, you must add them to the `/api` folder within your own Payload application.
|
||||
|
||||
1. All endpoint handlers have changed. The args no longer include `res`, and `next`, and the return type now expects a valid HTTP [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response) instead of `res.json`, `res.send`, etc.:
|
||||
```diff
|
||||
// payload.config.ts
|
||||
import { buildConfig } from 'payload'
|
||||
|
||||
const config = buildConfig({
|
||||
// ...
|
||||
- endpoints: [...]
|
||||
})
|
||||
```
|
||||
|
||||
For more details, see the [Next.js Documentation](https://nextjs.org/docs/app/api-reference/file-conventions/route).
|
||||
|
||||
1. All other endpoint handlers have changed. The args no longer include `res`, and `next`, and the return type now expects a valid HTTP [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response) instead of `res.json`, `res.send`, etc.:
|
||||
|
||||
```diff
|
||||
// collections/Posts.ts
|
||||
@@ -804,102 +556,47 @@ For more details, see the [Documentation](https://payloadcms.com/docs/getting-st
|
||||
}
|
||||
```
|
||||
|
||||
## React Hooks
|
||||
|
||||
1. The `useTitle` hook has been consolidated into the `useDocumentInfo` hook. Instead, you can get title directly from document info context:
|
||||
1. The `req` object used to extend the [Express Request](https://expressjs.com/), but now extends the [Web Request](https://developer.mozilla.org/en-US/docs/Web/API/Request). You may need to update your code accordingly to reflect this change. For example:
|
||||
|
||||
```diff
|
||||
'use client'
|
||||
- import { useTitle } from 'payload'
|
||||
+ import { useDocumentInfo } from '@payloadcms/ui'
|
||||
- req.headers['content-type']
|
||||
+ req.headers.get('content-type')
|
||||
```
|
||||
|
||||
export const MyComponent = () => {
|
||||
- const title = useTitle()
|
||||
+ const { title } = useDocumentInfo()
|
||||
1. `staticDir` must now be an absolute path. Before it would attempt to use the location of the Payload Config and merge the relative path set for staticDir.
|
||||
|
||||
// ...
|
||||
```diff
|
||||
// collections/Media.ts
|
||||
import type { CollectionConfig } from 'payload'
|
||||
import path from 'path'
|
||||
+ import { fileURLToPath } from 'url'
|
||||
|
||||
+ const filename = fileURLToPath(import.meta.url)
|
||||
+ const dirname = path.dirname(filename)
|
||||
|
||||
export const MediaCollection: CollectionConfig = {
|
||||
slug: 'media',
|
||||
upload: {
|
||||
- staticDir: path.resolve(__dirname, './uploads'),
|
||||
+ staticDir: path.resolve(dirname, '../uploads'),
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
1. The `useDocumentInfo` hook no longer returns `collection` or `global`. Instead, various properties of the config are passed, like `collectionSlug` and `globalSlug`. You can use these to access a client-side config, if needed, through the `useConfig` hook (see next bullet).
|
||||
1. `staticURL` has been removed. If you were using this format URLs when using an external provider, you can leverage the `generateFileURL` functions in order to do the same.
|
||||
|
||||
```diff
|
||||
'use client'
|
||||
import { useDocumentInfo } from '@payloadcms/ui'
|
||||
// collections/Media.ts
|
||||
import type { CollectionConfig } from 'payload'
|
||||
|
||||
export const MyComponent = () => {
|
||||
const {
|
||||
- collection,
|
||||
- global,
|
||||
+ collectionSlug,
|
||||
+ globalSlug
|
||||
} = useDocumentInfo()
|
||||
|
||||
// ...
|
||||
export const MediaCollection: CollectionConfig = {
|
||||
slug: 'media',
|
||||
upload: {
|
||||
- staticURL: '',
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
1. The `useConfig` hook now returns a `ClientConfig` and not a `SanitizedConfig`. This is because the config itself is not serializable and so it is not able to be thread through to the client. This means that all non-serializable props have been omitted from the Client Config, such as `db`, `bundler`, etc.
|
||||
|
||||
```diff
|
||||
'use client'
|
||||
- import { useConfig } from 'payload'
|
||||
+ import { useConfig } from '@payloadcms/ui'
|
||||
|
||||
export const MyComponent = () => {
|
||||
- const config = useConfig() // used to be a 'SanitizedConfig'
|
||||
+ const { config } = useConfig() // now is a 'ClientConfig'
|
||||
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
For more details, see the [Documentation](https://payloadcms.com/docs/admin/components#accessing-the-payload-config).
|
||||
|
||||
1. The `useCollapsible` hook has had slight changes to its property names. `collapsed` is now `isCollapsed` and `withinCollapsible` is now `isWithinCollapsible`.
|
||||
|
||||
```diff
|
||||
'use client'
|
||||
import { useCollapsible } from '@payloadcms/ui'
|
||||
|
||||
export const MyComponent = () => {
|
||||
- const { collapsed, withinCollapsible } = useCollapsible()
|
||||
+ const { isCollapsed, isWithinCollapsible } = useCollapsible()
|
||||
}
|
||||
```
|
||||
|
||||
1. The `useTranslation` hook no longer takes any options, any translations using shorthand accessors will need to use the entire `group:key`
|
||||
|
||||
```diff
|
||||
'use client'
|
||||
- import { useTranslation } from 'payload'
|
||||
+ import { useTranslation } from '@payloadcms/ui'
|
||||
|
||||
export const MyComponent = () => {
|
||||
- const { i18n, t } = useTranslation('general')
|
||||
+ const { i18n, t } = useTranslation()
|
||||
|
||||
- return <p>{t('cancel')}</p>
|
||||
+ return <p>{t('general:cancel')}</p>
|
||||
}
|
||||
```
|
||||
|
||||
## Types
|
||||
|
||||
1. The `Fields` type was renamed to `FormState` for improved semantics. If you were previously importing this type in your own application, simply change the import name:
|
||||
|
||||
```diff
|
||||
- import type { Fields } from 'payload'
|
||||
+ import type { FormState } from 'payload'
|
||||
```
|
||||
|
||||
1. The `BlockField` and related types have been renamed to `BlocksField` for semantic accuracy.
|
||||
|
||||
```diff
|
||||
- import type { BlockField, BlockFieldProps } from 'payload'
|
||||
+ import type { BlocksField, BlocksFieldProps } from 'payload'
|
||||
```
|
||||
|
||||
## Email Adapters
|
||||
|
||||
Email functionality has been abstracted out into email adapters.
|
||||
@@ -964,7 +661,7 @@ export default buildConfig({
|
||||
|
||||
- Now only available if using custom server and using express or similar
|
||||
|
||||
## Plugins
|
||||
# Plugins
|
||||
|
||||
1. *All* plugins have been standardized to use _named exports_ (as opposed to default exports). Most also have a suffix of `Plugin` to make it clear what is being imported.
|
||||
|
||||
@@ -987,10 +684,10 @@ export default buildConfig({
|
||||
|
||||
| Service | Package |
|
||||
| -------------------- | ---------------------------------------------------------------------------- |
|
||||
| Vercel Blob | https://github.com/payloadcms/payload/tree/main/packages/storage-vercel-blob |
|
||||
| AWS S3 | https://github.com/payloadcms/payload/tree/main/packages/storage-s3 |
|
||||
| Azure | https://github.com/payloadcms/payload/tree/main/packages/storage-azure |
|
||||
| Google Cloud Storage | https://github.com/payloadcms/payload/tree/main/packages/storage-gcs |
|
||||
| Vercel Blob | https://github.com/payloadcms/payload/tree/beta/packages/storage-vercel-blob |
|
||||
| AWS S3 | https://github.com/payloadcms/payload/tree/beta/packages/storage-s3 |
|
||||
| Azure | https://github.com/payloadcms/payload/tree/beta/packages/storage-azure |
|
||||
| Google Cloud Storage | https://github.com/payloadcms/payload/tree/beta/packages/storage-gcs |
|
||||
|
||||
```tsx
|
||||
// ❌ Before (required peer dependencies depending on adapter)
|
||||
@@ -1001,7 +698,7 @@ import { s3Adapter } from '@payloadcms/plugin-cloud-storage/s3'
|
||||
plugins: [
|
||||
cloudStorage({
|
||||
collections: {
|
||||
media: {
|
||||
[mediaSlug]: {
|
||||
adapter: s3Adapter({
|
||||
bucket: process.env.S3_BUCKET,
|
||||
config: {
|
||||
@@ -1024,7 +721,7 @@ plugins: [
|
||||
plugins: [
|
||||
s3Storage({
|
||||
collections: {
|
||||
media: true,
|
||||
[mediaSlug]: true,
|
||||
},
|
||||
bucket: process.env.S3_BUCKET,
|
||||
config: {
|
||||
@@ -1102,10 +799,4 @@ plugins: [
|
||||
|
||||
## `@payloadcms/richtext-lexical`
|
||||
|
||||
If you have custom features for `@payloadcms/richtext-lexical` you will need to migrate your code to the new API. Read more about the new API in the [documentation](https://payloadcms.com/docs/lexical/building-custom-features).
|
||||
|
||||
## Upgrade from previous beta
|
||||
|
||||
Reference this [community-made site](https://payload-releases-filter.vercel.app/?version=3&from=152429656&to=188243150&sort=asc&breaking=on). Set your version, sort by oldest first, enable breaking changes only.
|
||||
|
||||
Then go through each one of the breaking changes and make the adjustments. You can optionally reference the [blank template](https://github.com/payloadcms/payload/tree/main/templates/blank) for how things should end up.
|
||||
// TODO: Add migration guide for `@payloadcms/richtext-lexical`
|
||||
|
||||
@@ -36,7 +36,7 @@ Forms can be as simple or complex as you need, from a basic contact form, to a m
|
||||
Install the plugin using any JavaScript package manager like [Yarn](https://yarnpkg.com), [NPM](https://npmjs.com), or [PNPM](https://pnpm.io):
|
||||
|
||||
```bash
|
||||
pnpm add @payloadcms/plugin-form-builder
|
||||
pnpm add @payloadcms/plugin-form-builder@beta
|
||||
```
|
||||
|
||||
## Basic Usage
|
||||
@@ -72,7 +72,7 @@ The `fields` property is an object of field types to allow your admin editors to
|
||||
|
||||
```ts
|
||||
// payload.config.ts
|
||||
formBuilderPlugin({
|
||||
formBuilder({
|
||||
// ...
|
||||
fields: {
|
||||
text: true,
|
||||
@@ -95,7 +95,7 @@ The `redirectRelationships` property is an array of collection slugs that, when
|
||||
|
||||
```ts
|
||||
// payload.config.ts
|
||||
formBuilderPlugin({
|
||||
formBuilder({
|
||||
// ...
|
||||
redirectRelationships: ['pages'],
|
||||
})
|
||||
@@ -103,11 +103,11 @@ formBuilderPlugin({
|
||||
|
||||
### `beforeEmail`
|
||||
|
||||
The `beforeEmail` property is a [beforeChange](https://payloadcms.com/docs/hooks/globals#beforechange) hook that is called just after emails are prepared, but before they are sent. This is a great place to inject your own HTML template to add custom styles.
|
||||
The `beforeEmail` property is a [beforeChange](<[beforeChange](https://payloadcms.com/docs/hooks/globals#beforechange)>) hook that is called just after emails are prepared, but before they are sent. This is a great place to inject your own HTML template to add custom styles.
|
||||
|
||||
```ts
|
||||
// payload.config.ts
|
||||
formBuilderPlugin({
|
||||
formBuilder({
|
||||
// ...
|
||||
beforeEmail: (emailsToSend, beforeChangeParams) => {
|
||||
// modify the emails in any way before they are sent
|
||||
@@ -142,7 +142,7 @@ Provide a fallback for the email address to send form submissions to. If the ema
|
||||
|
||||
```ts
|
||||
// payload.config.ts
|
||||
formBuilderPlugin({
|
||||
formBuilder({
|
||||
// ...
|
||||
defaultToEmail: 'test@example.com',
|
||||
})
|
||||
@@ -160,7 +160,7 @@ Good to know: The form collection is publicly available to read by default. The
|
||||
|
||||
```ts
|
||||
// payload.config.ts
|
||||
formBuilderPlugin({
|
||||
formBuilder({
|
||||
// ...
|
||||
formOverrides: {
|
||||
slug: 'contact-forms',
|
||||
@@ -196,7 +196,7 @@ Override anything on the `form-submissions` collection by sending a [Payload Col
|
||||
|
||||
```ts
|
||||
// payload.config.ts
|
||||
formBuilderPlugin({
|
||||
formBuilder({
|
||||
// ...
|
||||
formSubmissionOverrides: {
|
||||
slug: 'leads',
|
||||
@@ -215,7 +215,7 @@ formBuilderPlugin({
|
||||
|
||||
### `handlePayment`
|
||||
|
||||
The `handlePayment` property is a [beforeChange](https://payloadcms.com/docs/hooks/globals#beforechange) hook that is called upon form submission. You can integrate into any third-party payment processing API here to accept payment based on form input. You can use the `getPaymentTotal` function to calculate the total cost after all conditions have been applied. This is only applicable if the form has enabled the `payment` field.
|
||||
The `handlePayment` property is a [beforeChange](<[beforeChange](https://payloadcms.com/docs/hooks/globals#beforechange)>) hook that is called upon form submission. You can integrate into any third-party payment processing API here to accept payment based on form input. You can use the `getPaymentTotal` function to calculate the total cost after all conditions have been applied. This is only applicable if the form has enabled the `payment` field.
|
||||
|
||||
First import the utility function. This will execute all of the price conditions that you have set in your form's `payment` field and returns the total price.
|
||||
|
||||
@@ -228,7 +228,7 @@ Then in your plugin's config:
|
||||
|
||||
```ts
|
||||
// payload.config.ts
|
||||
formBuilderPlugin({
|
||||
formBuilder({
|
||||
// ...
|
||||
handlePayment: async ({ form, submissionData }) => {
|
||||
// first calculate the price
|
||||
@@ -396,7 +396,7 @@ Then merging it into your own custom field:
|
||||
|
||||
```ts
|
||||
// payload.config.ts
|
||||
formBuilderPlugin({
|
||||
formBuilder({
|
||||
// ...
|
||||
fields: {
|
||||
text: {
|
||||
|
||||
@@ -48,7 +48,7 @@ Install the plugin using any JavaScript package manager like [Yarn](https://yarn
|
||||
or [PNPM](https://pnpm.io):
|
||||
|
||||
```bash
|
||||
pnpm add @payloadcms/plugin-nested-docs
|
||||
pnpm add @payloadcms/plugin-nested-docs@beta
|
||||
```
|
||||
|
||||
## Basic Usage
|
||||
@@ -102,8 +102,8 @@ level and stores the following fields.
|
||||
|
||||
| Field | Description |
|
||||
| ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `label` | The label of the breadcrumb. This field is automatically set to either the `collection.admin.useAsTitle` (if defined) or is set to the `ID` of the document. You can also dynamically define the `label` by passing a function to the options property of [`generateLabel`](#generatelabel). |
|
||||
| `url` | The URL of the breadcrumb. By default, this field is undefined. You can manually define this field by passing a property called function to the plugin options property of [`generateURL`](#generateurl). |
|
||||
| `label` | The label of the breadcrumb. This field is automatically set to either the `collection.admin.useAsTitle` (if defined) or is set to the `ID` of the document. You can also dynamically define the `label` by passing a function to the options property of [`generateLabel`](#generateLabel). |
|
||||
| `url` | The URL of the breadcrumb. By default, this field is undefined. You can manually define this field by passing a property called function to the plugin options property of [`generateURL`](#generateURL). |
|
||||
|
||||
### Options
|
||||
|
||||
@@ -120,7 +120,7 @@ You can also pass a function to dynamically set the `label` of your breadcrumb.
|
||||
|
||||
```ts
|
||||
// payload.config.ts
|
||||
nestedDocsPlugin({
|
||||
nestedDocs({
|
||||
//...
|
||||
generateLabel: (_, doc) => doc.title, // NOTE: 'title' is a hypothetical field
|
||||
})
|
||||
@@ -141,7 +141,7 @@ that point, like `/about-us/company/our-team`.
|
||||
|
||||
```ts
|
||||
// payload.config.ts
|
||||
nestedDocsPlugin({
|
||||
nestedDocs({
|
||||
//...
|
||||
generateURL: (docs) => docs.reduce((url, doc) => `${url}/${doc.slug}`, ''), // NOTE: 'slug' is a hypothetical field
|
||||
})
|
||||
@@ -226,7 +226,7 @@ const examplePageConfig: CollectionConfig = {
|
||||
|
||||
This plugin supports localization by default. If the `localization` property is set in your Payload Config,
|
||||
the `breadcrumbs` field is automatically localized. For more details on how localization works in Payload, see
|
||||
the [Localization](https://payloadcms.com/docs/configuration/localization) docs.
|
||||
the [Localization](https://payloadcms.com/docs/localization/overview) docs.
|
||||
|
||||
## TypeScript
|
||||
|
||||
@@ -238,4 +238,10 @@ import { PluginConfig, GenerateURL, GenerateLabel } from '@payloadcms/plugin-nes
|
||||
|
||||
## Examples
|
||||
|
||||
The [Templates Directory](https://github.com/payloadcms/payload/tree/main/templates) also contains an official [Website Template](https://github.com/payloadcms/payload/tree/main/templates/website) and [E-commerce Template](https://github.com/payloadcms/payload/tree/main/templates/ecommerce), both of which use this plugin.
|
||||
The [Examples Directory](https://github.com/payloadcms/payload/tree/main/examples) contains an
|
||||
official [Nested Docs Plugin Example](https://github.com/payloadcms/payload/tree/main/examples/nested-docs) which
|
||||
demonstrates exactly how to configure this plugin in Payload and implement it on your front-end.
|
||||
The [Templates Directory](https://github.com/payloadcms/payload/tree/main/templates) also contains an
|
||||
official [Website Template](https://github.com/payloadcms/payload/tree/main/templates/website)
|
||||
and [E-commerce Template](https://github.com/payloadcms/payload/tree/main/templates/ecommerce), both of which use this
|
||||
plugin.
|
||||
|
||||
@@ -8,7 +8,7 @@ keywords: plugins, config, configuration, extensions, custom, documentation, Con
|
||||
|
||||
Payload Plugins take full advantage of the modularity of the [Payload Config](../configuration/overview), allowing developers developers to easily inject custom—sometimes complex—functionality into Payload apps from a very small touch-point. This is especially useful is sharing your work across multiple projects or with the greater Payload community.
|
||||
|
||||
There are many [Official Plugins](#official-plugins) available that solve for some of the most common uses cases, such as the [Form Builder Plugin](./form-builder) or [SEO Plugin](./seo). There are also [Community Plugins](#community-plugins) available, maintained entirely by contributing members. To extend Payload's functionality in some other way, you can easily [build your own plugin](./build-your-own).
|
||||
There are many [Official Plugins](#official-plugins) available that solve for some of the most common uses cases, such as the [Form Builder Plugin](./seo) or [SEO Plugin](./seo). There are also [Community Plugins](#community-plugins) available, maintained entirely by contributing members. To extend Payload's functionality in some other way, you can easily [build your own plugin](./build-your-own).
|
||||
|
||||
To configure Plugins, use the `plugins` property in your [Payload Config](../configuration/overview):
|
||||
|
||||
@@ -68,7 +68,7 @@ Plugins are changing every day, so be sure to check back often to see what new p
|
||||
|
||||
Community Plugins are those that are maintained entirely by outside contributors. They are a great way to share your work across the ecosystem for others to use. You can discover Community Plugins by browsing the `payload-plugin` topic on [GitHub](https://github.com/topics/payload-plugin).
|
||||
|
||||
Some plugins have become so widely used that they are adopted as an [Official Plugin](#official-plugins), such as the [Lexical Plugin](https://github.com/AlessioGr/payload-plugin-lexical). If you have a plugin that you think should be an Official Plugin, please feel free to start a new [Discussion](https://github.com/payloadcms/payload/discussions).
|
||||
Some plugins have become so widely used that they are adopted as an [Official Plugin](#official-plugin), such as the [Lexical Plugin](https://github.com/AlessioGr/payload-plugin-lexical). If you have a plugin that you think should be an Official Plugin, please feel free to start a new [Discussion](https://github.com/payloadcms/payload/discussions).
|
||||
|
||||
<Banner type="warning">
|
||||
For maintainers building plugins for others to use, please add the `payload-plugin` topic on [GitHub](https://github.com/topics/payload-plugin) to help others find it.
|
||||
|
||||
@@ -32,7 +32,7 @@ For example, if you have a page at `/about` and you want to change it to `/about
|
||||
Install the plugin using any JavaScript package manager like [Yarn](https://yarnpkg.com), [NPM](https://npmjs.com), or [PNPM](https://pnpm.io):
|
||||
|
||||
```bash
|
||||
pnpm add @payloadcms/plugin-redirects
|
||||
pnpm add @payloadcms/plugin-redirects@beta
|
||||
```
|
||||
|
||||
## Basic Usage
|
||||
@@ -102,4 +102,4 @@ import { PluginConfig } from '@payloadcms/plugin-redirects/types'
|
||||
|
||||
## Examples
|
||||
|
||||
The [Templates Directory](https://github.com/payloadcms/payload/tree/main/templates) also contains an official [Website Template](https://github.com/payloadcms/payload/tree/main/templates/website) and [E-commerce Template](https://github.com/payloadcms/payload/tree/main/templates/ecommerce), both of which use this plugin.
|
||||
The [Examples Directory](https://github.com/payloadcms/payload/tree/main/examples) contains an official [Redirects Plugin Example](https://github.com/payloadcms/payload/tree/main/examples/redirects) which demonstrates exactly how to configure this plugin in Payload and implement it on your front-end. The [Templates Directory](https://github.com/payloadcms/payload/tree/main/templates) also contains an official [Website Template](https://github.com/payloadcms/payload/tree/main/templates/website) and [E-commerce Template](https://github.com/payloadcms/payload/tree/main/templates/ecommere), both of which use this plugin.
|
||||
|
||||
@@ -39,7 +39,7 @@ This plugin is a great way to implement a fast, immersive search experience such
|
||||
Install the plugin using any JavaScript package manager like [Yarn](https://yarnpkg.com), [NPM](https://npmjs.com), or [PNPM](https://pnpm.io):
|
||||
|
||||
```bash
|
||||
pnpm add @payloadcms/plugin-search
|
||||
pnpm add @payloadcms/plugin-search@beta
|
||||
```
|
||||
|
||||
## Basic Usage
|
||||
@@ -134,7 +134,7 @@ Note that the `fields` property is a function that receives an object with a `de
|
||||
|
||||
#### `beforeSync`
|
||||
|
||||
Before creating or updating a search record, the `beforeSync` function runs. This is an [afterChange](https://payloadcms.com/docs/hooks/globals#afterchange) hook that allows you to modify the data or provide fallbacks before its search record is created or updated.
|
||||
Before creating or updating a search record, the `beforeSync` function runs. This is an [afterChange](<[afterChange](https://payloadcms.com/docs/hooks/globals#afterchange)>) hook that allows you to modify the data or provide fallbacks before its search record is created or updated.
|
||||
|
||||
```ts
|
||||
// payload.config.ts
|
||||
|
||||
@@ -31,7 +31,7 @@ This multi-faceted software offers a range of features that will help you manage
|
||||
- **Integrations**: Connects with various tools and services for enhanced workflow and issue management
|
||||
|
||||
<Banner type="info">
|
||||
This plugin is completely open-source and the [source code can be found here](https://github.com/payloadcms/payload/tree/main/packages/plugin-sentry). If you need help, check out our [Community Help](https://payloadcms.com/community-help). If you think you've found a bug, please [open a new issue](https://github.com/payloadcms/payload/issues/new?assignees=&labels=plugin%3A%20seo&template=bug_report.md&title=plugin-sentry%3A) with as much detail as possible.
|
||||
This plugin is completely open-source and the [source code can be found here](https://github.com/payloadcms/payload/tree/beta/packages/plugin-sentry). If you need help, check out our [Community Help](https://payloadcms.com/community-help). If you think you've found a bug, please [open a new issue](https://github.com/payloadcms/payload/issues/new?assignees=&labels=plugin%3A%20seo&template=bug_report.md&title=plugin-sentry%3A) with as much detail as possible.
|
||||
</Banner>
|
||||
|
||||
## Installation
|
||||
@@ -39,7 +39,7 @@ This multi-faceted software offers a range of features that will help you manage
|
||||
Install the plugin using any JavaScript package manager like [Yarn](https://yarnpkg.com), [NPM](https://npmjs.com), or [PNPM](https://pnpm.io):
|
||||
|
||||
```bash
|
||||
pnpm add @payloadcms/plugin-sentry
|
||||
pnpm add @payloadcms/plugin-sentry@beta
|
||||
```
|
||||
|
||||
## Sentry for Next.js setup
|
||||
|
||||
@@ -37,7 +37,7 @@ To help you visualize what your page might look like in a search engine, a previ
|
||||
Install the plugin using any JavaScript package manager like [Yarn](https://yarnpkg.com), [NPM](https://npmjs.com), or [PNPM](https://pnpm.io):
|
||||
|
||||
```bash
|
||||
pnpm add @payloadcms/plugin-seo
|
||||
pnpm add @payloadcms/plugin-seo@beta
|
||||
```
|
||||
|
||||
## Basic Usage
|
||||
@@ -171,7 +171,7 @@ A function that allows you to return any meta description, including from docume
|
||||
}
|
||||
```
|
||||
|
||||
For a full list of arguments, see the [`generateTitle`](#generatetitle) function.
|
||||
For a full list of arguments, see the [`generateTitle`](#generateTitle) function.
|
||||
|
||||
##### `generateImage`
|
||||
|
||||
@@ -187,7 +187,7 @@ A function that allows you to return any meta image, including from document's c
|
||||
}
|
||||
```
|
||||
|
||||
For a full list of arguments, see the [`generateTitle`](#generatetitle) function.
|
||||
For a full list of arguments, see the [`generateTitle`](#generateTitle) function.
|
||||
|
||||
##### `generateURL`
|
||||
|
||||
@@ -204,7 +204,7 @@ A function called by the search preview component to display the actual URL of y
|
||||
}
|
||||
```
|
||||
|
||||
For a full list of arguments, see the [`generateTitle`](#generatetitle) function.
|
||||
For a full list of arguments, see the [`generateTitle`](#generateTitle) function.
|
||||
|
||||
#### `interfaceName`
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ keywords: plugins, stripe, payments, ecommerce
|
||||
|
||||
[](https://www.npmjs.com/package/@payloadcms/plugin-stripe)
|
||||
|
||||
With this plugin you can easily integrate [Stripe](https://stripe.com) into Payload. Simply provide your Stripe credentials and this plugin will open up a two-way communication channel between the two platforms. This enables you to easily sync data back and forth, as well as proxy the Stripe REST API through Payload's [Access Control](../access-control/overview). Use this plugin to completely offload billing to Stripe and retain full control over your application's data.
|
||||
With this plugin you can easily integrate [Stripe](https://stripe.com) into Payload. Simply provide your Stripe credentials and this plugin will open up a two-way communication channel between the two platforms. This enables you to easily sync data back and forth, as well as proxy the Stripe REST API through Payload's [Access Control](../access-control). Use this plugin to completely offload billing to Stripe and retain full control over your application's data.
|
||||
|
||||
For example, you might be building an e-commerce or SaaS application, where you have a `products` or a `plans` collection that requires either a one-time payment or a subscription. You can to tie each of these products to Stripe, then easily subscribe to billing-related events to perform your application's business logic, such as active purchases or subscription cancellations.
|
||||
|
||||
@@ -39,7 +39,7 @@ The beauty of this plugin is the entirety of your application's content and busi
|
||||
Install the plugin using any JavaScript package manager like [Yarn](https://yarnpkg.com), [NPM](https://npmjs.com), or [PNPM](https://pnpm.io):
|
||||
|
||||
```bash
|
||||
pnpm add @payloadcms/plugin-stripe
|
||||
pnpm add @payloadcms/plugin-stripe@beta
|
||||
```
|
||||
|
||||
## Basic Usage
|
||||
|
||||
@@ -147,11 +147,11 @@ But, if you do, and you still want to use an ephemeral filesystem provider, you
|
||||
|
||||
Payload provides a list of official cloud storage adapters for you to use:
|
||||
|
||||
- [Azure Blob Storage](https://github.com/payloadcms/payload/tree/main/packages/storage-azure)
|
||||
- [Google Cloud Storage](https://github.com/payloadcms/payload/tree/main/packages/storage-gcs)
|
||||
- [AWS S3](https://github.com/payloadcms/payload/tree/main/packages/storage-s3)
|
||||
- [Uploadthing](https://github.com/payloadcms/payload/tree/main/packages/storage-uploadthing)
|
||||
- [Vercel Blob Storage](https://github.com/payloadcms/payload/tree/main/packages/storage-vercel-blob)
|
||||
- [Azure Blob Storage](https://github.com/payloadcms/payload/tree/beta/packages/storage-azure)
|
||||
- [Google Cloud Storage](https://github.com/payloadcms/payload/tree/beta/packages/storage-gcs)
|
||||
- [AWS S3](https://github.com/payloadcms/payload/tree/beta/packages/storage-s3)
|
||||
- [Uploadthing](https://github.com/payloadcms/payload/tree/beta/packages/storage-uploadthing)
|
||||
- [Vercel Blob Storage](https://github.com/payloadcms/payload/tree/beta/packages/storage-vercel-blob)
|
||||
|
||||
Follow the docs to configure any one of these storage providers. For local development, it might be handy to simply store uploads on your own computer, and then when it comes to production, simply enable the plugin for the cloud storage vendor of your choice.
|
||||
|
||||
|
||||
@@ -52,8 +52,8 @@ The following operators are available for use in queries:
|
||||
| `all` | The value must contain all values provided in the comma-delimited list. |
|
||||
| `exists` | Only return documents where the value either exists (`true`) or does not exist (`false`). |
|
||||
| `near` | For distance related to a [Point Field](../fields/point) comma separated as `<longitude>, <latitude>, <maxDistance in meters (nullable)>, <minDistance in meters (nullable)>`. |
|
||||
| `within` | For [Point Fields](../fields/point) to filter documents based on whether points are inside of the given area defined in GeoJSON. [Example](../fields/point#querying-within) |
|
||||
| `intersects` | For [Point Fields](../fields/point) to filter documents based on whether points intersect with the given area defined in GeoJSON. [Example](../fields/point#querying-intersects) |
|
||||
| `within` | For [point fields][../fields/point] to filter documents based on whether points are inside of the given area defined in GeoJSON. [Example](../fields/point#querying---within) |
|
||||
| `intersects` | For [point fields][../fields/point] to filter documents based on whether points intersect with the given area defined in GeoJSON. [Example](../fields/point#querying---intersects) |
|
||||
|
||||
<Banner type="success">
|
||||
<strong>Tip:</strong>
|
||||
|
||||
@@ -107,9 +107,9 @@ const getPosts = async () => {
|
||||
The `defaultPopulate` property allows you specify which fields to select when populating the collection from another document.
|
||||
This is especially useful for links where only the `slug` is needed instead of the entire document.
|
||||
|
||||
With this feature, you can dramatically reduce the amount of JSON that is populated from [Relationship](/docs/fields/relationship) or [Upload](/docs/fields/upload) fields.
|
||||
With this feature, you can dramatically reduce the amount of JSON that is populated from [Relationship](/docs/beta/fields/relationship) or [Upload](/docs/beta/fields/upload) fields.
|
||||
|
||||
For example, in your content model, you might have a `Link` field which links out to a different page. When you go to retrieve these links, you really only need the `slug` of the page.
|
||||
For example, in your content model, you might have a `Link` field which links out to a different page. When you go to retrieve these links, you really only need the `slug` of the page.
|
||||
|
||||
Loading all of the page content, its related links, and everything else is going to be overkill and will bog down your Payload APIs. Instead, you can define the `defaultPopulate` property on your `Pages` collection, so that when Payload "populates" a related Page, it only selects the `slug` field and therefore returns significantly less JSON:
|
||||
|
||||
|
||||
@@ -13,8 +13,6 @@ keywords: rest, api, documentation, Content Management System, cms, headless, ja
|
||||
The REST API is a fully functional HTTP client that allows you to interact with your Documents in a RESTful manner. It supports all CRUD operations and is equipped with automatic pagination, depth, and sorting.
|
||||
All Payload API routes are mounted and prefixed to your config's `routes.api` URL segment (default: `/api`).
|
||||
|
||||
To enhance DX, you can use [Payload SDK](#payload-rest-api-sdk) to query your REST API.
|
||||
|
||||
**REST query parameters:**
|
||||
|
||||
- [depth](../queries/depth) - automatically populates relationships and uploads
|
||||
@@ -503,7 +501,7 @@ Globals cannot be created or deleted, so there are only two REST endpoints opene
|
||||
|
||||
## Preferences
|
||||
|
||||
In addition to the dynamically generated endpoints above Payload also has REST endpoints to manage the admin user [preferences](/docs/admin/preferences) for data specific to the authenticated user.
|
||||
In addition to the dynamically generated endpoints above Payload also has REST endpoints to manage the admin user [preferences](/docs/admin/overview#preferences) for data specific to the authenticated user.
|
||||
|
||||
<RestExamples
|
||||
data={[
|
||||
@@ -754,243 +752,3 @@ const res = await fetch(`${api}/${collectionSlug}?depth=1&locale=en`, {
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
## Payload REST API SDK
|
||||
|
||||
The best, fully type-safe way to query Payload REST API is to use its SDK client, which can be installed with:
|
||||
|
||||
```bash
|
||||
pnpm add @payloadcms/sdk
|
||||
```
|
||||
|
||||
Its usage is very similar to [the Local API](../local-api/overview).
|
||||
|
||||
Example:
|
||||
```ts
|
||||
import { PayloadSDK } from '@payloadcms/sdk'
|
||||
import type { Config } from './payload-types'
|
||||
|
||||
// Pass your config from generated types as generic
|
||||
const sdk = new PayloadSDK<Config>({
|
||||
baseURL: 'https://example.com/api',
|
||||
})
|
||||
|
||||
// Find operation
|
||||
const posts = await sdk.find({
|
||||
collection: 'posts',
|
||||
draft: true,
|
||||
limit: 10,
|
||||
locale: 'en',
|
||||
page: 1,
|
||||
where: { _status: { equals: 'published' } },
|
||||
})
|
||||
|
||||
// Find by ID operation
|
||||
const posts = await sdk.findByID({
|
||||
id,
|
||||
collection: 'posts',
|
||||
draft: true,
|
||||
locale: 'en',
|
||||
})
|
||||
|
||||
// Auth login operation
|
||||
const result = await sdk.login({
|
||||
collection: 'users',
|
||||
data: {
|
||||
email: 'dev@payloadcms.com',
|
||||
password: '12345',
|
||||
},
|
||||
})
|
||||
|
||||
// Create operation
|
||||
const result = await sdk.create({
|
||||
collection: 'posts',
|
||||
data: { text: 'text' },
|
||||
})
|
||||
|
||||
// Create operation with a file
|
||||
// `file` can be either a Blob | File object or a string URL
|
||||
const result = await sdk.create({ collection: 'media', file, data: {} })
|
||||
|
||||
// Count operation
|
||||
const result = await sdk.count({ collection: 'posts', where: { id: { equals: post.id } } })
|
||||
|
||||
// Update (by ID) operation
|
||||
const result = await sdk.update({
|
||||
collection: 'posts',
|
||||
id: post.id,
|
||||
data: {
|
||||
text: 'updated-text',
|
||||
},
|
||||
})
|
||||
|
||||
// Update (bulk) operation
|
||||
const result = await sdk.update({
|
||||
collection: 'posts',
|
||||
where: {
|
||||
id: {
|
||||
equals: post.id,
|
||||
},
|
||||
},
|
||||
data: { text: 'updated-text-bulk' },
|
||||
})
|
||||
|
||||
// Delete (by ID) operation
|
||||
const result = await sdk.delete({ id: post.id, collection: 'posts' })
|
||||
|
||||
// Delete (bulk) operation
|
||||
const result = await sdk.delete({ where: { id: { equals: post.id } }, collection: 'posts' })
|
||||
|
||||
// Find Global operation
|
||||
const result = await sdk.findGlobal({ slug: 'global' })
|
||||
|
||||
// Update Global operation
|
||||
const result = await sdk.updateGlobal({ slug: 'global', data: { text: 'some-updated-global' } })
|
||||
|
||||
// Auth Login operation
|
||||
const result = await sdk.login({
|
||||
collection: 'users',
|
||||
data: { email: 'dev@payloadcms.com', password: '123456' },
|
||||
})
|
||||
|
||||
// Auth Me operation
|
||||
const result = await sdk.me(
|
||||
{ collection: 'users' },
|
||||
{
|
||||
headers: {
|
||||
Authorization: `JWT ${user.token}`,
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
// Auth Refresh Token operation
|
||||
const result = await sdk.refreshToken(
|
||||
{ collection: 'users' },
|
||||
{ headers: { Authorization: `JWT ${user.token}` } },
|
||||
)
|
||||
|
||||
// Auth Forgot Password operation
|
||||
const result = await sdk.forgotPassword({
|
||||
collection: 'users',
|
||||
data: { email: user.email },
|
||||
})
|
||||
|
||||
// Auth Reset Password operation
|
||||
const result = await sdk.resetPassword({
|
||||
collection: 'users',
|
||||
data: { password: '1234567', token: resetPasswordToken },
|
||||
})
|
||||
|
||||
// Find Versions operation
|
||||
const result = await sdk.findVersions({
|
||||
collection: 'posts',
|
||||
where: { parent: { equals: post.id } },
|
||||
})
|
||||
|
||||
// Find Version by ID operation
|
||||
const result = await sdk.findVersionByID({ collection: 'posts', id: version.id })
|
||||
|
||||
// Restore Version operation
|
||||
const result = await sdk.restoreVersion({
|
||||
collection: 'posts',
|
||||
id,
|
||||
})
|
||||
|
||||
// Find Global Versions operation
|
||||
const result = await sdk.findGlobalVersions({
|
||||
slug: 'global',
|
||||
})
|
||||
|
||||
// Find Global Version by ID operation
|
||||
const result = await sdk.findGlobalVersionByID({ id: version.id, slug: 'global' })
|
||||
|
||||
// Restore Global Version operation
|
||||
const result = await sdk.restoreGlobalVersion({
|
||||
slug: 'global',
|
||||
id
|
||||
})
|
||||
```
|
||||
|
||||
|
||||
|
||||
Every operation has optional 3rd parameter which is used to add additional data to the RequestInit object (like headers):
|
||||
```ts
|
||||
await sdk.me({
|
||||
collection: "users"
|
||||
}, {
|
||||
// RequestInit object
|
||||
headers: {
|
||||
Authorization: `JWT ${token}`
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
To query custom endpoints, you can use the `request` method, which is used internally for all other methods:
|
||||
```ts
|
||||
await sdk.request({
|
||||
method: 'POST',
|
||||
path: '/send-data',
|
||||
json: {
|
||||
id: 1,
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
Custom `fetch` implementation and `baseInit` for shared `RequestInit` properties:
|
||||
```ts
|
||||
const sdk = new PayloadSDK<Config>({
|
||||
baseInit: { credentials: 'include' },
|
||||
baseURL: 'https://example.com/api',
|
||||
fetch: async (url, init) => {
|
||||
console.log('before req')
|
||||
const response = await fetch(url, init)
|
||||
console.log('after req')
|
||||
return response
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
Example of a custom `fetch` implementation for testing the REST API without needing to spin up a next development server:
|
||||
```ts
|
||||
import type { GeneratedTypes, SanitizedConfig } from 'payload'
|
||||
|
||||
import config from '@payload-config'
|
||||
import { REST_DELETE, REST_GET, REST_PATCH, REST_POST, REST_PUT } from '@payloadcms/next/routes'
|
||||
import { PayloadSDK } from '@payloadcms/sdk'
|
||||
|
||||
export type TypedPayloadSDK = PayloadSDK<GeneratedTypes>
|
||||
|
||||
const api = {
|
||||
GET: REST_GET(config),
|
||||
POST: REST_POST(config),
|
||||
PATCH: REST_PATCH(config),
|
||||
DELETE: REST_DELETE(config),
|
||||
PUT: REST_PUT(config),
|
||||
}
|
||||
|
||||
const awaitedConfig = await config
|
||||
|
||||
export const sdk = new PayloadSDK<GeneratedTypes>({
|
||||
baseURL: ``,
|
||||
fetch: (path: string, init: RequestInit) => {
|
||||
const [slugs, search] = path.slice(1).split('?')
|
||||
const url = `${awaitedConfig.serverURL || 'http://localhost:3000'}${awaitedConfig.routes.api}/${slugs}${search ? `?${search}` : ''}`
|
||||
|
||||
if (init.body instanceof FormData) {
|
||||
const file = init.body.get('file') as Blob
|
||||
if (file && init.headers instanceof Headers) {
|
||||
init.headers.set('Content-Length', file.size.toString())
|
||||
}
|
||||
}
|
||||
const request = new Request(url, init)
|
||||
|
||||
const params = {
|
||||
params: Promise.resolve({
|
||||
slug: slugs.split('/'),
|
||||
}),
|
||||
}
|
||||
|
||||
return api[init.method.toUpperCase()](request, params)
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
@@ -9,7 +9,7 @@ keywords: slatejs, lexical, rich text, json, custom editor, javascript, typescri
|
||||
Payload currently supports two official rich text editors and you can choose either one depending on your needs.
|
||||
|
||||
1. [SlateJS](/docs/rich-text/slate) - stable, backwards-compatible with 1.0
|
||||
2. [Lexical](/docs/lexical/overview) - recommended
|
||||
2. [Lexical](/docs/lexical/overview) - beta, where things will be moving in the future
|
||||
|
||||
These editors are built on an "adapter pattern" which means that you will need to install the editor you'd like to use. Take a look at the docs for the editor you'd like to use for instructions on how to install it.
|
||||
|
||||
|
||||
@@ -9,23 +9,23 @@ keywords: uploads, images, media, storage, adapters, s3, vercel, google cloud, a
|
||||
Payload offers additional storage adapters to handle file uploads. These adapters allow you to store files in different locations, such as Amazon S3, Vercel Blob Storage, Google Cloud Storage, and more.
|
||||
|
||||
| Service | Package |
|
||||
| -------------------- |-------------------------------------------------------------------------------------------------------------------|
|
||||
| Vercel Blob | [`@payloadcms/storage-vercel-blob`](https://github.com/payloadcms/payload/tree/main/packages/storage-vercel-blob) |
|
||||
| AWS S3 | [`@payloadcms/storage-s3`](https://github.com/payloadcms/payload/tree/main/packages/storage-s3) |
|
||||
| Azure | [`@payloadcms/storage-azure`](https://github.com/payloadcms/payload/tree/main/packages/storage-azure) |
|
||||
| Google Cloud Storage | [`@payloadcms/storage-gcs`](https://github.com/payloadcms/payload/tree/main/packages/storage-gcs) |
|
||||
| Uploadthing | [`@payloadcms/storage-uploadthing`](https://github.com/payloadcms/payload/tree/main/packages/storage-uploadthing) |
|
||||
| -------------------- | ----------------------------------------------------------------------------------------------------------------- |
|
||||
| Vercel Blob | [`@payloadcms/storage-vercel-blob`](https://github.com/payloadcms/payload/tree/beta/packages/storage-vercel-blob) |
|
||||
| AWS S3 | [`@payloadcms/storage-s3`](https://github.com/payloadcms/payload/tree/beta/packages/storage-s3) |
|
||||
| Azure | [`@payloadcms/storage-azure`](https://github.com/payloadcms/payload/tree/beta/packages/storage-azure) |
|
||||
| Google Cloud Storage | [`@payloadcms/storage-gcs`](https://github.com/payloadcms/payload/tree/beta/packages/storage-gcs) |
|
||||
|
||||
|
||||
## Vercel Blob Storage
|
||||
[`@payloadcms/storage-vercel-blob`](https://www.npmjs.com/package/@payloadcms/storage-vercel-blob)
|
||||
|
||||
### Installation#vercel-blob-installation
|
||||
### Installation
|
||||
|
||||
```sh
|
||||
pnpm add @payloadcms/storage-vercel-blob
|
||||
pnpm add @payloadcms/storage-vercel-blob@beta
|
||||
```
|
||||
|
||||
### Usage#vercel-blob-usage
|
||||
### Usage
|
||||
|
||||
- Configure the `collections` object to specify which collections should use the Vercel Blob adapter. The slug _must_ match one of your existing collection slugs.
|
||||
- Ensure you have `BLOB_READ_WRITE_TOKEN` set in your Vercel environment variables. This is usually set by Vercel automatically after adding blob storage to your project.
|
||||
@@ -43,8 +43,8 @@ export default buildConfig({
|
||||
enabled: true, // Optional, defaults to true
|
||||
// Specify which collections should use Vercel Blob
|
||||
collections: {
|
||||
media: true,
|
||||
'media-with-prefix': {
|
||||
[Media.slug]: true,
|
||||
[MediaWithPrefix.slug]: {
|
||||
prefix: 'my-prefix',
|
||||
},
|
||||
},
|
||||
@@ -55,7 +55,7 @@ export default buildConfig({
|
||||
})
|
||||
```
|
||||
|
||||
### Configuration Options#vercel-blob-configuration
|
||||
### Configuration Options
|
||||
|
||||
| Option | Description | Default |
|
||||
| -------------------- | -------------------------------------------------------------------- | ----------------------------- |
|
||||
@@ -68,13 +68,13 @@ export default buildConfig({
|
||||
## S3 Storage
|
||||
[`@payloadcms/storage-s3`](https://www.npmjs.com/package/@payloadcms/storage-s3)
|
||||
|
||||
### Installation#s3-installation
|
||||
### Installation
|
||||
|
||||
```sh
|
||||
pnpm add @payloadcms/storage-s3
|
||||
pnpm add @payloadcms/storage-s3@beta
|
||||
```
|
||||
|
||||
### Usage#s3-usage
|
||||
### Usage
|
||||
|
||||
- Configure the `collections` object to specify which collections should use the S3 Storage adapter. The slug _must_ match one of your existing collection slugs.
|
||||
- The `config` object can be any [`S3ClientConfig`](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/client/s3) object (from [`@aws-sdk/client-s3`](https://github.com/aws/aws-sdk-js-v3)). _This is highly dependent on your AWS setup_. Check the AWS documentation for more information.
|
||||
@@ -90,8 +90,8 @@ export default buildConfig({
|
||||
plugins: [
|
||||
s3Storage({
|
||||
collections: {
|
||||
media: true,
|
||||
'media-with-prefix': {
|
||||
[mediaSlug]: true,
|
||||
[mediaWithPrefixSlug]: {
|
||||
prefix,
|
||||
},
|
||||
},
|
||||
@@ -109,20 +109,20 @@ export default buildConfig({
|
||||
})
|
||||
```
|
||||
|
||||
### Configuration Options#s3-configuration
|
||||
#### Configuration Options
|
||||
|
||||
See the the [AWS SDK Package](https://github.com/aws/aws-sdk-js-v3) and [`S3ClientConfig`](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/client/s3) object for guidance on AWS S3 configuration.
|
||||
|
||||
## Azure Blob Storage
|
||||
[`@payloadcms/storage-azure`](https://www.npmjs.com/package/@payloadcms/storage-azure)
|
||||
|
||||
### Installation#azure-installation
|
||||
### Installation
|
||||
|
||||
```sh
|
||||
pnpm add @payloadcms/storage-azure
|
||||
pnpm add @payloadcms/storage-azure@beta
|
||||
```
|
||||
|
||||
### Usage#azure-usage
|
||||
### Usage
|
||||
|
||||
- Configure the `collections` object to specify which collections should use the Azure Blob adapter. The slug _must_ match one of your existing collection slugs.
|
||||
- When enabled, this package will automatically set `disableLocalStorage` to `true` for each collection.
|
||||
@@ -137,8 +137,8 @@ export default buildConfig({
|
||||
plugins: [
|
||||
azureStorage({
|
||||
collections: {
|
||||
media: true,
|
||||
'media-with-prefix': {
|
||||
[mediaSlug]: true,
|
||||
[mediaWithPrefixSlug]: {
|
||||
prefix,
|
||||
},
|
||||
},
|
||||
@@ -151,7 +151,7 @@ export default buildConfig({
|
||||
})
|
||||
```
|
||||
|
||||
### Configuration Options#azure-configuration
|
||||
### Configuration Options
|
||||
|
||||
| Option | Description | Default |
|
||||
| ---------------------- | ------------------------------------------------------------------------ | ------- |
|
||||
@@ -165,13 +165,13 @@ export default buildConfig({
|
||||
## Google Cloud Storage
|
||||
[`@payloadcms/storage-gcs`](https://www.npmjs.com/package/@payloadcms/storage-gcs)
|
||||
|
||||
### Installation#gcs-installation
|
||||
### Installation
|
||||
|
||||
```sh
|
||||
pnpm add @payloadcms/storage-gcs
|
||||
pnpm add @payloadcms/storage-gcs@beta
|
||||
```
|
||||
|
||||
### Usage#gcs-usage
|
||||
### Usage
|
||||
|
||||
- Configure the `collections` object to specify which collections should use the Google Cloud Storage adapter. The slug _must_ match one of your existing collection slugs.
|
||||
- When enabled, this package will automatically set `disableLocalStorage` to `true` for each collection.
|
||||
@@ -186,8 +186,8 @@ export default buildConfig({
|
||||
plugins: [
|
||||
gcsStorage({
|
||||
collections: {
|
||||
media: true,
|
||||
'media-with-prefix': {
|
||||
[mediaSlug]: true,
|
||||
[mediaWithPrefixSlug]: {
|
||||
prefix,
|
||||
},
|
||||
},
|
||||
@@ -201,7 +201,7 @@ export default buildConfig({
|
||||
})
|
||||
```
|
||||
|
||||
### Configuration Options#gcs-configuration
|
||||
### Configuration Options
|
||||
|
||||
| Option | Description | Default |
|
||||
| ------------- | --------------------------------------------------------------------------------------------------- | --------- |
|
||||
@@ -215,16 +215,16 @@ export default buildConfig({
|
||||
## Uploadthing Storage
|
||||
[`@payloadcms/storage-uploadthing`](https://www.npmjs.com/package/@payloadcms/storage-uploadthing)
|
||||
|
||||
### Installation#uploadthing-installation
|
||||
### Installation
|
||||
|
||||
```sh
|
||||
pnpm add @payloadcms/storage-uploadthing
|
||||
pnpm add @payloadcms/storage-uploadthing@beta
|
||||
```
|
||||
|
||||
### Usage#uploadthing-usage
|
||||
### Usage
|
||||
|
||||
- Configure the `collections` object to specify which collections should use uploadthing. The slug _must_ match one of your existing collection slugs and be an `upload` type.
|
||||
- Get a token from Uploadthing and set it as `token` in the `options` object.
|
||||
- Get an API key from Uploadthing and set it as `apiKey` in the `options` object.
|
||||
- `acl` is optional and defaults to `public-read`.
|
||||
|
||||
```ts
|
||||
@@ -233,10 +233,10 @@ export default buildConfig({
|
||||
plugins: [
|
||||
uploadthingStorage({
|
||||
collections: {
|
||||
media: true,
|
||||
[mediaSlug]: true,
|
||||
},
|
||||
options: {
|
||||
token: process.env.UPLOADTHING_TOKEN,
|
||||
apiKey: process.env.UPLOADTHING_SECRET,
|
||||
acl: 'public-read',
|
||||
},
|
||||
}),
|
||||
@@ -244,11 +244,11 @@ export default buildConfig({
|
||||
})
|
||||
```
|
||||
|
||||
### Configuration Options#uploadthing-configuration
|
||||
### Configuration Options
|
||||
|
||||
| Option | Description | Default |
|
||||
| ---------------- | ----------------------------------------------- | ------------- |
|
||||
| `token` | Token from Uploadthing. Required. | |
|
||||
| `apiKey` | API key from Uploadthing. Required. | |
|
||||
| `acl` | Access control list for files that are uploaded | `public-read` |
|
||||
| `logLevel` | Log level for Uploadthing | `info` |
|
||||
| `fetch` | Custom fetch function | `fetch` |
|
||||
@@ -259,11 +259,11 @@ export default buildConfig({
|
||||
|
||||
If you need to create a custom storage adapter, you can use the [`@payloadcms/plugin-cloud-storage`](https://www.npmjs.com/package/@payloadcms/plugin-cloud-storage) package. This package is used internally by the storage adapters mentioned above.
|
||||
|
||||
### Installation#custom-installation
|
||||
### Installation
|
||||
|
||||
`pnpm add @payloadcms/plugin-cloud-storage`
|
||||
`pnpm add @payloadcms/plugin-cloud-storage@beta`
|
||||
|
||||
### Usage#custom-usage
|
||||
### Usage
|
||||
|
||||
Reference any of the existing storage adapters for guidance on how this should be structured. Create an adapter following the `GeneratedAdapter` interface. Then, pass the adapter to the `cloudStorage` plugin.
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ export const rootParserOptions = {
|
||||
ecmaVersion: 'latest',
|
||||
projectService: {
|
||||
maximumDefaultProjectFileMatchCount_THIS_WILL_SLOW_DOWN_LINTING: 40,
|
||||
allowDefaultProject: ['scripts/*.ts', '*.js', '*.mjs', '*.d.ts'],
|
||||
allowDefaultProject: ['scripts/*.ts', '*.js', '*.mjs', '*.spec.ts', '*.d.ts'],
|
||||
},
|
||||
}
|
||||
|
||||
@@ -42,7 +42,6 @@ export const rootEslintConfig = [
|
||||
'test/live-preview/next-app',
|
||||
'packages/**/*.spec.ts',
|
||||
'templates/**',
|
||||
'examples/**',
|
||||
],
|
||||
},
|
||||
{
|
||||
|
||||
1
examples/auth/next-app/.env.example
Normal file
1
examples/auth/next-app/.env.example
Normal file
@@ -0,0 +1 @@
|
||||
NEXT_PUBLIC_PAYLOAD_URL=http://localhost:3000
|
||||
7
examples/auth/next-app/.eslintrc.cjs
Normal file
7
examples/auth/next-app/.eslintrc.cjs
Normal file
@@ -0,0 +1,7 @@
|
||||
module.exports = {
|
||||
root: true,
|
||||
extends: ['plugin:@next/next/recommended', '@payloadcms'],
|
||||
rules: {
|
||||
'import/extensions': 'off',
|
||||
},
|
||||
}
|
||||
6
examples/auth/next-app/.gitignore
vendored
Normal file
6
examples/auth/next-app/.gitignore
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
.next
|
||||
dist
|
||||
build
|
||||
node_modules
|
||||
.env
|
||||
package-lock.json
|
||||
44
examples/auth/next-app/README.md
Normal file
44
examples/auth/next-app/README.md
Normal file
@@ -0,0 +1,44 @@
|
||||
# Payload Auth Example Front-End
|
||||
|
||||
This is a [Next.js](https://nextjs.org) [App Router](https://nextjs.org/docs/app) front-end made explicitly for the [Payload Auth Example](https://github.com/payloadcms/payload/tree/main/examples/auth). This example demonstrates how to authenticate your Next.js app using [Payload Authentication](https://payloadcms.com/docs/authentication/overview).
|
||||
|
||||
> This example uses the App Router, the latest API of Next.js. If your app is using the legacy [Pages Router](https://nextjs.org/docs/pages), check out the official [Pages Router Example](https://github.com/payloadcms/payload/tree/main/examples/auth/next-pages).
|
||||
|
||||
**IMPORTANT—This application runs on a different server as Payload and establishes a connection from another domain or port over HTTP.** For an integrated setup that runs on a single server and uses the [Local API](https://payloadcms.com/docs/local-api/overview#local-api), check out [how to serve Payload alongside Next.js](https://github.com/payloadcms/payload/tree/main/examples/auth/payload). To learn more about this, check out [how Payload can be used in its various headless capacities](https://payloadcms.com/blog/the-ultimate-guide-to-using-nextjs-with-payload).
|
||||
|
||||
## Getting Started
|
||||
|
||||
### Payload
|
||||
|
||||
First you'll need a running Payload app. There is one made explicitly for this example and [can be found here](https://github.com/payloadcms/payload/tree/main/examples/auth/payload). If you have not done so already, clone it down and follow the setup instructions there. This will provide all the necessary APIs that your Next.js app requires for authentication.
|
||||
|
||||
### Next.js
|
||||
|
||||
1. Clone this repo
|
||||
2. `cd` into this directory and run `pnpm i --ignore-workspace`\*, `yarn`, or `npm install`
|
||||
> \*If you are running using pnpm within the Payload Monorepo, the `--ignore-workspace` flag is needed so that pnpm generates a lockfile in this example's directory despite the fact that one exists in root.
|
||||
3. `cp .env.example .env` to copy the example environment variables
|
||||
4. `pnpm dev`, `yarn dev`, or `npm run dev` to start the server
|
||||
5. `open http://localhost:3001` to see the result
|
||||
|
||||
Once running, a user is automatically seeded in your local environment with some basic instructions. See the [Payload Auth Example](https://github.com/payloadcms/payload/tree/main/examples/auth) for full details.
|
||||
|
||||
## Learn More
|
||||
|
||||
To learn more about Payload and Next.js, take a look at the following resources:
|
||||
|
||||
- [Payload Documentation](https://payloadcms.com/docs) - learn about Payload features and API.
|
||||
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
|
||||
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
|
||||
|
||||
You can check out [the Payload GitHub repository](https://github.com/payloadcms/payload) as well as [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome!
|
||||
|
||||
## Deployment
|
||||
|
||||
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new) from the creators of Next.js. You could also combine this app into a [single Express server](https://github.com/payloadcms/payload/tree/main/examples/custom-server) and deploy in to [Payload Cloud](https://payloadcms.com/new/import).
|
||||
|
||||
Check out our [Payload deployment documentation](https://payloadcms.com/docs/production/deployment) or the [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
|
||||
|
||||
## Questions
|
||||
|
||||
If you have any issues or questions, reach out to us on [Discord](https://discord.com/invite/payload) or start a [GitHub discussion](https://github.com/payloadcms/payload/discussions).
|
||||
75
examples/auth/next-app/app/_components/Button/index.tsx
Normal file
75
examples/auth/next-app/app/_components/Button/index.tsx
Normal file
@@ -0,0 +1,75 @@
|
||||
'use client'
|
||||
|
||||
import React, { ElementType } from 'react'
|
||||
import Link from 'next/link'
|
||||
|
||||
import classes from './index.module.scss'
|
||||
|
||||
export type Props = {
|
||||
label?: string
|
||||
appearance?: 'default' | 'primary' | 'secondary'
|
||||
el?: 'button' | 'link' | 'a'
|
||||
onClick?: () => void
|
||||
href?: string
|
||||
newTab?: boolean
|
||||
className?: string
|
||||
type?: 'submit' | 'button'
|
||||
disabled?: boolean
|
||||
invert?: boolean
|
||||
}
|
||||
|
||||
export const Button: React.FC<Props> = ({
|
||||
el: elFromProps = 'link',
|
||||
label,
|
||||
newTab,
|
||||
href,
|
||||
appearance,
|
||||
className: classNameFromProps,
|
||||
onClick,
|
||||
type = 'button',
|
||||
disabled,
|
||||
invert,
|
||||
}) => {
|
||||
let el = elFromProps
|
||||
const newTabProps = newTab ? { target: '_blank', rel: 'noopener noreferrer' } : {}
|
||||
|
||||
const className = [
|
||||
classes.button,
|
||||
classNameFromProps,
|
||||
classes[`appearance--${appearance}`],
|
||||
invert && classes[`${appearance}--invert`],
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(' ')
|
||||
|
||||
const content = (
|
||||
<div className={classes.content}>
|
||||
<span className={classes.label}>{label}</span>
|
||||
</div>
|
||||
)
|
||||
|
||||
if (onClick || type === 'submit') el = 'button'
|
||||
|
||||
if (el === 'link') {
|
||||
return (
|
||||
<Link href={href || ''} className={className} {...newTabProps} onClick={onClick}>
|
||||
{content}
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
|
||||
const Element: ElementType = el
|
||||
|
||||
return (
|
||||
<Element
|
||||
href={href}
|
||||
className={className}
|
||||
type={type}
|
||||
{...newTabProps}
|
||||
onClick={onClick}
|
||||
disabled={disabled}
|
||||
>
|
||||
{content}
|
||||
</Element>
|
||||
)
|
||||
}
|
||||
@@ -1,22 +1,21 @@
|
||||
import type { Ref } from 'react'
|
||||
|
||||
import React, { forwardRef } from 'react'
|
||||
import React, { forwardRef, Ref } from 'react'
|
||||
|
||||
import classes from './index.module.scss'
|
||||
|
||||
type Props = {
|
||||
children: React.ReactNode
|
||||
className?: string
|
||||
left?: boolean
|
||||
ref?: Ref<HTMLDivElement>
|
||||
right?: boolean
|
||||
className?: string
|
||||
children: React.ReactNode
|
||||
ref?: Ref<HTMLDivElement>
|
||||
}
|
||||
|
||||
export const Gutter: React.FC<Props> = forwardRef<HTMLDivElement, Props>((props, ref) => {
|
||||
const { children, className, left = true, right = true } = props
|
||||
const { left = true, right = true, className, children } = props
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={ref}
|
||||
className={[
|
||||
classes.gutter,
|
||||
left && classes.gutterLeft,
|
||||
@@ -25,7 +24,6 @@ export const Gutter: React.FC<Props> = forwardRef<HTMLDivElement, Props>((props,
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(' ')}
|
||||
ref={ref}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
38
examples/auth/next-app/app/_components/Header/Nav/index.tsx
Normal file
38
examples/auth/next-app/app/_components/Header/Nav/index.tsx
Normal file
@@ -0,0 +1,38 @@
|
||||
'use client'
|
||||
|
||||
import React from 'react'
|
||||
import Link from 'next/link'
|
||||
|
||||
import { useAuth } from '../../../_providers/Auth'
|
||||
|
||||
import classes from './index.module.scss'
|
||||
|
||||
export const HeaderNav: React.FC = () => {
|
||||
const { user } = useAuth()
|
||||
|
||||
return (
|
||||
<nav
|
||||
className={[
|
||||
classes.nav,
|
||||
// fade the nav in on user load to avoid flash of content and layout shift
|
||||
// Vercel also does this in their own website header, see https://vercel.com
|
||||
user === undefined && classes.hide,
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(' ')}
|
||||
>
|
||||
{user && (
|
||||
<React.Fragment>
|
||||
<Link href="/account">Account</Link>
|
||||
<Link href="/logout">Logout</Link>
|
||||
</React.Fragment>
|
||||
)}
|
||||
{!user && (
|
||||
<React.Fragment>
|
||||
<Link href="/login">Login</Link>
|
||||
<Link href="/create-account">Create Account</Link>
|
||||
</React.Fragment>
|
||||
)}
|
||||
</nav>
|
||||
)
|
||||
}
|
||||
34
examples/auth/next-app/app/_components/Header/index.tsx
Normal file
34
examples/auth/next-app/app/_components/Header/index.tsx
Normal file
@@ -0,0 +1,34 @@
|
||||
import React from 'react'
|
||||
import Image from 'next/image'
|
||||
import Link from 'next/link'
|
||||
|
||||
import { Gutter } from '../Gutter'
|
||||
import { HeaderNav } from './Nav'
|
||||
|
||||
import classes from './index.module.scss'
|
||||
|
||||
export function Header() {
|
||||
return (
|
||||
<header className={classes.header}>
|
||||
<Gutter className={classes.wrap}>
|
||||
<Link href="/" className={classes.logo}>
|
||||
<picture>
|
||||
<source
|
||||
srcSet="https://raw.githubusercontent.com/payloadcms/payload/main/packages/payload/src/admin/assets/images/payload-logo-light.svg"
|
||||
media="(prefers-color-scheme: dark)"
|
||||
/>
|
||||
<Image
|
||||
width={150}
|
||||
height={30}
|
||||
alt="Payload Logo"
|
||||
src="https://raw.githubusercontent.com/payloadcms/payload/main/packages/payload/src/admin/assets/images/payload-logo-dark.svg"
|
||||
/>
|
||||
</picture>
|
||||
</Link>
|
||||
<HeaderNav />
|
||||
</Gutter>
|
||||
</header>
|
||||
)
|
||||
}
|
||||
|
||||
export default Header
|
||||
55
examples/auth/next-app/app/_components/Input/index.tsx
Normal file
55
examples/auth/next-app/app/_components/Input/index.tsx
Normal file
@@ -0,0 +1,55 @@
|
||||
import React from 'react'
|
||||
import { FieldValues, UseFormRegister, Validate } from 'react-hook-form'
|
||||
|
||||
import classes from './index.module.scss'
|
||||
|
||||
type Props = {
|
||||
name: string
|
||||
label: string
|
||||
register: UseFormRegister<FieldValues & any>
|
||||
required?: boolean
|
||||
error: any
|
||||
type?: 'text' | 'number' | 'password' | 'email'
|
||||
validate?: (value: string) => boolean | string
|
||||
}
|
||||
|
||||
export const Input: React.FC<Props> = ({
|
||||
name,
|
||||
label,
|
||||
required,
|
||||
register,
|
||||
error,
|
||||
type = 'text',
|
||||
validate,
|
||||
}) => {
|
||||
return (
|
||||
<div className={classes.inputWrap}>
|
||||
<label htmlFor="name" className={classes.label}>
|
||||
{`${label} ${required ? '*' : ''}`}
|
||||
</label>
|
||||
<input
|
||||
className={[classes.input, error && classes.error].filter(Boolean).join(' ')}
|
||||
{...{ type }}
|
||||
{...register(name, {
|
||||
required,
|
||||
validate,
|
||||
...(type === 'email'
|
||||
? {
|
||||
pattern: {
|
||||
value: /\S+@\S+\.\S+/,
|
||||
message: 'Please enter a valid email',
|
||||
},
|
||||
}
|
||||
: {}),
|
||||
})}
|
||||
/>
|
||||
{error && (
|
||||
<div className={classes.errorMessage}>
|
||||
{!error?.message && error?.type === 'required'
|
||||
? 'This field is required'
|
||||
: error?.message}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
33
examples/auth/next-app/app/_components/Message/index.tsx
Normal file
33
examples/auth/next-app/app/_components/Message/index.tsx
Normal file
@@ -0,0 +1,33 @@
|
||||
import React from 'react'
|
||||
|
||||
import classes from './index.module.scss'
|
||||
|
||||
export const Message: React.FC<{
|
||||
message?: React.ReactNode
|
||||
error?: React.ReactNode
|
||||
success?: React.ReactNode
|
||||
warning?: React.ReactNode
|
||||
className?: string
|
||||
}> = ({ message, error, success, warning, className }) => {
|
||||
const messageToRender = message || error || success || warning
|
||||
|
||||
if (messageToRender) {
|
||||
return (
|
||||
<div
|
||||
className={[
|
||||
classes.message,
|
||||
className,
|
||||
error && classes.error,
|
||||
success && classes.success,
|
||||
warning && classes.warning,
|
||||
!error && !success && !warning && classes.default,
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(' ')}
|
||||
>
|
||||
{messageToRender}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
return null
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
'use client'
|
||||
|
||||
import { useSearchParams } from 'next/navigation'
|
||||
|
||||
import { Message } from '../Message'
|
||||
|
||||
export const RenderParams: React.FC<{
|
||||
params?: string[]
|
||||
message?: string
|
||||
className?: string
|
||||
}> = ({ params = ['error', 'message', 'success'], message, className }) => {
|
||||
const searchParams = useSearchParams()
|
||||
const paramValues = params.map((param) => searchParams.get(param)).filter(Boolean)
|
||||
|
||||
if (paramValues.length) {
|
||||
return (
|
||||
<div className={className}>
|
||||
{paramValues.map((paramValue) => (
|
||||
<Message
|
||||
key={paramValue}
|
||||
message={(message || 'PARAM')?.replace('PARAM', paramValue || '')}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
@@ -1,8 +1,9 @@
|
||||
import React from 'react'
|
||||
|
||||
import classes from './index.module.scss'
|
||||
import serialize from './serialize'
|
||||
|
||||
import classes from './index.module.scss'
|
||||
|
||||
const RichText: React.FC<{ className?: string; content: any }> = ({ className, content }) => {
|
||||
if (!content) {
|
||||
return null
|
||||
@@ -1,19 +1,19 @@
|
||||
import escapeHTML from 'escape-html'
|
||||
import React, { Fragment } from 'react'
|
||||
import escapeHTML from 'escape-html'
|
||||
import { Text } from 'slate'
|
||||
|
||||
|
||||
// eslint-disable-next-line no-use-before-define
|
||||
type Children = Leaf[]
|
||||
|
||||
type Leaf = {
|
||||
[key: string]: unknown
|
||||
children: Children
|
||||
type: string
|
||||
url?: string
|
||||
value?: {
|
||||
alt: string
|
||||
url: string
|
||||
alt: string
|
||||
}
|
||||
children: Children
|
||||
url?: string
|
||||
[key: string]: unknown
|
||||
}
|
||||
|
||||
const serialize = (children: Children): React.ReactNode[] =>
|
||||
@@ -35,7 +35,7 @@ const serialize = (children: Children): React.ReactNode[] =>
|
||||
|
||||
if (node.underline) {
|
||||
text = (
|
||||
<span key={i} style={{ textDecoration: 'underline' }}>
|
||||
<span style={{ textDecoration: 'underline' }} key={i}>
|
||||
{text}
|
||||
</span>
|
||||
)
|
||||
@@ -43,7 +43,7 @@ const serialize = (children: Children): React.ReactNode[] =>
|
||||
|
||||
if (node.strikethrough) {
|
||||
text = (
|
||||
<span key={i} style={{ textDecoration: 'line-through' }}>
|
||||
<span style={{ textDecoration: 'line-through' }} key={i}>
|
||||
{text}
|
||||
</span>
|
||||
)
|
||||
34
examples/auth/next-app/app/_providers/Auth/gql.ts
Normal file
34
examples/auth/next-app/app/_providers/Auth/gql.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
export const USER = `
|
||||
id
|
||||
email
|
||||
firstName
|
||||
lastName
|
||||
`
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export const gql = async (query: string): Promise<any> => {
|
||||
try {
|
||||
const res = await fetch(`${process.env.NEXT_PUBLIC_PAYLOAD_URL}/api/graphql`, {
|
||||
method: 'POST',
|
||||
credentials: 'include',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
query,
|
||||
}),
|
||||
})
|
||||
|
||||
const { data, errors } = await res.json()
|
||||
|
||||
if (errors) {
|
||||
throw new Error(errors[0].message)
|
||||
}
|
||||
|
||||
if (res.ok && data) {
|
||||
return data
|
||||
}
|
||||
} catch (e: unknown) {
|
||||
throw new Error(e as string)
|
||||
}
|
||||
}
|
||||
180
examples/auth/next-app/app/_providers/Auth/index.tsx
Normal file
180
examples/auth/next-app/app/_providers/Auth/index.tsx
Normal file
@@ -0,0 +1,180 @@
|
||||
'use client'
|
||||
|
||||
import React, { createContext, useCallback, useContext, useEffect, useState } from 'react'
|
||||
|
||||
import { User } from '../../payload-types'
|
||||
import { gql, USER } from './gql'
|
||||
import { rest } from './rest'
|
||||
import { AuthContext, Create, ForgotPassword, Login, Logout, ResetPassword } from './types'
|
||||
|
||||
const Context = createContext({} as AuthContext)
|
||||
|
||||
export const AuthProvider: React.FC<{ children: React.ReactNode; api?: 'rest' | 'gql' }> = ({
|
||||
children,
|
||||
api = 'rest',
|
||||
}) => {
|
||||
const [user, setUser] = useState<User | null>()
|
||||
|
||||
const create = useCallback<Create>(
|
||||
async (args) => {
|
||||
if (api === 'rest') {
|
||||
const user = await rest(`${process.env.NEXT_PUBLIC_PAYLOAD_URL}/api/users`, args)
|
||||
setUser(user)
|
||||
return user
|
||||
}
|
||||
|
||||
if (api === 'gql') {
|
||||
const { createUser: user } = await gql(`mutation {
|
||||
createUser(data: { email: "${args.email}", password: "${args.password}", firstName: "${args.firstName}", lastName: "${args.lastName}" }) {
|
||||
${USER}
|
||||
}
|
||||
}`)
|
||||
|
||||
setUser(user)
|
||||
return user
|
||||
}
|
||||
},
|
||||
[api],
|
||||
)
|
||||
|
||||
const login = useCallback<Login>(
|
||||
async (args) => {
|
||||
if (api === 'rest') {
|
||||
const user = await rest(`${process.env.NEXT_PUBLIC_PAYLOAD_URL}/api/users/login`, args)
|
||||
setUser(user)
|
||||
return user
|
||||
}
|
||||
|
||||
if (api === 'gql') {
|
||||
const { loginUser } = await gql(`mutation {
|
||||
loginUser(email: "${args.email}", password: "${args.password}") {
|
||||
user {
|
||||
${USER}
|
||||
}
|
||||
exp
|
||||
}
|
||||
}`)
|
||||
|
||||
setUser(loginUser?.user)
|
||||
return loginUser?.user
|
||||
}
|
||||
},
|
||||
[api],
|
||||
)
|
||||
|
||||
const logout = useCallback<Logout>(async () => {
|
||||
if (api === 'rest') {
|
||||
await rest(`${process.env.NEXT_PUBLIC_PAYLOAD_URL}/api/users/logout`)
|
||||
setUser(null)
|
||||
return
|
||||
}
|
||||
|
||||
if (api === 'gql') {
|
||||
await gql(`mutation {
|
||||
logoutUser
|
||||
}`)
|
||||
|
||||
setUser(null)
|
||||
}
|
||||
}, [api])
|
||||
|
||||
// On mount, get user and set
|
||||
useEffect(() => {
|
||||
const fetchMe = async () => {
|
||||
if (api === 'rest') {
|
||||
const user = await rest(
|
||||
`${process.env.NEXT_PUBLIC_PAYLOAD_URL}/api/users/me`,
|
||||
{},
|
||||
{
|
||||
method: 'GET',
|
||||
},
|
||||
)
|
||||
setUser(user)
|
||||
}
|
||||
|
||||
if (api === 'gql') {
|
||||
const { meUser } = await gql(`query {
|
||||
meUser {
|
||||
user {
|
||||
${USER}
|
||||
}
|
||||
exp
|
||||
}
|
||||
}`)
|
||||
|
||||
setUser(meUser.user)
|
||||
}
|
||||
}
|
||||
|
||||
fetchMe()
|
||||
}, [api])
|
||||
|
||||
const forgotPassword = useCallback<ForgotPassword>(
|
||||
async (args) => {
|
||||
if (api === 'rest') {
|
||||
const user = await rest(
|
||||
`${process.env.NEXT_PUBLIC_PAYLOAD_URL}/api/users/forgot-password`,
|
||||
args,
|
||||
)
|
||||
setUser(user)
|
||||
return user
|
||||
}
|
||||
|
||||
if (api === 'gql') {
|
||||
const { forgotPasswordUser } = await gql(`mutation {
|
||||
forgotPasswordUser(email: "${args.email}")
|
||||
}`)
|
||||
|
||||
return forgotPasswordUser
|
||||
}
|
||||
},
|
||||
[api],
|
||||
)
|
||||
|
||||
const resetPassword = useCallback<ResetPassword>(
|
||||
async (args) => {
|
||||
if (api === 'rest') {
|
||||
const user = await rest(
|
||||
`${process.env.NEXT_PUBLIC_PAYLOAD_URL}/api/users/reset-password`,
|
||||
args,
|
||||
)
|
||||
setUser(user)
|
||||
return user
|
||||
}
|
||||
|
||||
if (api === 'gql') {
|
||||
const { resetPasswordUser } = await gql(`mutation {
|
||||
resetPasswordUser(password: "${args.password}", token: "${args.token}") {
|
||||
user {
|
||||
${USER}
|
||||
}
|
||||
}
|
||||
}`)
|
||||
|
||||
setUser(resetPasswordUser.user)
|
||||
return resetPasswordUser.user
|
||||
}
|
||||
},
|
||||
[api],
|
||||
)
|
||||
|
||||
return (
|
||||
<Context.Provider
|
||||
value={{
|
||||
user,
|
||||
setUser,
|
||||
login,
|
||||
logout,
|
||||
create,
|
||||
resetPassword,
|
||||
forgotPassword,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</Context.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
type UseAuth<T = User> = () => AuthContext // eslint-disable-line no-unused-vars
|
||||
|
||||
export const useAuth: UseAuth = () => useContext(Context)
|
||||
34
examples/auth/next-app/app/_providers/Auth/rest.ts
Normal file
34
examples/auth/next-app/app/_providers/Auth/rest.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
import type { User } from '../../payload-types'
|
||||
|
||||
export const rest = async (
|
||||
url: string,
|
||||
args?: any, // eslint-disable-line @typescript-eslint/no-explicit-any
|
||||
options?: RequestInit,
|
||||
): Promise<User | null | undefined> => {
|
||||
const method = options?.method || 'POST'
|
||||
|
||||
try {
|
||||
const res = await fetch(url, {
|
||||
method,
|
||||
...(method === 'POST' ? { body: JSON.stringify(args) } : {}),
|
||||
credentials: 'include',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
...options?.headers,
|
||||
},
|
||||
...options,
|
||||
})
|
||||
|
||||
const { errors, user } = await res.json()
|
||||
|
||||
if (errors) {
|
||||
throw new Error(errors[0].message)
|
||||
}
|
||||
|
||||
if (res.ok) {
|
||||
return user
|
||||
}
|
||||
} catch (e: unknown) {
|
||||
throw new Error(e as string)
|
||||
}
|
||||
}
|
||||
31
examples/auth/next-app/app/_providers/Auth/types.ts
Normal file
31
examples/auth/next-app/app/_providers/Auth/types.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
import type { User } from '../../payload-types'
|
||||
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
export type ResetPassword = (args: {
|
||||
password: string
|
||||
passwordConfirm: string
|
||||
token: string
|
||||
}) => Promise<User>
|
||||
|
||||
export type ForgotPassword = (args: { email: string }) => Promise<User> // eslint-disable-line no-unused-vars
|
||||
|
||||
export type Create = (args: {
|
||||
email: string
|
||||
password: string
|
||||
firstName: string
|
||||
lastName: string
|
||||
}) => Promise<User> // eslint-disable-line no-unused-vars
|
||||
|
||||
export type Login = (args: { email: string; password: string }) => Promise<User> // eslint-disable-line no-unused-vars
|
||||
|
||||
export type Logout = () => Promise<void>
|
||||
|
||||
export interface AuthContext {
|
||||
user?: User | null
|
||||
setUser: (user: User | null) => void // eslint-disable-line no-unused-vars
|
||||
logout: Logout
|
||||
login: Login
|
||||
create: Create
|
||||
resetPassword: ResetPassword
|
||||
forgotPassword: ForgotPassword
|
||||
}
|
||||
41
examples/auth/next-app/app/_utilities/getMeUser.ts
Normal file
41
examples/auth/next-app/app/_utilities/getMeUser.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
import { cookies } from 'next/headers'
|
||||
import { redirect } from 'next/navigation'
|
||||
|
||||
import type { User } from '../payload-types'
|
||||
|
||||
export const getMeUser = async (args?: {
|
||||
nullUserRedirect?: string
|
||||
validUserRedirect?: string
|
||||
}): Promise<{
|
||||
user: User
|
||||
token: string | undefined
|
||||
}> => {
|
||||
const { nullUserRedirect, validUserRedirect } = args || {}
|
||||
const cookieStore = cookies()
|
||||
const token = cookieStore.get('payload-token')?.value
|
||||
|
||||
const meUserReq = await fetch(`${process.env.NEXT_PUBLIC_PAYLOAD_URL}/api/users/me`, {
|
||||
headers: {
|
||||
Authorization: `JWT ${token}`,
|
||||
},
|
||||
})
|
||||
|
||||
const {
|
||||
user,
|
||||
}: {
|
||||
user: User
|
||||
} = await meUserReq.json()
|
||||
|
||||
if (validUserRedirect && meUserReq.ok && user) {
|
||||
redirect(validUserRedirect)
|
||||
}
|
||||
|
||||
if (nullUserRedirect && (!meUserReq.ok || !user)) {
|
||||
redirect(nullUserRedirect)
|
||||
}
|
||||
|
||||
return {
|
||||
user,
|
||||
token,
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user