v3.78.0

payloadcms/payloadv3.78.0Feb 27, 2026by denolfe

AI Summary

Introduced a TypeScript plugin for import paths, stabilized the trash feature with granular delete access control, and added configurable widget fields to dashboards. The MCP plugin also moved out of beta.

Key Highlights

  • New `@payloadcms/typescript-plugin` validates PayloadComponent import paths
  • Trash feature moved out of beta with granular delete access control
  • Dashboard widgets can now declare configurable fields
  • MCP plugin moved out of beta and supports virtual field filtering

Breaking Changes

  • Renamed `Widget.ComponentPath` to `Widget.Component` and typed as `PayloadComponent`

New Features

  • TypeScript plugin for component paths
  • Widget fields
  • Trash feature stabilization
  • MCP plugin stable release
  • Markdown transformer for upload nodes

Full Release Notes

## [v3.78.0](https://github.com/payloadcms/payload/compare/v3.77.0...v3.78.0) (2026-02-27)


### πŸš€ Features

* typescript plugin for import paths ([#15779](https://github.com/payloadcms/payload/issues/15779)) ([7639664](https://github.com/payloadcms/payload/commit/7639664))
* move trash out of beta and delete access can now be limited to trash only ([#15210](https://github.com/payloadcms/payload/issues/15210)) ([2347cd9](https://github.com/payloadcms/payload/commit/2347cd9))
* **next, ui:** widget fields ([#15700](https://github.com/payloadcms/payload/issues/15700)) ([0a123b5](https://github.com/payloadcms/payload/commit/0a123b5))
* **plugin-mcp:** out of beta ([#15711](https://github.com/payloadcms/payload/issues/15711)) ([1b19d5f](https://github.com/payloadcms/payload/commit/1b19d5f))
* **plugin-mcp:** ignore virtual fields in create and update operations ([#15680](https://github.com/payloadcms/payload/issues/15680)) ([cc8f888](https://github.com/payloadcms/payload/commit/cc8f888))
* **richtext-lexical:** add markdown transformer for upload nodes ([#15630](https://github.com/payloadcms/payload/issues/15630)) ([4af5a85](https://github.com/payloadcms/payload/commit/4af5a85))
* **ui:** adds dashed button style ([#15728](https://github.com/payloadcms/payload/issues/15728)) ([d570787](https://github.com/payloadcms/payload/commit/d570787))
* **ui:** make query-presets editable from the form view ([#15657](https://github.com/payloadcms/payload/issues/15657)) ([575f8e9](https://github.com/payloadcms/payload/commit/575f8e9))

---

### Feature Details

**TypeScript Plugin for Component Paths** - New `@payloadcms/typescript-plugin` validates PayloadComponent import paths directly in your IDE. It checks that referenced files and exports exist, provides autocomplete for file paths and export names, supports go-to-definition on component path strings, and understands all Payload path conventions including absolute paths, relative paths, tsconfig aliases, and package imports. [#15779](https://github.com/payloadcms/payload/pull/15779)

https://github.com/user-attachments/assets/2a8a8f73-a8fd-4e79-9dc4-795d817b5b75

```bash
pnpm add -D @payloadcms/typescript-plugin
```

```json
{
  "compilerOptions": {
    "plugins": [{ "name": "next" }, { "name": "@payloadcms/typescript-plugin" }]
  }
}
```

**Trash Out of Beta with Granular Delete Access** - Trash is now a stable feature. Delete access control can now distinguish between trashing and permanently deleting β€” allowing you to permit users to soft-delete documents while restricting permanent deletion to admins. When `data.deletedAt` is being set, the operation is a trash; otherwise it's a permanent delete. [#15210](https://github.com/payloadcms/payload/pull/15210)

```ts
import type { CollectionConfig } from 'payload'

export const Posts: CollectionConfig = {
  slug: 'posts',
  trash: true,
  access: {
    delete: ({ req: { user }, data }) => {
      // Not logged in - no access
      if (!user) {
        return false
      }

      // Admins can do anything (trash or permanently delete)
      if (user.roles?.includes('admin')) {
        return true
      }

      // Regular users: check what operation they're attempting
      // If data.deletedAt is being set, it's a trash operation - allow it
      if (data?.deletedAt) {
        return true
      }

      // Otherwise it's a permanent delete - deny for non-admins
      return false
    },
  },
  fields: [
    // ...
  ],
}
```

**Widget Fields (next, ui)** - Dashboard widgets can now declare configurable fields, similar to Blocks. Widget data is editable from a new drawer UI when in dashboard editing mode. Full type generation is included β€” `WidgetInstance<T>` is generic with typed `data` and `width`, and `WidgetServerProps` is generic so widget components receive typed `widgetData`. [#15700](https://github.com/payloadcms/payload/pull/15700)

https://github.com/user-attachments/assets/b24d3635-8635-4d5f-84af-a6dcf801aa4f

```ts
import { buildConfig } from 'payload'

export default buildConfig({
  admin: {
    dashboard: {
      widgets: [
        {
          slug: 'sales-summary',
          ComponentPath: './components/SalesSummary.tsx#default',
          fields: [
            { name: 'title', type: 'text' },
            {
              name: 'timeframe',
              type: 'select',
              options: ['daily', 'weekly', 'monthly', 'yearly'],
            },
            { name: 'showTrend', type: 'checkbox' },
          ],
          minWidth: 'small',
          maxWidth: 'medium',
        },
      ],
    },
  },
})
```

```tsx
import type { WidgetServerProps } from 'payload'

import type { SalesSummaryWidget } from '../payload-types'

export default async function SalesSummaryWidgetComponent({
  widgetData,
}: WidgetServerProps<SalesSummaryWidget>) {
  const title = widgetData?.title ?? 'Sales Summary'
  const timeframe = widgetData?.timeframe ?? 'monthly'

  return (
    <div className="card">
      <h3>
        {title} ({timeframe})
      </h3>
    </div>
  )
}
```

**MCP Plugin Out of Beta (plugin-mcp)** - `@payloadcms/plugin-mcp` is now stable and ready for production use. [#15711](https://github.com/payloadcms/payload/pull/15711)

**Virtual Field Filtering in MCP (plugin-mcp)** - Virtual fields (`virtual: true`) are now automatically stripped from MCP tool input schemas and filtered from parsed data before `create`, `update`, and `updateGlobal` operations. This prevents non-stored fields from appearing as accepted MCP parameters. [#15680](https://github.com/payloadcms/payload/pull/15680)

**Markdown Transformer for Upload Nodes (richtext-lexical)** - Upload nodes are now properly converted when using `convertLexicalToMarkdown`. Previously, upload nodes were silently dropped during markdown conversion. Now populated image uploads output `![alt text](/uploads/image.jpg)`, non-image uploads output link syntax, and non-populated uploads output a reference placeholder so data is never lost. [#15630](https://github.com/payloadcms/payload/pull/15630)

**Dashed Button Style (ui)** - Adds a new `dashed` button style variant. Also replaces `box-shadow` with `border` on all buttons and fixes icon-only button padding. [#15728](https://github.com/payloadcms/payload/pull/15728)

<img width="2021" height="1261" alt="Button styles overview" src="https://github.com/user-attachments/assets/8d97fb19-51c3-46a3-9148-c54b15d49723" />

**Editable Query Presets from Form View (ui)** - Query presets can now be created and edited directly from the document form view using a full WhereBuilder, column picker, and groupBy selector β€” no longer requiring the list view to build queries first. [#15657](https://github.com/payloadcms/payload/pull/15657)

https://github.com/user-attachments/assets/d25b447b-c942-47ae-8878-47bd17a8a7fb

---

### πŸ› Bug Fixes

* getFieldsToSign crashes when user missing group/tab fields ([#15775](https://github.com/payloadcms/payload/issues/15775)) ([9f0c101](https://github.com/payloadcms/payload/commit/9f0c101))
* improve mobile touch support for dnd ([#15771](https://github.com/payloadcms/payload/issues/15771)) ([418bb92](https://github.com/payloadcms/payload/commit/418bb92))
* prevent silent data overwrites on concurrent edits ([#15749](https://github.com/payloadcms/payload/issues/15749)) ([7a3f43f](https://github.com/payloadcms/payload/commit/7a3f43f))
* preserve block metadata in mergeLocalizedData and filterDataToSelectedLocales ([#15715](https://github.com/payloadcms/payload/issues/15715)) ([6557292](https://github.com/payloadcms/payload/commit/6557292))
* return 400 for malformed JSON request bodies ([#15706](https://github.com/payloadcms/payload/issues/15706)) ([4861fa1](https://github.com/payloadcms/payload/commit/4861fa1))
* globals not updating updatedAt when saving drafts ([#15764](https://github.com/payloadcms/payload/issues/15764)) ([df17cb1](https://github.com/payloadcms/payload/commit/df17cb1))
* return early if pasteURL is not defined ([#15748](https://github.com/payloadcms/payload/issues/15748)) ([23d52a0](https://github.com/payloadcms/payload/commit/23d52a0))
* prevent req.file leak between sequential duplicate() calls on upload collections ([#15620](https://github.com/payloadcms/payload/issues/15620)) ([2baea2e](https://github.com/payloadcms/payload/commit/2baea2e))
* sanitize filenames in storage adapters ([#15746](https://github.com/payloadcms/payload/issues/15746)) ([45bd2f1](https://github.com/payloadcms/payload/commit/45bd2f1))
* throw error for unknown query operators ([#15739](https://github.com/payloadcms/payload/issues/15739)) ([08226db](https://github.com/payloadcms/payload/commit/08226db))
* preserve locale data in unnamed groups with localizeStatus ([#15658](https://github.com/payloadcms/payload/issues/15658)) ([38b8c68](https://github.com/payloadcms/payload/commit/38b8c68))
* **next:** conditionally query snapshot field based on localization ([#15693](https://github.com/payloadcms/payload/issues/15693)) ([d5706ee](https://github.com/payloadcms/payload/commit/d5706ee))
* **plugin-import-export:** update docs on jobs and basic usage as well as visibility ([#15695](https://github.com/payloadcms/payload/issues/15695)) ([a40210c](https://github.com/payloadcms/payload/commit/a40210c))
* **plugin-mcp:** use inline block schemas in JSON output ([#15675](https://github.com/payloadcms/payload/issues/15675)) ([a66e844](https://github.com/payloadcms/payload/commit/a66e844))
* **plugin-multi-tenant:** hasMany tenant fields double-wrap arrays in filterOptions ([#15709](https://github.com/payloadcms/payload/issues/15709)) ([aaddeac](https://github.com/payloadcms/payload/commit/aaddeac))
* **plugin-multi-tenant:** return false instead of query when no tenants ([#15679](https://github.com/payloadcms/payload/issues/15679)) ([f5a5bd8](https://github.com/payloadcms/payload/commit/f5a5bd8))
* **richtext-lexical:** bump acorn to resolve type mismatch with transitive dep ([#15791](https://github.com/payloadcms/payload/issues/15791)) ([801e851](https://github.com/payloadcms/payload/commit/801e851))
* **richtext-lexical:** link markdown regex should not match similar looking image markdown ([#15713](https://github.com/payloadcms/payload/issues/15713)) ([0a0afb0](https://github.com/payloadcms/payload/commit/0a0afb0))
* **richtext-lexical:** use headingLevel in danish heading label ([#15685](https://github.com/payloadcms/payload/issues/15685)) ([ad4c0f6](https://github.com/payloadcms/payload/commit/ad4c0f6))
* **richtext-lexical:** strip server-only properties from blocks in lexical client schema map ([#15756](https://github.com/payloadcms/payload/issues/15756)) ([c05ace2](https://github.com/payloadcms/payload/commit/c05ace2))
* **templates:** ecommerce find my order access functionality to use email ([#15736](https://github.com/payloadcms/payload/issues/15736)) ([b317eaa](https://github.com/payloadcms/payload/commit/b317eaa))
* **ui:** array field add button margin not applying consistently ([#15773](https://github.com/payloadcms/payload/issues/15773)) ([ee298f5](https://github.com/payloadcms/payload/commit/ee298f5))
* **ui:** respect custom Cell components on richText fields in list view ([#15762](https://github.com/payloadcms/payload/issues/15762)) ([139cf3e](https://github.com/payloadcms/payload/commit/139cf3e))
* **ui:** use correct translation key for collection version restore modal ([#15757](https://github.com/payloadcms/payload/issues/15757)) ([c1892eb](https://github.com/payloadcms/payload/commit/c1892eb))
* **ui:** encode HTML special characters in version diff view ([#15747](https://github.com/payloadcms/payload/issues/15747)) ([30fee83](https://github.com/payloadcms/payload/commit/30fee83))
* **ui:** flash of border on list selection buttons ([#15735](https://github.com/payloadcms/payload/issues/15735)) ([7f3c6c8](https://github.com/payloadcms/payload/commit/7f3c6c8))

### πŸ›  Refactors

* extract regex escape pattern into shared utility ([#15676](https://github.com/payloadcms/payload/issues/15676)) ([8ce389e](https://github.com/payloadcms/payload/commit/8ce389e))
* **plugin-mcp:** reduced code and de-duplication ([#15705](https://github.com/payloadcms/payload/issues/15705)) ([039e6c9](https://github.com/payloadcms/payload/commit/039e6c9))

### 🎨 Styles

* **richtext-lexical:** lists and quotes have inconsistent letter spacing ([#15682](https://github.com/payloadcms/payload/issues/15682)) ([f2397a8](https://github.com/payloadcms/payload/commit/f2397a8))

### πŸ§ͺ Tests

* fix flaky versions tests timing out on link clicks before hydration ([#15790](https://github.com/payloadcms/payload/issues/15790)) ([02da3fd](https://github.com/payloadcms/payload/commit/02da3fd))
* fix flaky lexical code block test with incorrect error expectations ([#15776](https://github.com/payloadcms/payload/issues/15776)) ([4e3e780](https://github.com/payloadcms/payload/commit/4e3e780))
* trash e2e list view clicks timing out in CI ([#15778](https://github.com/payloadcms/payload/issues/15778)) ([fd81fd5](https://github.com/payloadcms/payload/commit/fd81fd5))
* fix flaky localization tests with race conditions ([#15777](https://github.com/payloadcms/payload/issues/15777)) ([bda5d0d](https://github.com/payloadcms/payload/commit/bda5d0d))
* bulk upload button clicks timeout in CI ([#15703](https://github.com/payloadcms/payload/issues/15703)) ([8228a7d](https://github.com/payloadcms/payload/commit/8228a7d))
* field update access control test times out locally ([#15691](https://github.com/payloadcms/payload/issues/15691)) ([78e8e14](https://github.com/payloadcms/payload/commit/78e8e14))
* fix flaky account unlock test in postgres-uuid and custom-schema ([#15696](https://github.com/payloadcms/payload/issues/15696)) ([e3a76e3](https://github.com/payloadcms/payload/commit/e3a76e3))
* fix flaky lexical e2e navigation timeouts and drawer clicks ([#15694](https://github.com/payloadcms/payload/issues/15694)) ([9ca5cda](https://github.com/payloadcms/payload/commit/9ca5cda))
* fix flaky access-control bulk delete tests failing due to timing issues ([#15692](https://github.com/payloadcms/payload/issues/15692)) ([558345b](https://github.com/payloadcms/payload/commit/558345b))
* fix flaky drawer open and column sort timing issues ([#15677](https://github.com/payloadcms/payload/issues/15677)) ([00f763b](https://github.com/payloadcms/payload/commit/00f763b))

### πŸ“ Templates

* fix cloudflare logger error in with-cloudflare-d1 template ([#15752](https://github.com/payloadcms/payload/issues/15752)) ([8791a72](https://github.com/payloadcms/payload/commit/8791a72))
* ecommerce add missing access control on collections ([#15744](https://github.com/payloadcms/payload/issues/15744)) ([c74d91d](https://github.com/payloadcms/payload/commit/c74d91d))
* fix ecommerce webhooks url and update docs on using stripe webhooks ([#15681](https://github.com/payloadcms/payload/issues/15681)) ([677596c](https://github.com/payloadcms/payload/commit/677596c))

### 🏑 Chores

* make test:e2e agent-friendly with --grep support and dev server reuse ([#15755](https://github.com/payloadcms/payload/issues/15755)) ([929203c](https://github.com/payloadcms/payload/commit/929203c))
* **deps:** bump next.js canary in monorepo ([#15708](https://github.com/payloadcms/payload/issues/15708)) ([e3d8a94](https://github.com/payloadcms/payload/commit/e3d8a94))
* **plugin-mcp:** improve schema sanitization and test coverage ([#15704](https://github.com/payloadcms/payload/issues/15704)) ([7e20b5c](https://github.com/payloadcms/payload/commit/7e20b5c))
* **templates:** bump versions to 3.77.0 ([#15678](https://github.com/payloadcms/payload/issues/15678)) ([e26df27](https://github.com/payloadcms/payload/commit/e26df27))

### 🀝 Contributors

- Elliot DeNolf (@denolfe)
- Alessio Gravili (@AlessioGr)
- Patrik (@PatrikKozak)
- Sean Zubrickas (@zubricks)
- Benedikt RΓΆtsch (@axe312ger)
- Shobhit Singhal (@shobhitsinghal624)
- The Mavik (@themavik)
- Divyesh Jain (@divyesh123-jain)
- Paul (@paulpopus)
- German Jablonski (@GermanJablo)
- Jens Becker (@jhb-dev)
- Jarrod Flesch (@JarrodMFlesch)
- Jessica Rynkar (@JessRynkar)
- Kendell (@kendelljoseph)
- Varun Chawla (@veeceey)