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.
DraftGuides

Your First Layout

Create a layout component that wraps every page with your site header, footer, and navigation.

The layout is already part of the CWA data model — your job is to create the Vue file it renders through. You write one file; editors create instances with different content configurations (different nav links, different class names) and assign pages to whichever instance they want.

The key difference from a regular Nuxt layout: the navigation inside a CWA layout isn't hard-coded HTML. It's a component group — a named region that an editor fills with components at runtime. Change the nav without touching code.

IMAGE: Admin panel showing a layout in edit mode, with the navigation component group visible and a + hotspot for adding navigation link components

The File

Create app/cwa/layouts/primary.vue:

<template>
  <div>
    <header>
      <nav>
        <CwaComponentGroup
          reference="navigation"
          :location="$cwa.resources.layoutIri.value"
          :allowed-components="['/component/navigation_links']"
        />
      </nav>
    </header>

    <main>
      <slot />
    </main>

    <footer>
      <p>My Site &copy; {{ new Date().getFullYear() }}</p>
    </footer>
  </div>
</template>

<script setup lang="ts">
import { useCwa } from '#imports'
const $cwa = useCwa()
</script>

What's happening here

<slot /> — this is where the current page template renders. The module injects the page content here automatically.

useCwa() — gives you access to the current layout's resource data: layoutIri (the IRI of the active layout record) and layout.value?.data (its fields).

CwaComponentGroup — marks a region in the layout where components can be placed by editors.

  • reference — a stable name for this region (e.g. "navigation", "header-cta"). Must be unique within this layout.
  • location — the IRI of the resource that owns this group. For layout regions, always use $cwa.resources.layoutIri.value.
The location must be the IRI of a layout, page, or component — not a ComponentGroup or any other resource type. For layout regions: $cwa.resources.layoutIri.value. For page regions: props.iri (the IRI passed into the page template as a prop).
  • allowed-components — restricts which component types editors can add. /component/navigation_links limits this region to NavigationLink components only. Omit it to allow any component type.

Register in nuxt.config

// nuxt.config.ts
cwa: {
  layouts: {
    primary: {
      name: 'Primary Layout',
      classes: [
        { value: '', label: 'Default' },
        { value: 'bg-gray-50', label: 'Light Background' },
      ]
    }
  }
}

The classes array defines the style options an editor can choose from for this layout. The selected value is stored on the layout record as uiClassNames and you apply it to the root element of your Vue file:

<div :class="$cwa.resources.layout.value?.data?.uiClassNames">
Class names are intentionally restricted to this predefined list — defined by you and your designer. This keeps the design system intact when layouts are reused across client projects and prevents unintended style drift from ad-hoc values being entered by editors.

Create the Layout in the API

The Vue file defines how a layout looks, but it doesn't create a Layout record in the database. You need a record for the module to associate pages with it and for component groups (like the nav) to have an owner.

Naming convention: the uiComponent value is derived from the filename — convert to PascalCase and prefix with Cwa + resource type. primary.vueCwaLayoutPrimary. site-header.vueCwaLayoutSiteHeader. The same convention applies to page templates and components.

Via the admin

Go to /_cwa/layoutsCreate → set uiComponent to CwaLayoutPrimary. The record is created immediately.

IMAGE: Admin create layout form with the uiComponent field set to CwaLayoutPrimary

Via fixtures

Add the layout to your fixture scaffold and it will be created automatically on every doctrine:fixtures:load:

// api/src/DataFixtures/AppScaffold.php
public function build(CwaFixtureBuilder $cwa): void
{
    $cwa->layout('main', 'CwaLayoutPrimary');
    $cwa->flush();
}

Fixtures are the recommended approach for development — they give you a consistent, repeatable starting state, which is invaluable when switching feature branches or onboarding a new environment for a client. See Data Fixtures for the full scaffold API.

Next: Create a Page Template

With a layout in place, create a page template to define the content regions for individual pages.