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

useCwaComponent

The recommended entry point for all CWA display components — fetch resource data, expose to the admin manager, and compose plugins for collections, images, or custom behaviour.

The recommended composable for all CWA display components. Wraps useCwaResource and adds a typed plugin system so collection, image, and custom behaviours can be composed together without nesting.

const props = defineProps<IriProp>()
const { resource, exposeMeta } = useCwaComponent(props)
defineExpose(exposeMeta)

Signature

useCwaComponent(
  props: IriProp,
  plugins?: CwaResourcePlugin[],
  ops?: {
    name?: string
    styles?: {
      multiple?: boolean
      classes: Record<string, string[]>
    }
    manager?: {
      disabled?: boolean
    }
    autoClass?: boolean   // default true — auto-applies uiClassNames to the root element
  }
)

props — pass the raw defineProps<IriProp>() result directly (no toRef needed).

plugins — optional array of plugin factory results (see below).

ops — same options as useCwaResource: style variants, UI variant name, manager opt-out.

Return values

const { resource, exposeMeta, $cwa, getCurrentStyleName, ...pluginReturns } = useCwaComponent(props)
ReturnTypePurpose
resourceComputedRef<Resource | null | undefined>Reactive resource ref — access data on .value?.data
exposeMetaobjectPass directly to defineExpose
$cwaCwaComposableAuth, resources, UI state
getCurrentStyleName(data) => string | undefinedRead the currently selected style name
uiClassNamesComputedRef<string[] | undefined>The active style classes — auto-applied to the root element by default
...pluginReturnsmerged objectAll return values from provided plugins

defineExpose(exposeMeta) is required — without it the admin manager cannot select this component.

Basic component

<template>
  <div v-if="resource?.data">
    <h2>{{ resource.data.title }}</h2>
  </div>
</template>

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

const props = defineProps<IriProp>()
const { resource, exposeMeta } = useCwaComponent(props)
defineExpose(exposeMeta)
</script>

resource.value states:

  • undefined — loading
  • null — not found
  • object — data available

Plugins

Plugins extend useCwaComponent with additional state and methods. Pass an array of plugin factory results as the second argument. Return values from all plugins are merged and destructured alongside the base returns.

withCollection()

For components backed by the built-in Collection API resource. Returns pagination state and navigation helpers.

import { useCwaComponent, withCollection } from '#imports'

const props = defineProps<IriProp>()
const {
  resource,
  exposeMeta,
  collectionItems,
  isLoadingCollection,
  totalPages,
  pageModel,
  goToNextPage,
  goToPreviousPage,
  changePage,
  resolveResourceLink,
} = useCwaComponent(props, [withCollection()])
defineExpose(exposeMeta)

Replaces the deprecated useCwaCollectionResource.

withImage(imageOps?)

For components backed by a PHP entity with #[Silverback\Uploadable]. Returns image URL, load state, and media object data.

import { useCwaComponent, withImage } from '#imports'

const props = defineProps<IriProp>()
const {
  resource,
  exposeMeta,
  contentUrl,
  displayMedia,
  handleLoad,
  loaded,
  mediaObjects,
} = useCwaComponent(props, [withImage()])
defineExpose(exposeMeta)

Options:

OptionDescription
imagineFilterNameImagine filter to apply to the image URL
imageRefTemplate ref pointing to the <img> element (auto-created if omitted)
filePropName of the PHP entity property holding the file (default: file)

Replaces the deprecated useCwaImageResource.

Combining plugins

Multiple plugins can be composed in a single call — something the old useCwaCollectionResource / useCwaImageResource pattern could not do:

const { resource, exposeMeta, collectionItems, contentUrl } = useCwaComponent(
  props,
  [withCollection(), withImage()]
)
defineExpose(exposeMeta)

Style variants

const { resource, exposeMeta, getCurrentStyleName } = useCwaComponent(props, [], {
  styles: {
    multiple: false,
    classes: {
      'Default': [],
      'Filled': ['bg-blue-600', 'text-white', 'px-6', 'py-2', 'rounded-md'],
      'Outlined': ['border', 'border-blue-600', 'text-blue-600', 'px-6', 'py-2'],
    }
  }
})

const currentStyleName = computed(() => {
  if (!resource.value?.data) return undefined
  return getCurrentStyleName(resource.value.data)
})

The style names appear in the admin "Style" selector. The selected classes are automatically applied to the component's root element — no :class binding needed.

Applying classes to an inner element

If you want the style classes on an inner element rather than the root, opt out of auto-apply and bind uiClassNames manually:

const { resource, exposeMeta, uiClassNames } = useCwaComponent(props, [], {
  autoClass: false,
  styles: { ... }
})
<template>
  <div>
    <a :class="uiClassNames">{{ resource?.data?.label }}</a>
  </div>
</template>

Writing a custom plugin

A plugin is a function that receives { iri, resource, $cwa } and returns any object:

import type { CwaResourcePlugin } from '#cwa/composables/cwa-component'

const withWordCount = (): CwaResourcePlugin<{ wordCount: ComputedRef<number> }> => {
  return ({ resource }) => {
    const wordCount = computed(() => {
      const body = resource.value?.data?.body ?? ''
      return body.split(/\s+/).filter(Boolean).length
    })
    return { wordCount }
  }
}

// Usage
const { resource, exposeMeta, wordCount } = useCwaComponent(props, [withWordCount()])