CWA 2 is a prerelease (2.0.0-alpha), heading for a beta. It runs production sites; configuration can still change between releases, always with upgrade notes. View versions and support
Core 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 editors 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/app/cwa/components/Title/Title.vue -->
<template>
  <h1>{{ resource?.data?.title || 'No Title' }}</h1>
</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>

The module passes the component's IRI as a prop. useCwaComponent reads the resource from the module's store — the module has already fetched it — and stays reactive to updates. 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/
├─Title.vuedisplay component
└─admin/
└─Title.vueadmin editing tab
<!-- app/app/cwa/components/Title/admin/Title.vue -->
<template>
  <CwaUiFormLabelWrapper label="Title">
    <CwaUiFormInput :model="titleModel" />
  </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.

VIDEO: Typing in a component's admin tab and the rendered component updating live on the page behind the manager panel, with no save step

Component Patterns

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

PatternExampleWhat it needs
Text fieldTitle, TextBlockuseCwaComponent + useCwaResourceModel, as in Your First Component
File uploadImage, HeroSectionuseCwaComponent + withFile() + useCwaResourceUpload, see Images & Media
Rich textHtmlContentuseCwaComponent + TipTap editor in admin, see HTML Content
LinkNavigationLinkText + Route/URL fields, see <CwaLink>
Parent + childrenAccordion, GalleryCwaComponentGroup inside the parent component, see <CwaComponentGroup>
Collection listingBlog listBuilt-in Collection component, see Collections & Pagination

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/app/cwa/components/Title/ui/AlternativeName.vue as a full alternative template. It registers as CwaComponentTitleUiAlternativeName, the name the admin stores in uiComponent when it is picked

Both approaches are covered in the Alternative UI Variants guide.

Publishing

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

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

See Draft & Publish for details.

Next: Draft & Publish

Draft & Publish explains how a publishable component's draft and published versions work.