Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/fields/default-fields.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ The following fields are added to [upload-enabled collections](../upload/overvie
| ---------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `filename`, `mimeType`, `filesize`, `width`, `height`, `url`, `thumbnailURL` | Uploads are enabled. |
| `focalX`, `focalY` | Uploads are enabled, unless `focalPoint` is `false` and no Sharp `variants` or image adjustment options (`resizeOptions`, `formatOptions`, and so on) are configured. |
| `sizes` | Sharp `variants` are configured. Matching fields nested under `sizes` are also merged recursively. |
| `variants` | Sharp `variants` are configured. Matching fields nested under `variants` are also merged recursively. |

The `file` field is reserved and cannot be overridden.

Expand Down
52 changes: 51 additions & 1 deletion docs/migration-guide/v4.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -1863,7 +1863,7 @@ pnpm add @payloadcms/transformer-sharp

Run `npx @payloadcms/codemod --transform migrate-sharp-to-transformer` to migrate automatically. The codemod moves a top-level `sharp` dependency and inline collection Sharp options — including `crop` and `focalPoint` — into `sharpTransformer({ collections })` (writing `imageSizes` as `variants`), appends to an existing `upload.transformers` array if you already have one, and leaves anything it can't safely rewrite in place with a note — for example, a `collections` array built by a function call, or a collection defined outside the `buildConfig` call. Review those manually. For a config that already registers `sharpTransformer`, it only renames `imageSizes` to `variants` inside the existing `sharpTransformer` call and flags a leftover top-level `sharp` property; it doesn't move any remaining collection options.

Your `variants`, `crop`, and `focalPoint` are readable at `collection.upload.variants`/`.crop`/`.focalPoint` on the **sanitized** config (for example `req.payload.collections.media.config.upload.variants`). Code that read the sanitized `upload.imageSizes` in 3.x must read `upload.variants` instead. `sharpTransformer` writes a projection of what you configure back onto the sanitized collection at startup, so the Admin Panel, generated types, and the stored `sizes` shape on your documents are unchanged. For a collection configured on `sharpTransformer`, a `crop` or `focalPoint` set on the transformer replaces the collection's own value; one left unset there keeps the collection's. Neither `imageSizes` nor `variants` can be set on the collection at all.
Your `variants`, `crop`, and `focalPoint` are readable at `collection.upload.variants`/`.crop`/`.focalPoint` on the **sanitized** config (for example `req.payload.collections.media.config.upload.variants`). Code that read the sanitized `upload.imageSizes` in 3.x must read `upload.variants` instead. `sharpTransformer` writes a projection of what you configure back onto the sanitized collection at startup, so the Admin Panel and the rest of core see the same configuration. For a collection configured on `sharpTransformer`, a `crop` or `focalPoint` set on the transformer replaces the collection's own value; one left unset there keeps the collection's. Neither `imageSizes` nor `variants` can be set on the collection at all.

If you inject a custom Sharp build or version, pass it the same way:

Expand All @@ -1874,6 +1874,56 @@ If you inject a custom Sharp build or version, pass it the same way:

`sharpTransformer` can also resize images at request time (`/api/media/file/photo.png?width=400`). This is new in Payload 4 and **disabled by default**, so a migrated config keeps serving only the original file and your pre-generated `variants`. The codemod doesn't enable it. To opt in, pass `dynamic: true` or `dynamic: { collections: ['media'] }` — see [Dynamic (request-time) resizing](../upload/transformers#dynamic-request-time-resizing), and [Securing dynamic resizing](../upload/transformers#securing-dynamic-resizing) before enabling it on a publicly readable collection.

#### The stored `sizes` field on upload documents is renamed to `variants`

The group field that holds each generated image size on an upload document is now `variants` instead of `sizes`. Every place that reads or queries it changes:

```diff
- doc.sizes?.thumbnail?.url
+ doc.variants?.thumbnail?.url

- where: { 'sizes.thumbnail.filename': { equals: 'photo-400x300.png' } }
+ where: { 'variants.thumbnail.filename': { equals: 'photo-400x300.png' } }

- select: { sizes: { thumbnail: true } }
+ select: { variants: { thumbnail: true } }
```

GraphQL queries select `variants { thumbnail { url } }` instead of `sizes { thumbnail { url } }`, and regenerated types expose `variants?: { ... }` (and `variants?: T | { ... }` on the `*Select` types) in place of `sizes`. Update any frontend code that reads `media.sizes`, including values you pass to helpers such as `getBestFitFromSizes({ sizes: doc.variants })`.

Existing documents must be migrated. Create the predefined migration for your database adapter and run it:

<Banner type="warning">
Payload 4 uses `variants` as the top-level field for generated upload
variants. If an upload collection already defines a custom field named
`variants`, rename that field before you run the predefined migration. Choose
a new name, update your config, and write a custom migration that moves the
existing custom data from `variants` to the new field. Then run the predefined
`sizes-to-variants` migration. If legacy `sizes` data or columns and custom
`variants` data or columns are both present, the predefined migration stops
before it changes data or indexes. It cannot choose a new name or merge this
custom data for you.
</Banner>

```sh
# MongoDB
payload migrate:create --file @payloadcms/db-mongodb/sizes-to-variants
# Postgres
payload migrate:create --file @payloadcms/db-postgres/sizes-to-variants
# Vercel Postgres
payload migrate:create --file @payloadcms/db-vercel-postgres/sizes-to-variants
# SQLite
payload migrate:create --file @payloadcms/db-sqlite/sizes-to-variants
# Cloudflare D1
payload migrate:create --file @payloadcms/db-d1-sqlite/sizes-to-variants

payload migrate
```

**Postgres and SQLite (including Vercel Postgres and D1):** the columns are renamed in place from `sizes_<name>_<property>` to `variants_<name>_<property>`, on each upload collection's table and its versions table, along with their indexes. Use the predefined migration above **instead of** a plain `payload migrate:create` for this change. A generated migration would drop the `sizes_*` columns and add new `variants_*` columns, losing the stored image size data. The predefined migration also writes the schema snapshot, so the next generated migration starts from the renamed columns.

On MongoDB the migration renames `sizes` (and `version.sizes` on versions) with `$rename` and re-creates the indexes on those fields under the new path. Run the migration before the upgraded app writes to upload documents.

#### The file endpoint changes once any transformer is registered

Registering any transformer under `upload.transformers` — including `sharpTransformer()` with dynamic resizing off — routes every file request through the transformer-aware handler:
Expand Down
4 changes: 2 additions & 2 deletions docs/upload/overview.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ keywords: uploads, images, media, overview, documentation, Content Management Sy

**By simply enabling Upload functionality on a Collection, Payload will automatically transform your Collection into a robust file management / storage solution. The following modifications will be made:**

1. `filename`, `mimeType`, and `filesize` fields will be automatically added to your Collection. Optionally, if you declare image sizes as `variants` on [`sharpTransformer`](#image-sizes), a [`sizes`](#image-sizes) group will also be added containing auto-resized image sizes and filenames.
1. `filename`, `mimeType`, and `filesize` fields will be automatically added to your Collection. Optionally, if you declare image sizes as `variants` on [`sharpTransformer`](#image-sizes), a [`variants`](#image-sizes) group will also be added containing auto-resized image sizes and filenames.
1. The Admin Panel will modify its built-in `List` component to show a thumbnail for each upload within the List View
1. The Admin Panel will modify its `Edit` view(s) to add a new set of corresponding Upload UI which will allow for file upload
1. The `create`, `update`, and `delete` Collection operations will be modified to support file upload, re-upload, and deletion
Expand Down Expand Up @@ -227,7 +227,7 @@ export default buildConfig({
})
```

`sharpTransformer` writes a Sharp-agnostic projection of each configured `variants` entry (`name`, `admin`, and `generateImageName` only) back onto the collection's sanitized `upload.variants`, so the rest of this section — and the generated `sizes` shape on your documents — is unaffected by where the configuration is authored. Neither `imageSizes` nor `variants` can be set on the collection itself.
`sharpTransformer` writes a Sharp-agnostic projection of each configured `variants` entry (`name`, `admin`, and `generateImageName` only) back onto the collection's sanitized `upload.variants`, so the rest of this section — and the generated `variants` shape on your documents — is unaffected by where the configuration is authored. Neither `imageSizes` nor `variants` can be set on the collection itself.

#### Admin List View Options

Expand Down
2 changes: 1 addition & 1 deletion docs/upload/transformers.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,7 @@ export default buildConfig({
})
```

At startup, `sharpTransformer` writes a Sharp-agnostic projection of `variants` (name, `admin`, and `generateImageName` only), `crop`, and `focalPoint` back onto the collection's sanitized `upload` config, as `upload.variants`/`.crop`/`.focalPoint`. A `crop` or `focalPoint` set on `sharpTransformer` replaces the collection's own value; when it isn't set there, the collection's value is kept. So the Admin Panel, generated types, and the rest of core see the same sizes as before. The `sizes` shape on your documents is unchanged. `variants` exists only on the sanitized config: setting it (or the removed `imageSizes`) on the collection itself fails the build.
At startup, `sharpTransformer` writes a Sharp-agnostic projection of `variants` (name, `admin`, and `generateImageName` only), `crop`, and `focalPoint` back onto the collection's sanitized `upload` config, as `upload.variants`/`.crop`/`.focalPoint`. A `crop` or `focalPoint` set on `sharpTransformer` replaces the collection's own value; when it isn't set there, the collection's value is kept. So the Admin Panel, generated types, and the rest of core see the same sizes as before. Each generated size is stored on your documents under `variants.<name>`. `variants` exists only on the sanitized config: setting it (or the removed `imageSizes`) on the collection itself fails the build.

See [Image Sizes](./overview#image-sizes) for what each Sharp-specific image size option does, and [Crop and Focal Point Selector](./overview#crop-and-focal-point-selector) for the Admin Panel behavior these settings control.

Expand Down
1 change: 1 addition & 0 deletions packages/db-d1-sqlite/src/exports/migration-utils.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export { getBlocksToJsonMigrator } from '@payloadcms/drizzle'
export { migrateSqliteSizesToVariants as migrateSizesToVariants } from '@payloadcms/drizzle/sqlite'
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import type { DynamicMigrationTemplate } from 'payload'

import { buildDynamicPredefinedSizesToVariantsMigration } from '@payloadcms/drizzle'

export const dynamic: DynamicMigrationTemplate = buildDynamicPredefinedSizesToVariantsMigration({
packageName: '@payloadcms/db-d1-sqlite',
})
1 change: 1 addition & 0 deletions packages/db-mongodb/src/exports/migration-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@ export {
migrateLocalizeStatus,
} from '../predefinedMigrations/migrateLocalizeStatus.js'
export { migrateRelationshipsV2_V3 } from '../predefinedMigrations/migrateRelationshipsV2_V3.js'
export { migrateSizesToVariants } from '../predefinedMigrations/migrateSizesToVariants.js'
export { migrateVersionsV1_V2 } from '../predefinedMigrations/migrateVersionsV1_V2.js'
Loading
Loading