Module Setup
@cwa/nuxt is the front-end half of a CWA application. It connects your Nuxt application to a Symfony API powered by API Platform, providing automatic route resolution, a reactive resource store, real-time updates via Mercure, authentication pages, a full admin CMS panel, and composables for every content pattern.
You own your Vue templates — CWA drives what data they receive and in what order they render.
Installation
pnpm add @cwa/nuxt
The module requires these peer modules — add them all to your nuxt.config.ts:
// nuxt.config.ts
export default defineNuxtConfig({
modules: [
'@nuxt/ui',
'@nuxt/image',
'@nuxtjs/seo',
'@pinia/nuxt',
'@pinia-plugin-persistedstate/nuxt',
'nuxt-security',
'@cwa/nuxt' // must come after its peers
]
})
Minimum Configuration
Set your API URL via runtimeConfig. The browser URL is used client-side; the server URL is used during SSR (can be an internal Docker network address):
export default defineNuxtConfig({
runtimeConfig: {
public: {
cwa: {
apiUrl: 'http://api-internal', // server-side (SSR)
apiUrlBrowser: 'https://api.example.com' // client-side
}
}
}
})
In a Docker Compose setup these are typically different — the internal hostname resolves only within the Docker network.
Full cwa: Config Reference
See Nuxt Config for the complete reference. The key options at a glance:
export default defineNuxtConfig({
cwa: {
resources: { /* your CMS component types */ },
layouts: { /* your layout component types */ },
pages: { /* your page template component types */ },
pageData: { /* your PageData resource classes */ },
pagesDepth: 2, // nested page depth (default: 4)
siteConfig: { siteName: 'My App' }
}
})
What the Module Auto-Provides
You do not create these — the module ships them:
Pages (override by creating the same path in app/pages/):
/login— email + password login form/forgot-password— request a password reset email/reset-password/[username]/[token]— set a new password/verify-email/[username]/[token]— verify email on registration/confirm-new-email/[username]/[newEmail]/[token]— confirm email change/_cwa/*— the full admin panel
Middleware:
- Route resolution on every navigation — fetches the manifest and resolves the layout, page, and page data IRIs from the current URL
- Auth state hydration from the JWT cookie payload
Pinia stores (accessed via useCwa()):
resources— all fetched resource data, keyed by IRIauth— authentication state and methodsforms— form view state and submission helpersui— admin panel open/closed state, selected component, etc.siteConfig— site-wide settings fetched from the API
The useCwa() Composable
useCwa() (or $cwa in templates) is your entry point to everything the module manages:
const cwa = useCwa()
cwa.resources.layout.value // current layout resource
cwa.resources.page.value // current page resource
cwa.resources.pageData.value // current page data (dynamic pages)
cwa.auth.isSignedIn.value // boolean
cwa.auth.isAdmin.value // boolean (ROLE_ADMIN+)
cwa.auth.user.value // current user object
cwa.siteConfig.siteName.value // the site name from API settings
cwa.ui.isEditing.value // admin edit mode active
Mixing Your Own Pages with CWA {#mixing-your-own-pages-with-cwa}
CWA's catch-all route covers / and all paths. But you can still create regular Nuxt pages alongside it — your app/pages/ files take precedence over CWA's dynamic routing.
For pages you write yourself, use definePageMeta to control how much of CWA runs on that page:
Disable CWA route fetching
If your page has nothing to do with CWA content, disable the middleware entirely:
<!-- app/pages/status.vue -->
<script setup lang="ts">
definePageMeta({
cwa: { disabled: true }
})
</script>
CWA will not fetch a route manifest or resolve any resources. Auth state and useCwa() are still available.
Use a static Nuxt layout
If you want to use one of your own Nuxt layouts instead of CWA's resolved layout:
<script setup lang="ts">
definePageMeta({
cwa: { staticLayout: 'my-nuxt-layout' }
})
</script>
Use the CWA root layout on your own page
You can opt into cwa-root-layout to get the admin header and edit-mode overlay on a hand-coded page:
<script setup lang="ts">
definePageMeta({
layout: 'cwa-root-layout',
cwa: { disabled: true } // disable CWA content fetching but keep the layout
})
</script>
This is useful for pages like account settings or checkout flows that are not CMS-managed but should still appear inside the site shell and support admin edit mode for other parts of the page.
TypeScript Support
The module exports types for all component patterns:
import type { IriProp } from '@cwa/nuxt/runtime/composables'
// Every CWA component receives :iri as a prop
defineProps<IriProp>()
API resource data is dynamically shaped (resource.data is typed as Record<string, any> or similar). Under strict mode TypeScript may flag index access on these types as potentially undefined, even in contexts where you've checked for it. If you encounter these conflicts and don't want to add ? or type assertions throughout your component code, set strict: false in tsconfig.json. Alternatively, keep strict: true and use optional chaining (resource.data?.title) consistently.