Page Templates
A page template defines the structure of a page — the slots where admins add and manage content. The PHP Page entity's uiComponent field maps to your Vue file. For dynamic pages (isTemplate: true), many routes share one template but each route points to its own PageData record.
File Convention
app/cwa/pages/Primary.vue → CwaPagePrimary
app/cwa/pages/BlogDetail.vue → CwaPageBlogDetail
The file name (without .vue) must match the uiComponent value on the Page entity.
Every Page Component Receives :iri
The module passes the page IRI as a prop. Declare it:
import type { IriProp } from '@cwa/nuxt/runtime/composables'
defineProps<IriProp>()
The iri prop is the IRI of the current Page entity. Pass it as the location to any CwaComponentGroup.
Minimal Page Template
<!-- app/cwa/pages/Primary.vue -->
<template>
<main>
<CwaComponentGroup reference="content" :location="iri" />
</main>
</template>
<script setup lang="ts">
import type { IriProp } from '@cwa/nuxt/runtime/composables'
defineProps<IriProp>()
</script>
Multiple Content Regions
A real page usually has distinct zones — hero, main body, sidebar:
<template>
<div :class="uiClassNames">
<section class="hero">
<CwaComponentGroup reference="hero" :location="iri" />
</section>
<div class="grid lg:grid-cols-3 gap-8">
<div class="lg:col-span-2">
<CwaComponentGroup reference="main" :location="iri" />
</div>
<aside>
<CwaComponentGroup reference="sidebar" :location="iri" />
</aside>
</div>
</div>
</template>
<script setup lang="ts">
import type { IriProp } from '@cwa/nuxt/runtime/composables'
const props = defineProps<IriProp>()
const cwa = useCwa()
const page = computed(() => cwa.resources.page.value)
const uiClassNames = computed(() => page.value?.data?.uiClassNames ?? '')
</script>
Dynamic Pages: Accessing Page Data
When the page is a template (isTemplate: true), cwa.resources.pageData contains the current record's data:
const cwa = useCwa()
const pageData = computed(() => cwa.resources.pageData.value)
const headline = computed(() => pageData.value?.data?.headline)
const createdAt = computed(() => pageData.value?.data?.createdAt)
The module automatically sets the <title> and <meta name="description"> from pageData.data.title and pageData.data.metaDescription. You don't need to call useHead for the basics.
Registering Pages
// nuxt.config.ts
cwa: {
pages: {
Primary: {
name: 'Standard Page',
classes: [
{ value: '', label: 'Default' },
{ value: 'full-width', label: 'Full Width' }
]
},
BlogDetail: {
name: 'Blog Article',
classes: []
}
}
}
Page Load State
const isPageLoading = computed(() => cwa.resources.isLoading.value)
const progress = computed(() => cwa.resources.pageLoadProgress.value)
// { total: 8, complete: 5, percent: 62.5, resources: [...IRIs] }
Use progress.percent to drive a loading bar if you need one.
Nested Pages
If your pages have a parent–child URL structure — events listing → event detail, blog → post, docs section → page — add <CwaPage /> wherever the child should render inside the parent template. It works like <NuxtPage /> but for CWA's depth-aware hierarchy.
See <CwaPage /> for complete working examples, the inject key reference, and configuration.