Site Config & SEO
CWA's SEO layer is built on top of @nuxtjs/seo — the full Nuxt SEO bundle is available to you out of the box, including useSeoMeta, useSchemaOrg, robots meta, sitemap generation, and OG image support. CWA layers on top of it with API-driven site config and automatic per-page meta from your Page and PageData records.
Site-wide settings are stored in the API and cached in Pinia. Admins change them at /_cwa/settings — no deploy required.
Default Settings
The module ships with sensible defaults:
| Key | Default | Description |
|---|---|---|
siteName | 'CWA Web App' | Used in <title> concatenation |
fallbackTitle | true | Show siteName when a page has no title |
concatTitle | true | Append | siteName to page titles |
indexable | true | Whether search engines should index the site |
canonicalUrl | '' | Base URL for canonical tags |
sitemapEnabled | true | Include CWA routes in the generated sitemap |
sitemapXml | '' | Custom sitemap XML served at /__sitemap__/cwa-custom.xml and added to the sitemap index when non-empty. Validated on save — an invalid document aborts saveConfig() and returns totalConfigsChanged: 0 |
maintenanceModeEnabled | false | Serve a 503 to all non-admin visitors |
robotsText | '' | Custom robots.txt content |
robotsAllowNonSeoCrawlers | true | Allow non-SEO bots |
robotsAllowAiBots | true | Allow AI crawlers |
robotsRemoveSitemap | false | Omit the sitemap directive from robots.txt |
Accessing Site Config
Use cwa.siteConfig.config — it's a reactive store getter:
const cwa = useCwa()
const siteName = computed(() => cwa.siteConfig.config.value?.siteName)
const isMaintenanceMode = computed(() => cwa.siteConfig.config.value?.maintenanceModeEnabled)
const sitemapEnabled = computed(() => cwa.siteConfig.config.value?.sitemapEnabled)
Or use useCwaSiteConfig directly for utility functions:
import { useCwaSiteConfig } from '#imports'
const { defaultSiteConfig } = useCwaSiteConfig()
Saving Config Changes (Admin)
const result = cwa.siteConfig.saveConfig({
siteName: 'My Rebranded Site',
maintenanceModeEnabled: false,
robotsAllowAiBots: false
})
// result.totalConfigsChanged — number of keys that actually changed
// Only changed keys are PATCHed to the API
Per-Page SEO
The module's CWA page component automatically applies SEO meta from the current page or page data:
| Source | Applied as |
|---|---|
Page.title | <title> |
Page.metaDescription | <meta name="description"> |
PageData.title | Overrides Page.title |
PageData.metaDescription | Overrides Page.metaDescription |
They're set with useHead() at tagPriority: 101 — one step below the default priority — so any useHead() or useSeoMeta() call of your own overrides them. Titles are composed across nesting depth (deepest page first, joined with |), falling back to a title-cased last URL segment when fallbackTitle is enabled.
// In a page template component
useHead({
title: computed(() => `${articleHeadline.value} - In-depth Guide`)
})
Default OG Image
The module ships a default Open Graph image template via nuxt-og-image. Every CWA page automatically gets a dark-background social card containing the CWA logo, the page title, and the meta description — no setup required.
The template is CwaDefault.satori.vue (a Satori/SVG renderer). It receives:
| Prop | Source |
|---|---|
title | Current page or pageData title |
description | Current page or pageData meta description |
Previewing locally — visit /__og-image__/image/your-path/og.png to preview the generated image for any route.
Overriding the OG image
Per-page override — call defineOgImage() in your page template component. Your call replaces the module default for that route:
// In your page template <script setup>
defineOgImage('MyCustomTemplate', {
title: pageData.value?.data?.headline,
imageUrl: pageData.value?.data?.heroImage?.contentUrl
})
Project-wide override — create app/components/og-image/CwaDefault.satori.vue in your application. Nuxt resolves component names from the app layer first, so this shadows the module's default template for all pages that haven't overridden per-page.
Open Graph and Twitter Cards
Social card meta tags (og:title, og:description, og:image, twitter:card) are too content-specific for the module to set automatically — add them in your page templates alongside your own content fields:
useSeoMeta({
ogTitle: computed(() => pageData.value?.data?.headline),
ogDescription: computed(() => pageData.value?.data?.summary),
ogImage: computed(() => pageData.value?.data?.heroImage?.contentUrl),
twitterCard: 'summary_large_image'
})
Maintenance Mode
When maintenanceModeEnabled: true, the module's server middleware throws a 503 Service Unavailable (statusMessage: 'Website under maintenance', message "We will be back up and running as soon as possible"), which Nuxt renders through your app/error.vue. There is no redirect and no dedicated maintenance page — customise the screen by handling error.statusCode === 503 in your error page.
Exempt from the check: /login, /__nuxt_error, anything under /_cwa, plus /sitemap.xml, /sitemap_index.xml, /robots.txt and /__sitemap__/*. Signed-in users whose api_component JWT carries ROLE_ADMIN or ROLE_SUPER_ADMIN (and hasn't expired) bypass it entirely, so admins can still reach the site to turn maintenance mode off.
Sitemap
The sitemap is generated by @nuxtjs/sitemap. The module registers a CWA source endpoint (/__sitemap__/cwa-urls) that returns every Route in the API as a loc entry — there is no publish or visibility filtering, and no per-route priority or changefreq. Set defaults through @nuxtjs/sitemap's own sitemap config in nuxt.config, or add a second source, if you need more than URLs.
To publish a hand-written sitemap instead, put the XML in sitemapXml (admin → Settings) — it's served at /__sitemap__/cwa-custom.xml and added to the sitemap index.
Stop CWA contributing routes at all if you manage the sitemap externally — the source then returns an empty list:
// Admin settings panel → Sitemap → Disabled
// Or programmatically:
cwa.siteConfig.saveConfig({ sitemapEnabled: false })
Nuxt Config Overrides (Static Defaults)
You can set site config defaults in nuxt.config — these are merged with the API's stored values, with the API winning for any key it defines:
// nuxt.config.ts
cwa: {
siteConfig: {
siteName: 'My App',
canonicalUrl: 'https://www.example.com'
}
}
This lets you ship a sensible default without requiring a database record on first boot.