> [!NOTE]
> Stacked on #18411. Draft.
### What?
Upload documents now store generated image sizes under `variants`
instead of `sizes`, matching the `variants` config option.
```diff
- doc.sizes?.thumbnail?.url
+ doc.variants?.thumbnail?.url
```
### How?
- Renames the stored field and every place that reads or queries it,
across core, the admin UI, the storage plugins, tests, templates and
docs.
- Adds a `sizes-to-variants` migration for each database adapter
(MongoDB, Postgres, Vercel Postgres, SQLite, D1): `payload
migrate:create --file @payloadcms/db-<adapter>/sizes-to-variants`. On
SQL databases it renames the columns in place, so no data is lost.
- Stops the predefined migration before it changes data or indexes when
legacy `sizes` storage conflicts with custom `variants` storage.
- Escapes custom SQL identifiers, makes SQLite index replacement safe to
retry, and preserves and validates MongoDB index definitions, including
partial filters and text-index weights.
- Fixes the `db-postgres` and `db-vercel-postgres` builds, which shipped
`migration-utils` importing a predefined-migration helper that was only
published as `.mjs`, so importing
`@payloadcms/db-postgres/migration-utils` failed.
### Breaking changes
Payload 4 uses `variants` as the top-level field for generated upload
variants. An upload collection can already define a custom field with
this name.
If a collection has a custom `variants` field, rename it before running
the predefined migration:
1. Choose a new field name, such as `customVariants`.
2. Update the collection config to use the new name.
3. Write a custom migration that moves the existing custom data from
`variants` to the new field.
4. Run the predefined `sizes-to-variants` migration after the custom
data has moved.
5. Start Payload 4 only after both migrations succeed.
The predefined migration cannot choose a name or merge custom `variants`
data because Payload cannot determine the intended schema. It checks
MongoDB documents, SQL columns, and SQL snapshots for conflicts and
reports what must be moved before it can continue. A partly completed
migration that contains only expected generated variant fields remains
safe to retry.
### Testing
- `test/sizes-to-variants` covers the migration on MongoDB, SQLite and
Postgres, including camelCase variant names and running inside a
transaction, the way `payload migrate` does.
- Regression coverage includes custom `variants` collisions, quoted SQL
identifiers, SQLite retry and rerun behaviour, MongoDB partial filters
and index options, text indexes, and incompatible destination indexes.
- End to end on blank projects outside the monorepo, upgrading a real
Payload 3.90.2 project to this branch's packed packages:
1. Set up and seed with 3.x (`sharp` + `imageSizes`), using migrations
for SQL.
2. Upgrade and run the `migrate-sharp-to-transformer` codemod.
3. Run the `sizes-to-variants` migration.
4. For SQL, generate a plain v4 migration and check it creates v4's new
tables without dropping any `sizes`/`variants` columns.
5. Compare every stored size before and after, run runtime checks, then
roll back with `migrate:down`, migrate up again and compare.
| | Postgres | SQLite | MongoDB |
| ------------------------------- |
-------------------------------------------------------------------------------------------------------------
|
----------------------------------------------------------------------------------------------------
|
---------------------------------------------------------------------------------------------------
|
| Config | drafts + localization, camelCase/webp sizes, custom `dbName`,
a collection without sizes, upload relationship | versions without
drafts, crop/focal point, `generateImageName`, upload in an array,
`hasMany` upload | drafts + 3 locales, `generateImageName`, avif size,
upload in a block, polymorphic `hasMany` upload |
| Documents and versions compared | 15 | 10 | 16 |
Runtime checks after the upgrade cover `where` and `select` on
`variants.*`, generated files on disk, and new uploads. Index names
match the v4 schema.
### Notes
- The `templates/with-vercel-website` initial migration still creates
`sizes_*` columns and needs regenerating.
Written with AI
---------
Co-authored-by: Paul Popus <paul@payloadcms.com>
Note
Part 3 of 3 in a stacked PR. See #17827 for the full description, breaking changes and migration.
@payloadcms/transformer-sharp, its README, workspace registration and lockfileThis is the top of the stack, where CI is expected to pass.
Scope
@payloadcms/transformer-sharppackage and README: upload-time processing (variants, crop, focal point, format/trim/resize options, metadata) and opt-in dynamic resizingpayload(getImageResizeAction,sanitizeResizeConfig,optionallyAppendMetadata)build:transformer-sharpscript, roottsconfig.jsonreferencesharpremoved frompayload, bumped0.32.6→0.35.4; lockfile