Skip to content

Instantly share code, notes, and snippets.

@HarleySalas
Last active June 28, 2025 11:03
Show Gist options
  • Save HarleySalas/ca24eea1301a94abaaf15311cbffdf14 to your computer and use it in GitHub Desktop.
Save HarleySalas/ca24eea1301a94abaaf15311cbffdf14 to your computer and use it in GitHub Desktop.
Payload Image Aspect Ratio
import type { CollectionBeforeOperationHook, FileData, Plugin, UploadCollectionSlug } from 'payload'
type AspectRatios = Record<string, number>
type Collections = Partial<Record<UploadCollectionSlug, AspectRatios>>
interface ImageAspectRatiosPluginArgs {
collections: Collections
enable?: boolean
}
const imageAspectRatiosPlugin = ({
collections,
enable = true,
}: ImageAspectRatiosPluginArgs): Plugin => {
return (config) => {
if (!enable) {
return config
}
const uploadCollections = collections
const uploadCollectionSlugs = Object.keys(collections)
config.collections = (config.collections || []).map((c) => {
const slug = c.slug as UploadCollectionSlug
if (uploadCollectionSlugs.includes(c.slug) && slug in uploadCollections) {
return {
...c,
hooks: {
...(c.hooks || {}),
beforeOperation: [
createBeforeOperationHook(uploadCollections[slug]!),
...(c.hooks?.beforeOperation || []),
],
},
}
} else {
return c
}
})
return config
}
}
type Dimensions = {
height: number
width: number
}
const createBeforeOperationHook = (aspectRatios: AspectRatios) => {
const beforeOperationHook: CollectionBeforeOperationHook = async ({
req,
args,
collection,
operation,
}) => {
if (operation == 'update' || operation == 'create') {
let originalDimensions: Dimensions | undefined = undefined
if (req.file) {
const sharp = req.payload.config.sharp
const metadata = await sharp(req.file.data).metadata()
if (metadata.width && metadata.height) {
originalDimensions = {
width: metadata.width,
height: metadata.height,
}
}
} else {
const duplicateFromID: string | number | undefined = args.duplicateFromID
if (duplicateFromID) {
const data = await req.payload.findByID({
collection: collection.slug as UploadCollectionSlug,
id: duplicateFromID,
select: {
width: true,
height: true,
},
})
originalDimensions = {
width: data.width!,
height: data.height!,
}
} else {
const data = args.data as FileData
originalDimensions = {
width: data.width,
height: data.height,
}
}
}
if (originalDimensions) {
const originalWidth = originalDimensions.width
const originalHeight = originalDimensions.height
const originalAspectRatio = originalWidth / originalHeight
collection.upload.imageSizes = (collection.upload.imageSizes || []).map((size) => {
if (size.name in aspectRatios) {
const desiredAspectRatio = aspectRatios[size.name]!
const dimensions =
desiredAspectRatio > originalAspectRatio
? {
width: originalWidth,
height: Math.round(originalWidth / desiredAspectRatio),
}
: {
width: Math.round(originalHeight * desiredAspectRatio),
height: originalHeight,
}
return {
...size,
...dimensions,
}
}
return size
})
}
}
return args
}
return beforeOperationHook
}
export const imageAspectRatio = imageAspectRatiosPlugin({
enable: true,
collections: {
media: {
square: 1.0,
portrait: 0.75,
landscape: 1.5,
},
},
})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment