2.0.0 (2023-10-09)
Features
- New database adapter pattern
- Official Postgres adapter released, built on Drizzle ORM
- Database transactions added
- Full, first-party migration support added
- The admin UI has been redesigned to be more extensible and offer more horizontal real estate
- Admin UI sidebar is now collapsible
- Live preview added to admin UI, including usable frontend hooks
- New "Views" API added, which allows for custom sub-views on List and Edit views within Admin UI
- New bundler adapter pattern released
- Official Vite bundler released
- Offical Lexical rich text adapter released
- Lexical rich text editor now supports drag and drop of rich text elements
- Lexical rich text now supports Payload blocks directly within rich text editor
- Upload image cropping added
- Upload "focal point" controls added
- New "API" view added to Edit view(s), allowing for quick and customizable references to API response
- MongoDB draft querying has been significantly improved and is now much faster
- Arabic / RTL UI support added
- Locales can now be further configured to accept settings like
rtl
, human-friendly labels, etc. - The
tsconfig
path
pointing to your generated Payload types is no longer required for types to work
BREAKING CHANGES
⚠️ You now need to provide your Payload config with a database, a bundler, and a rich text adapter
Here's an example of a barebones Payload config, set up to work as 1.0 did:
import { mongooseAdapter } from "@payloadcms/db-mongodb";
import { slateEditor } from "@payloadcms/richtext-slate";
import { webpackBundler } from "@payloadcms/bundler-webpack";
import { buildConfig } from "payload/config";
export default buildConfig({
admin: {
bundler: webpackBundler(),
},
editor: slateEditor({}),
collections: [
// your collections here
],
db: mongooseAdapter({
url: process.env.DATABASE_URI,
}),
});
These new properties are all now required for Payload to function, and you will have to install each separate adapter that you use. Feel free to swap out any of the adapters with your choice (Lexical, Postgres, Vite, etc.)
Make sure to install the packages that you need. In the above example, you would need to install the following:
npm install --save @payloadcms/db-mongodb @payloadcms/richtext-slate @payloadcms/bundler-webpack
⚠️ Draft versions now require a latest: true
property to be set on the most recent draft in your _versions
collections(s)
We have a ready-to-go migration script for your versions from v1 to v2, and to use it, all you have to do is run the following commands:
1. First, make sure you have a payload
npm script in your package.json
{
"scripts": {
"payload": "cross-env PAYLOAD_CONFIG_PATH=src/payload.config.ts payload"
}
}
Adjust the PAYLOAD_CONFIG_PATH
to point to your Payload config file if necessary.
2. Create a migration, using the new Payload migration API
npm run payload migrate:create --file @payloadcms/db-mongodb/versions-v1-v2
The above command will output a migration file into your ./src/migrations
folder (default migrations location). It contains a migration script to automatically add a latest: true
flag to each of your newest drafts, for all draft-enabled collections. It works out of the box!
3. Run migrations
From there, you need to run migrations. Run the following command to execute your new migration:
npm run payload migrate
And you'll be all good!
⚠️ Array and block field validations now accept the full array of field data as their validation argument instead of the value.length
This change should only affect you if you have a custom array / block validation defined.
⚠️ For MongoDB, all models have been moved from the Payload object to the database adapter
For example, if you are leveraging Mongoose models directly, in 1.0, you would have accessed them via payload.collections[myCollectionSlug].Model
. Now, you can access the Mongoose model from payload.db.collections[myCollectionSlug]
.
Version models can be accessed from payload.db.versions[myEntitySlug]
, and the global model can be accessed via payload.db.globals
.
⚠️ User preferences data shape has changed, and you will lose preferences unless you manually migrate them to the new shape
We don't have a migration ready to go yet for user preferences, and this typically wouldn't be a big deal for most Payload projects. So don't let it stop you from updating unless you have a serious amount of user preferences that you'd like to keep. If so, we'll come up with a migration script for you and hook you up. Just reach out to us on Discord to ask for this.
⚠️ Node 16 is now the minimum required node version and Node 14 is no longer supported
Pretty self-explanatory on this one. Node 14 is old.
⚠️ The Pino logger has been updated, which may require you to make changes to your Pino config if you have a custom one
If you don't have anything custom with the Pino logger, this does not apply to you.
⚠️ Transactions are now enabled by default if your database supports them
MongoDB requires a replica set for transactions to work, so they likely are not going to work for you unless you do indeed have a replica set configured. But if you do, transactions will now instantly work for all internal Payload operations.
This means that in some fringe cases, if you are creating a doc and then instantly trying to update it within an afterChange
hook, the newly created doc may not truly exist yet. This should not cause any problems for 99% of projects, but if you think this applies to you, might be good to double-check through your code.
To avoid any issues, you can pass the req.transactionID
through to your Local API calls, so that your Local API calls are included as part of the parent transaction.
⚠️ Locales now have more functionality, and in some places, you might need to update custom code
Payload's locales have become more powerful and now allow you to customize more aspects per locale such as a human-friendly label and if the locale is RTL or not.
This means that certain functions now return a different shape, such as useLocale
. This hook used to return a string of the locale code you are currently editing, but it now returns an object with type of Locale
.
⚠️ Admin panel CSS classes may have changed
The revisions we've made in 2.0 required changes to both HTML and CSS within the admin panel. For this reason, if you were loading custom CSS into the admin panel to customize the look and feel, your stylesheets may need to be updated. If your CSS is targeting elements on the page using HTML selectors or class names, you may need to update these selectors based on the current markup. It may also be necessary to update your style definitions if the core Payload component you are targeting has undergone significant change.
In many cases, our classnames and structure have remained the same, but technically, this could be a breaking change.
⚠️ Custom admin views API has changed
These changes only affect apps that are using custom views via the admin.components.routes
config.
The type AdminRoute
was renamed to AdminViewConfig
. Simply update your instances with this new type and it will continue to work as expected. The properties of this type have not changed.
The admin.components.routes
config has been merged with admin.components.views
. Simply move your custom views to the views
object. The properties of the config have not changed. Here is an example:
Previous:
admin: {
components: {
routes: [
{
Component: MyCustomView,
path: "/custom-view",
},
];
}
}
Current:
admin: {
components: {
views: {
MyCustomView: {
Component: MyCustomView,
path: '/custom-view'
}
}]
}
}
⚠️ Rich text admin properties have moved
If you have customized the Slate rich text editor via admin.elements
or admin.leaves
properties, you now need to add your customizations to a slateEditor({ admin: {} })
property. The signatures are all the same, but you might have to move some properties around.
Previous:
const myRichTextField: Field = {
name: "content",
type: "richText",
admin: {
elements: [
"h1",
"link",
// etc
],
},
};
Current:
import { slateEditor } from "@payloadcms/richtext-slate";
const myRichTextField: Field = {
name: "content",
type: "richText",
editor: slateEditor({
// Move the admin property as shown below
admin: {
elements: [
"h1",
"link",
// etc
],
},
}),
};
⚠️ MongoDB connection options have been removed from payload.init
To pass connection options for MongoDB, you now need to pass them to db: mongooseAdapter({})
instead of passing them to payload.init()
.
⚠️ Some types have changed locations
If you are importing types from Payload, some of their locations may have changed. An example would be Slate-specific types being no longer exported from Payload itself—they are now exported from the @payloadcms/richtext-slate
package.
Recap
That's it! Most of these changes will be instantly apparent to you thanks to TypeScript, and many may not apply to you at all. But this list should be comprehensive and we will do our best to keep everything up-to-date here accordingly. There are inevitably some types that have changed locations, and similar things like that, but overall, you should be able to swing the breaking changes and get updated.
If you need a hand, reach out on Discord and we will hook you up!