---
title: Plugins
label: Overview
order: 10
desc: Plugins provide a great way to modularize Payload functionalities into easy-to-use enhancements and extensions of your Payload apps.
keywords: plugins, config, configuration, extensions, custom, documentation, Content Management System, cms, headless, javascript, node, react, express
---

Payload comes with a built-in Plugins infrastructure that allows developers to build their own modular and easily reusable sets of functionality.

<Banner type="success">
  Because we rely on a simple config-based structure, Payload plugins simply take in a user's
  existing config and return a modified config with new fields, hooks, collections, admin views, or
  anything else you can think of.
</Banner>

Writing plugins is no more complex than writing regular JavaScript. If you know how [spread syntax](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax) works and are up to speed with Payload concepts, writing a plugin will be a breeze.

**Example use cases:**

- Automatically sync data from a specific collection to HubSpot or a similar CRM when data is added or changes
- Add password-protection functionality to certain documents
- Add a full e-commerce backend to any Payload app
- Add custom reporting views to Payload's admin panel
- Encrypt specific collections' data
- Add a full form builder implementation
- Integrate all `upload`-enabled collections with a third-party file host like S3 or Cloudinary
- Add custom endpoints or GraphQL queries / mutations with any type of custom functionality that you can think of

### How to install plugins

The base Payload config allows for a `plugins` property which takes an `array` of [`Plugins`](https://github.com/payloadcms/payload/blob/main/packages/payload/src/config/types.ts).

```js
import { buildConfig } from 'payload/config'
// note: these plugins are not real (yet?)
import addLastModified from 'payload-add-last-modified'
import passwordProtect from 'payload-password-protect'
import { mongooseAdapter } from '@payloadcms/db-mongodb'
import { postgresAdapter } from '@payloadcms/db-postgres'

import { viteBundler } from '@payloadcms/bundler-vite'
import { webpackBundler } from '@payloadcms/bundler-webpack'


const config = buildConfig({
  bundler: webpackBundler() // or viteBundler(),
  collections: [
    {
      slug: 'pages',
      fields: [
        {
          name: 'title',
          type: 'text',
          required: true,
        },
        {
          name: 'content',
          type: 'richText',
          required: true,
        },
      ],
    },
  ],
  db: mongooseAdapter({}) // or postgresAdapter({})
  plugins: [
    // Many plugins require options to be passed.
    // In the following example, we call the function
    // and pass it options accordingly
    passwordProtect(['pages']),

    // This plugin takes no options and just
    // needs to be passed directly
    addLastModified,

    // ..
    // To understand how to use the plugins you're interested in,
    // consult their corresponding documentation
  ],
})

export default config
```

#### When Plugins are initialized

Payload Plugins are executed _after_ the incoming config is validated, but before it is sanitized and had default options merged in.

After all plugins are executed, the full config with all plugins will be sanitized.

## Simple example

Here is an example for how to automatically add a `lastModifiedBy` field to all Payload collections using a Plugin written in TypeScript.

```ts
import { Config, Plugin } from 'payload/config'

const addLastModified: Plugin = (incomingConfig: Config): Config => {
  // Find all incoming auth-enabled collections
  // so we can create a lastModifiedBy relationship field
  // to all auth collections
  const authEnabledCollections = incomingConfig.collections.filter((collection) =>
    Boolean(collection.auth),
  )

  // Spread the existing config
  const config: Config = {
    ...incomingConfig,
    collections: incomingConfig.collections.map((collection) => {
      // Spread each item that we are modifying,
      // and add our new field - complete with
      // hooks and proper admin UI config
      return {
        ...collection,
        fields: [
          ...collection.fields,
          {
            name: 'lastModifiedBy',
            type: 'relationship',
            relationTo: authEnabledCollections.map(({ slug }) => slug),
            hooks: {
              beforeChange: [
                ({ req }) => ({
                  value: req?.user?.id,
                  relationTo: req?.user?.collection,
                }),
              ],
            },
            admin: {
              position: 'sidebar',
              readOnly: true,
            },
          },
        ],
      }
    }),
  }

  return config
}

export default addLastModified
```

### Available Plugins

You can discover existing plugins by browsing the `payload-plugin` topic on [GitHub](https://github.com/topics/payload-plugin).

For maintainers building plugins for others to use, please add the topic to help others find it. If you would like one to be built by the core Payload team, [open a Feature Request](https://github.com/payloadcms/payload/discussions) in our GitHub Discussions board. We would be happy to review your code and maybe feature you and your plugin where appropriate.
