The CWA is in heavy development
The CWA is still in alpha and not ready for production - some code and implementations are likely to change. If you would like to try out the CWA, please enjoy what we have provided and feel free to provide feedback, or get involved on GitHub.
DraftComponent

useCwaFileField

Resolve a single uploadable file field on a resource — content URL, display media, load state, and Imagine filter variants — named at the call site.

useCwaFileField resolves one uploadable file field on a component backed by a PHP entity with #[Silverback\Uploadable]. It returns the display media, its content URL, and load state for that field. Because you name the field at the call site, one component can wire several file fields without them colliding.

It's the standalone form of the withFile() plugin — the same resolver under the hood, called directly instead of through useCwaComponent's plugin array.

Renamed from useCwaImageResource (removed). The file APIs handle any uploadable file, not just images — imagineFilterName is an image-specific convenience.
import { useCwaFileField } from '#imports'
import type { IriProp } from '#cwa/composables/cwa-resource'

const props = defineProps<IriProp>()

const { contentUrl, displayMedia, handleLoad, loaded } = useCwaFileField(props, {
  imagineFilterName: 'thumbnail'
})

Signature

useCwaFileField(
  props: IriProp,                  // your component's props (with `iri`)
  fileOps?: {
    fileProp?: string              // the PHP #[UploadableField] property (default: 'file')
    imagineFilterName?: string     // Imagine filter variant to surface as displayMedia
    imageRef?: ShallowRef          // template ref for load detection (default: useTemplateRef(fileProp))
  }
)
  • Pass props directly — no need to toRef the iri; the composable resolves the resource itself.
  • fileProp — which uploadable field to read. Defaults to 'file', and also becomes the default template-ref name used to detect when an <img> has already loaded.
  • imagineFilterName — the LiipImagineBundle filter to surface as displayMedia/contentUrl (e.g. 'thumbnail', 'hero'). Omit for the original uploaded file.

Return values

ReturnTypePurpose
contentUrlComputedRef<string | undefined>URL of the (optionally filtered) file
displayMediaComputedRef<MediaFile | undefined>The media object to display — has contentUrl, mimeType, width, height; undefined until available
handleLoad() => voidCall on the <img>'s @load event
loadedRef<boolean>true once handleLoad() fires

Example

<template>
  <NuxtImg
    v-if="displayMedia"
    ref="file"
    :src="contentUrl"
    :width="displayMedia.width"
    :height="displayMedia.height"
    @load="handleLoad"
  />
  <div v-else class="skeleton h-48 w-full bg-gray-200 animate-pulse" />
</template>

<script setup lang="ts">
import { useCwaFileField } from '#imports'
import type { IriProp } from '#cwa/composables/cwa-resource'

const props = defineProps<IriProp>()
const { contentUrl, displayMedia, handleLoad } = useCwaFileField(props, { imagineFilterName: 'thumbnail' })
</script>

The ref="file" matches the default fileProp, letting the composable detect an already-cached image on mount. displayMedia stays undefined until the media exists, keeping the skeleton visible.

Multiple file fields

Call it once per field, naming each with fileProp:

const hero  = useCwaFileField(props, { fileProp: 'heroImage' })
const thumb = useCwaFileField(props, { fileProp: 'thumbnail' })
// hero.contentUrl, thumb.contentUrl, …

If you're already calling useCwaComponent, prefer the plugin form — useCwaComponent(props, [withFile({ fileProp: 'heroImage' }), withFile({ fileProp: 'thumbnail' })]) exposes the same data under a files map.