decap-cms
decap-cms copied to clipboard
Ability to save image width and height when uploading an image
Is your feature request related to a problem? Please describe. Nowadays modern browsers use image 'width' and 'height' attributes to avoid layout shift https://twitter.com/jensimmons/status/1172922185570279425
Also Next.js image component requires 'width' and 'height' attributes to be provided https://nextjs.org/docs/api-reference/next/image#required-props
The problem is that it's pretty annoying to input these values manually. It would be very convenient if these values could be written automatically when image is uploaded.
Describe the solution you'd like In config in could look like
collections:
- label: Albums
name: albums
folder: content/albums
format: yaml
create: true
fields:
- { label: Title, name: title, widget: string }
- label: Images
name: images
widget: list
fields:
- { label: Image, name: image, widget: image }
- { label: Width, name: width, widget: string, value: '{{entry.images.image.width}}' }
- { label: Height, name: height, widget: string, value: '{{entry.images.image.height}}' }
Describe alternatives you've considered Currently I'm doing it using CMS preSave event, but it's pretty verbose
async function saveImageDimensions({ entry }: any) {
if (entry.get('data').get('collection') !== 'albums') {
return entry.get('data')
}
const images = entry.get('data').get('images').toJSON()
const mediaFiles = entry.get('mediaFiles').toJSON() as ReadonlyArray<{
path: string
url: string
}>
const mediaFilesObj: { [key: string]: string } = {}
for (let i = 0, l = mediaFiles.length; i < l; i += 1) {
const { url, path } = mediaFiles[i]
mediaFilesObj[path.replace(/^public(.*)/, '$1')] = url
}
const dimensions = await Promise.all<{
path: string
width: number
height: number
}>(
images.map(({ image }: { image: string }) => {
return new Promise((resolve) => {
const img = new Image()
img.onload = function handleLoad() {
resolve({
path: image,
width: img.naturalWidth,
height: img.naturalHeight,
})
}
img.src = mediaFilesObj[image]
})
}),
)
const dimensionsObj: {
[key: string]: { width: number; height: number }
} = {}
for (let i = 0, l = dimensions.length; i < l; i += 1) {
const { path, width, height } = dimensions[i]
dimensionsObj[path] = { width, height }
}
return entry.get('data').set(
'images',
entry
.get('data')
.get('images')
.map((item: any) => {
return item
.set('width', dimensionsObj[item.get('image')].width)
.set('height', dimensionsObj[item.get('image')].height)
}),
)
}
cms.registerEventListener({
name: 'preSave',
handler: saveImageDimensions,
})