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.
DraftCore Concepts

Components

The atomic content unit in CWA — what a component is, what it consists of, and the patterns you'll use most.

What Is a Component?

A component is the smallest unit of managed content on a page. Every piece of visible content — a heading, a block of rich text, an image, a call-to-action button, a conference card — is a component.

Components are:

  • Stored in the API as entities in your database (each type is a PHP class)
  • Rendered by the Nuxt module using matching Vue files
  • Edited inline in the browser by admins without leaving the page

A Component Has Two Sides

1. The PHP entity (the data)

#[Silverback\Publishable]
#[ApiResource(mercure: true)]
#[ORM\Entity]
class Title extends AbstractComponent
{
    use PublishableTrait;

    #[ORM\Column(nullable: true)]
    public ?string $title = null;
}

AbstractComponent gives it identity (IRI, UUID) and the uiComponent field. You add your own data fields.

2. The Vue component (the display)

<!-- app/cwa/components/Title/Title.vue -->
<template>
  <h1>{{ resource?.data?.title || 'No Title' }}</h1>
</template>

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

const props = defineProps<IriProp>()
const { getResource, exposeMeta } = useCwaResource(toRef(props, 'iri'))
const resource = getResource()
defineExpose(exposeMeta)
</script>

The module passes the component's IRI as a prop. useCwaResource fetches and subscribes to the resource. defineExpose(exposeMeta) wires the component into the admin manager.

The Admin Tab

An admin tab is a separate Vue file that renders inside the manager panel when this component is selected in edit mode.

<!-- app/cwa/components/Title/admin/Title.vue -->
<template>
  <CwaUiFormLabelWrapper label="Title">
    <CwaUiFormInput v-model="titleModel.model.value" />
  </CwaUiFormLabelWrapper>
</template>

<script setup lang="ts">
import { useCwaResourceManagerTab, useCwaResourceModel } from '#imports'

const { exposeMeta, iri } = useCwaResourceManagerTab({ name: 'Title' })
const titleModel = useCwaResourceModel<string>(iri, 'title')
defineExpose(exposeMeta)
</script>

The v-model binding sends a debounced PATCH to the API every time the admin types. No save button needed.

Component Patterns

Looking at real-world usage, a few patterns come up constantly:

PatternExampleWhat it needs
Text fieldTitle, TextBlockuseCwaResource + useCwaResourceModel
Image uploadImage, HeroSectionuseCwaImageResource + useCwaResourceUpload
Rich textHtmlContentuseCwaResource + TipTap editor in admin
LinkNavigationLinkText + Route/URL fields
Parent + childrenAccordion, GalleryCwaComponentGroup inside the parent component
Collection listingBlog listBuilt-in Collection component

Each of these is covered in detail in the Guides section with step-by-step instructions.

Alternative UI Variants

A component can have multiple visual representations. The same NavigationLink data might render as a plain text link, a filled button, or a YouTube icon depending on what an admin selects.

There are two ways to provide variants:

  1. Style classes — apply different Tailwind classes based on the selected uiClassNames
  2. UI directory — create app/cwa/components/Title/ui/AlternativeName.vue as a full alternative template

Both approaches are covered in the Alternative UI Variants guide.

Publishing

Most components use the #[Silverback\Publishable] annotation. This means:

  • Admins edit a draft version — the live site is unaffected
  • Changes become live when the admin clicks Publish
  • Mercure broadcasts the update so visitors see the new content in real-time

See Draft & Publish for details.