Use CMS photos
Map CMS assets to PhotoItem and store dimensions before rendering.
Map CMS-specific fields once at the data boundary. The component should receive
one valid PhotoItem collection with stable IDs and real dimensions.
Map assets that include dimensions
<script setup lang="ts">
import type { PhotoItem } from '@lupinum/nuxt-photo/app'
type Asset = {
key: string
url: string
width: number
height: number
alternativeText?: string
title?: string
}
const { data: assets } = await useFetch<Asset[]>('/api/photos')
const photos = computed<PhotoItem[]>(() =>
(assets.value ?? []).map((asset) => ({
id: asset.key,
src: asset.url,
width: asset.width,
height: asset.height,
alt: asset.alternativeText,
caption: asset.title,
})),
)
</script>
<template>
<PhotoAlbum :photos="photos" layout="rows" />
</template>Use the CMS asset ID instead of the array index. Preserve the original pixel dimensions even when the image service returns a smaller display URL.
When the CMS has no dimensions
Calculate width and height during upload or trusted server-side ingestion, then store them with the asset. Do not download and measure images in the browser. Browser measurement makes layout depend on the network request Nuxt Photo is designed to avoid.
For an upload pipeline that already uses sharp:
import sharp from 'sharp'
export async function readImageDimensions(file: Buffer) {
const metadata = await sharp(file).metadata()
if (!metadata.width || !metadata.height) {
throw new Error('The uploaded image has no readable width or height.')
}
return {
width: metadata.width,
height: metadata.height,
}
}Call this function before the upload record is written. Save both values in the CMS or application database. If an image provider already exposes metadata, use that response instead of downloading the asset again.
For existing assets, run a one-time server-side backfill from a trusted source. Do not fetch arbitrary user-controlled URLs without the normal server-side request protections.
Keep URLs stable during SSR
Generate signed or expiring URLs before rendering. Store the final URL on the
photo for that request. Calling Date.now() or using random state inside an
image adapter can give the server and browser different markup.
Handle invalid records deliberately
The default validation policy throws when a photo is missing required data or uses a duplicate ID. Keep that behavior while integrating a new CMS.
Use validation="drop" only when the page may continue without invalid records,
and report the emitted event:
<PhotoAlbum :photos="photos" validation="drop" @invalid-photos="reportInvalidPhotos" />Check one portrait, one landscape, a duplicate ID, and a missing dimension before treating the CMS mapping as complete.