Module Options
All configuration is declared under the swiftConfig key in nuxt.config.ts. The framework reads these options during Nuxt module initialization and enables the corresponding features on demand.
Options Overview
| Option | Type | Default | Description |
|---|---|---|---|
theme | { mode: 'system' | 'toggle' | 'dark' | 'light', default?: 'auto' | 'light' | 'dark', toggleStates?: 2 | 3 } | { mode: 'toggle', default: 'auto', toggleStates: 3 } | Single-knob theme mode. system=follow OS (no switcher) / toggle=user-switchable (mounts useTheme() + SwThemeSwitch) / dark / light=fixed single theme ({ mode: 'light' } / { mode: 'dark' }) |
i18n | boolean | { enabled: boolean, defaultLocale: string, locales: string[] } | { enabled: true, defaultLocale: 'zh-CN', locales: ['zh-CN'] } | i18n. Enables @nuxtjs/i18n and auto-registers locale files. Defaults to zh-CN only; multi-locale projects pick more locales via the cli wizard or pass an explicit locales array |
pwa | boolean | true | PWA master switch (Service Worker + manifest). Boolean only — manifest content goes in the top-level pwa.manifest of nuxt.config.ts |
addToHomescreen | boolean | AddToHomescreenOptions | follows pwa | Add-to-home-screen guide behaviour. ⚠️ Does nothing when pwa is false (the guide has its own hard gate) |
auth | boolean | { enabled?: boolean, deviceLogin?: boolean, guardedPaths: string[], guestPaths: string[] } | { deviceLogin: true, guardedPaths: ['/mine'], guestPaths: ['/login', '/register'] } | Route guard + client auth init. false = disable site-wide (docs / no-backend); deviceLogin: false = only turn off /login/device |
obfuscate | boolean | process.env.NODE_ENV === 'production' | JS obfuscation (client bundle only). Enabled in production by default; set false for debugging |
websocket | boolean | true | WebSocket plugin. Registers $ws when enabled |
errorHandler | boolean | { enabled?: boolean, endpoint?: string, toast?: boolean } | { enabled: true, endpoint: '/_error-report', toast: false } | Captures Vue runtime errors + unhandledrejection and reports |
styles | boolean | { reset?: boolean, responsive?: boolean, modal?: boolean, toast?: boolean } | { reset: true, responsive: true, modal: true, toast: true } | CSS base-layer injection. tokens always injected; the theme layer is assembled per theme.mode (see theme details below) |
systemInfo | boolean | { enabled?: boolean, endpoint?: string, ignorePaths?: string[], onError?: 'throw' | 'silent', clientRefreshTtl?: number } | true (clientRefreshTtl defaults to 300) | /system/info fetch. false = disable entirely; object supports custom endpoint + path-level skip + SSR failure policy (onError, defaults to 'throw') + client refetch reuse window (clientRefreshTtl seconds, 0 = refetch on every full page load) |
suppressConsole | boolean | true | Disables console.log/warn in production. false keeps output |
image | { provider?: string, providers?: object, originRules?: array } | { provider: 'noop', providers: {}, originRules: [] } | SwImage external-image provider routing (noop / cf-image / oss-aliyun / cos-tencent / qiniu / imgproxy). Image quality is hardcoded in the framework (quality=80) — not configurable here, and not a SwImage prop |
manualChunks | Record<string, string[]> | {} | Appends Vite custom chunk-split rules. Key = chunk name, value = array of package-name fragments |
request | { timeout?: number, uploadTimeout?: number, headers?: Record<string, string>, mock?: { enabled?: boolean, builtins?: boolean, systemInfo?: object }, transport?: 'base64' | 'binary' } | {} | $request globals: request timeout (default 20s), upload timeout (default 600s), global headers, dev-time request mock (mock) and encrypted-payload transport encoding (transport, default 'base64'). See ### request below and Dev request mock |
seo | { aiBotPolicy?: 'allow' | 'disallow', sitemap?: { exclude?: string[], mandatorySources?: string[] } } | { aiBotPolicy: 'allow', sitemap: { exclude: [] } } | SEO suite is permanently enabled. aiBotPolicy controls AI training bots (GPTBot/ClaudeBot/CCBot/PerplexityBot) crawl access; sitemap.exclude appends private-path exclusions to sitemap.xml (glob, merged with framework defaults, not replacing). siteUrl is locked via NUXT_PUBLIC_SITE_URL, not exposed here. See ### seo below |
ad | { showLabel?: boolean } | { showLabel: true } | SwAd* ad family config. showLabel is the site-wide default of the "Ad" compliance badge — components without an explicit showAdLabel follow it; an explicit component prop always wins. See ### ad below |
tracker | boolean | { enabled?: boolean, autoPageView?: boolean, heartbeatInterval?: number } | { enabled: true, autoPageView: true, heartbeatInterval: 30000 } | Unified tracking foundation (group WebSDK / self-hosted channel). Auto-degrades silently when config is missing — no need to turn it off manually. See ### tracker below |
apiDocs | { source: 'yapi' | 'openapi', url: string, project?: string | number, pathVars?: Record<string,string> } | none | API doc source for pnpm generate-api (build tooling only — the runtime module never reads it). Configure source / url (plus project for yapi) here when you wire the backend; the token goes to API_DOCS_TOKEN in .env. Not part of swift.config.json, which only holds what sw create can decide at creation time. See Codegen |
guards | Record<guardName, 'error' | 'warn' | 'off'> | all error | Escape hatch for build-time guards: downgrade one when it misfires so the build unblocks immediately (a line still goes to the build log — never silent). Not part of swift.config.json, and the CLI does not manage it — write it in swiftConfig inside nuxt.config.ts. See the "escape hatch guards" section of Rendering strategies |
Array defaults are replaced, not concatenated (framework#384): arrays such as
auth.guardedPaths/auth.guestPaths/i18n.localesare replaced wholesale as soon as you set them explicitly inswiftConfig(including an empty array[]) — the defaults are never merged back in. The one exception isseo.sitemap.exclude, which is appended to framework's private-path defaults on the sitemap module side (see### seobelow).
Internally framework uses the
moduleDependenciesAPI (@nuxt/kit 4.x) to register submodules (pinia / vueuse / fonts / scripts / sitemap / schema-org / robots, plus i18n / pwa when enabled). Transparent to consumers.
Configuration Examples
Docs Site Configuration (used by this site)
// nuxt.config.ts
export default defineNuxtConfig({
modules: ['@swift-web/framework'],
swiftConfig: {
theme: { mode: 'toggle', default: 'auto', toggleStates: 3 },
i18n: {
enabled: true,
defaultLocale: 'zh-CN',
locales: ['zh-CN', 'zh-TW', 'en'],
},
pwa: true,
// the docs site has no auth backend (no /login/device) → auth: false disables it site-wide
auth: false,
websocket: false,
// docs site has no error-report backend
errorHandler: false,
// docs site has no /system/info backend → disable the fetch
systemInfo: false,
},
})
Typical Business Project Configuration
export default defineNuxtConfig({
modules: ['@swift-web/framework'],
swiftConfig: {
theme: { mode: 'light' },
i18n: { enabled: false },
pwa: false,
auth: {
guardedPaths: ['/mine', '/profile', '/settings', '/orders'],
guestPaths: ['/login', '/register'],
},
websocket: false,
errorHandler: {
enabled: true,
endpoint: '/_error-report',
},
// systemInfo: true (default; framework fetches /system/info on SSR)
},
})
Option Details
theme
theme is a single-knob config: one mode field decides three things at once — "which token set to load", "which color-scheme to declare", and "whether a switcher is provided".
mode | Tokens loaded | color-scheme | Switcher |
|---|---|---|---|
'system' | light + dark | dark in dark contexts (follows OS) | ❌ none |
'toggle' | light + dark | dark in dark contexts | ✅ mounts useTheme() + SwThemeSwitch |
'dark' | fixed dark | dark | ❌ |
'light' (starter default) | fixed light | light | ❌ |
default(toggle only): the switcher's initial state —'auto'(follow OS) /'light'/'dark', default'auto'toggleStates(toggle only): the switcher form —3= tri-state (auto / light / dark cycle, can return to follow-system at any time) /2= two-state (light / dark only, simpler, but cannot return to follow-system once toggled), default3
In toggle mode the framework reads the localStorage theme preference on startup and writes it to the html[data-theme] attribute; switch it with useTheme() or the built-in SwThemeSwitch component.
Fixed
dark/lightdeclarecolor-schemeunconditionally (injectingcolor-scheme-dark.css/color-scheme-light.cssrespectively) — so native UI (scrollbars, form controls) follows the site, avoiding "a dark page with light scrollbars".
system/toggle, by contrast, declare it contextually:color-scheme: darkis emitted only while dark is active (@media (prefers-color-scheme: dark)or[data-theme='dark']); in a light context nothing is declared and the browser default (normal, which renders the same as light) applies. So "always declared" holds only for the two fixed modes — don't infer that every mode ships acolor-scheme.
themeonly accepts the object form. For a fixed single-theme site, write{ mode: 'light' }or{ mode: 'dark' }directly.
i18n
Enables @nuxtjs/i18n and auto-registers the locale files for the languages listed in locales (files placed in the i18n/locales/ directory). Set to false for Chinese-only projects to skip the i18n runtime.
pwa
Integrates @vite-pwa/nuxt and generates the Service Worker + Web App Manifest — enables "Add to Home Screen" and basic offline access. Set to false for projects that don't need PWA. The addToHomescreen sub-option controls "Add to Home Screen" prompt-card timing.
auth
Route guard config + framework client-side auto device login. Three forms:
false— Disable site-wide (docs / demo / no-backend projects where/login/devicedoesn't exist). Framework skipsauth.client.tsdevice login; global login/guest middleware naturally no-ops.trueorundefined(default) — Uses defaultguardedPaths: ['/mine']+guestPaths: ['/login', '/register'].- Object
{ enabled?, deviceLogin?, guardedPaths, guestPaths }— Custom paths;enabled: falseis equivalent to setting the top-level option tofalsebut keeps path declarations. deviceLogin: false— Only turns off the anonymous/login/devicelogin; username / QR / register and remember-me credential renewal keep working. For sites that don't want to pay one backend request per new device. The gate lives inuseUserStore().autoLogin()alone: the startup plugin, the systemInfo fallback and the 401 / 2002 re-login all go through it. Before turning it off, check: public backend endpoints must accept requests without a token; guests have nouser_id, so tracking / error reports carry no userId and guest-level endpoints that rely on a device identity stop working.
// Docs / demo / no-backend project
swiftConfig: {
auth: false,
systemInfo: false,
}
// Standard business project
swiftConfig: {
auth: {
guardedPaths: ['/mine', '/orders', '/settings'],
guestPaths: ['/login', '/register'],
},
}
Used with the framework's built-in global middleware (login.global.ts / guest.global.ts):
guardedPaths: Path prefixes requiring login (prefix-matched)guestPaths: Paths inaccessible to logged-in users — accessing them redirects to the home page
⚠️ Build-time validation (#166): with auth enabled,
runtimeConfig.public.deviceKey / tokenKeymust be non-empty strings or the build fails immediately — an empty cookie name crashes the whole site client-side during hydration (invisible to SSR/curl). Sites without an auth backend should setauth: falseto skip the check.
errorHandler
When enabled, the framework registers a Vue global error handler and an unhandledrejection listener on the client. On error, it sends a POST request to endpoint with the error details as JSON.
Two kinds are not reported: unhandled rejections whose name is AbortError (playback interrupted by pause() or a source change, AbortController cancellations — intentional, not failures), and errors whose stack script URLs are all browser-extension URLs (chrome-extension:// etc.). Errors without a stack are still reported.
false: disables the entire error-monitoring stacktrueor an object{ enabled, endpoint, toast }(all fields optional): enabled; the object form customizes the report endpoint (default/_error-report) and the error toast
Toast decoupled from reporting (#282, v0.97.0): toast controls whether end users see a visible "Something went wrong — we've been notified" prompt. It defaults to false — report silently (matching the default behaviour of industry SDKs such as Sentry / Bugsnag; on a content site, popping a dialog for one irrelevant third-party script error is pure noise). To keep the old toast behaviour, opt in explicitly:
// swift.config.json
"errorHandler": { "enabled": true, "toast": true }
Either way, Vue errors are still visible in the console during dev; this switch only gates the POST reporting layer.
⚠️ When customizing
endpoint, keep it out of/api/. The starter template — and every project derived from it — proxies all of/api/**to the business backend viarouteRules['/api/**'], and that proxy returns before the router is reached, so a local handler under/api/is never hit: error payloads (userId / stack / route / ua) get proxied to the backend, take a 404, and are silently swallowed. The symptom is "error monitoring is on but nothing ever arrives, with no error to go on." The default/_error-reportalready sits outside/api/; implement the endpoint underserver/routes/, notserver/api/. Same constraint applies to sitemap source endpoints.
systemInfo
Controls whether framework fetches /system/info during SSR, the endpoint to use, and which paths to skip.
true(default, equivalent to{ enabled: true, onError: 'throw' }): framework auto-fetches/system/infoon SSR and writes to systemStore (via frameworkrequestutil; SSR-side deviceId is auto-empty)false: don't fetch at all (no-backend scenarios like pure static docs sites){ enabled?: boolean, endpoint?: string, ignorePaths?: string[], onError?: 'throw' | 'silent', clientRefreshTtl?: number }: custom endpoint + path-level skip + SSR failure policy + client refetch reuse window
// Disable entirely (e.g. docs site with no backend)
swiftConfig: {
systemInfo: false,
}
// Custom endpoint + path-level skip
swiftConfig: {
systemInfo: {
endpoint: '/api/system/info', // defaults to '/system/info'
ignorePaths: ['/preview', '/embed'],
},
}
// Tolerate SSR fetch failure (flaky backend; accept client-side SEO field hydration)
swiftConfig: {
systemInfo: { onError: 'silent' },
}
// Client refetch reuse window: full page loads within 10 minutes in the same tab don't hit the backend; 0 = refetch every time
swiftConfig: {
systemInfo: { clientRefreshTtl: 600 },
}
clientRefreshTtl — reuse window for the client-side refetch (seconds, default 300)
After a full page load, framework refetches /system/info in the background on the client so that ops changes (ads / announcements) don't have to wait for the page cache to expire. The result is stored in sessionStorage with a timestamp (key prefixed with cachePrefix); full page loads inside the window reuse it without a request, and 0 restores "refetch on every full page load". Without a refetch, the whole session uses the systemInfo baked into the entry page's payload, so its freshness equals the entry page's cache TTL; in-app (SPA) navigation never refetched anyway. On an ISR home page this request used to be the largest per-page-view backend cost; a config change shows up on new full page loads after at most clientRefreshTtl seconds, and a tab the user opens manually counts as a new session and refetches immediately (a tab opened via target="_blank" copies the opener's sessionStorage and stays inside the window).
onError — SSR fetch failure policy
Behavior when the /system/info fetch fails during SSR. Defaults to 'throw':
'throw'(default, recommended) — fail-fast: throwscreateError({ statusCode: 503, fatal: true }), Nuxt renders error.vue. Surfaces problems immediately in dev; triggers 5xx monitoring alerts in production and prevents empty-shell HTML from being indexed by crawlers.'silent'— tolerate failure: onlyconsole.error+ a state flag; pages keep rendering with empty systemInfo, and the client silently re-fetches in the background. Suited to "high-SLA backend + SEO fields can be hydrated client-side"; the cost is hidden bugs in dev, empty-shell SEO loss in production, and no monitoring trigger.
Client-side fetch failures are not governed by
onError— they alwaysconsole.errorand never navigate to error.vue.The device is established entirely client-side, so the SSR-side cookie has no deviceId. If the backend requires a non-empty deviceId, the SSR fetch is bound to fail → the default
'throw'triggers a 503. This is the expected fail-fast behavior. Fixes: let the backend accept an empty deviceId for public endpoints, explicitly setonError: 'silent', or usesystemInfo: false.
obfuscate
JS obfuscation toggle. Enabled by default in production (process.env.NODE_ENV === 'production'); the client build artifact is processed through javascript-obfuscator, modestly increasing bundle size and dramatically reducing readability. Disabled automatically during debug / dev. Set explicitly to false when you need to debug production builds with readable code.
Only the client bundle is obfuscated: server code never ships to users (zero benefit), and obfuscating the SSR output breaks Nitro's static tracing of dynamic import chunks, causing Cannot find module 500s at runtime.
websocket
Whether to register the framework's built-in $ws instance (with auto-reconnect). Default true. Set to false to skip plugin registration and trim client bundle size if your project doesn't use WebSocket.
styles
CSS base-layer injection. The framework ships six CSS layers:
tokens.css— always injected (CSS variables are the single source of truth, no toggle)theme.css— injected whentheme.modeissystem/toggle(both light + dark tokens); fixeddark/lightmodes inject the correspondingcolor-schemedeclaration insteadreset.css— gated bystyles.reset(defaulttrue)responsive.css— gated bystyles.responsive(defaulttrue); provides.sw-container+.sw-show-mobile/.sw-hide-mobile/.sw-hide-tablet/.sw-hide-desktopresponsive utility classes (see Styling & Tokens)modal.css— gated bystyles.modal(defaulttrue); SwModal default skin (--sw-modal-*mapped to semantic tokens)toast.css— gated bystyles.toast(defaulttrue); SwToast default skin (--sw-toast-*mapped to semantic tokens)
// Disable framework reset (e.g. when using Tailwind preflight)
styles: { reset: false, responsive: true }
// Skip entirely (project ships its own complete base style)
styles: false
suppressConsole
Whether to suppress console.log / console.warn in production (console.error is always preserved) to reduce information leakage. Default true. Set false when you need to keep output for production troubleshooting. No effect during dev.
image
SwImage external-image provider routing. Only affects <SwImage> rendering of external URLs; does NOT affect:
- Encrypted images (
.bncsuffix, handled bydecrypt-image) - Local images (
/images/*, handled natively by Nuxt static)
image: {
provider: 'noop', // Default handler: noop / cf-image / oss-aliyun / cos-tencent / qiniu / imgproxy
providers: {
'cf-image': { baseURL: 'https://cdn.example.com' },
},
originRules: [
{ match: '^https://(oss|img)\\.example\\.com/', use: 'oss-aliyun' },
],
}
Image quality (quality) is hardcoded to 80 in the framework and not configurable; display dimensions are derived from <SwImage>'s ratio prop (width / height are <img> attributes computed internally from ratio, not props). See the SwImage docs for the full prop list.
manualChunks
Vite Rollup custom chunk-split rules. Key = output chunk name, value = an array of package-name fragments to match.
manualChunks: {
'vendor-charts': ['echarts', 'chart.js'],
'vendor-rich-editor': ['@tiptap/'],
}
Best for splitting "only-used-on-a-few-pages" third-party libraries (charts / rich-text editors) into separate chunks instead of bloating the main bundle.
request
Framework $request global configuration.
request: {
timeout: 20_000, // default 20000ms (20s). Normal request timeout
uploadTimeout: 600_000, // default 600000ms (10min). Upload timeout for SwUploadImage / SwUploadVideo etc.
headers: {
'X-Tenant-Id': 'foo', // global headers, injected into $request and nitroApiFetch calls
},
}
Per-request overrides (request(url, { timeout: 5000 })) take precedence over global config.
request.mock — dev-time request mock (framework#100 / #188)
Intercepts mocks at the request layer (the createApiFetch plaintext layer), dev-only (both SSR and CSR, framework#208), stripped from production builds via DCE. Build the UI / interactions first while the backend API isn't ready, then just delete the mock to wire up the real API.
enabled?: boolean— master switch. Defaults toimport.meta.env.DEV(on in dev / off in prod). Setfalseto temporarily disable allregisterApiMockinterception.builtins?: boolean— built-in presets: framework ships ready-made fake data for/login/*+/system/info. Only active whenenabled: trueis set explicitly; defaults totrue(on whenever enabled). Setfalseto use only the mechanism without the built-in fake data.systemInfo?: object— overrides fields of the built-in/system/infoskeleton (shallow merge + one level of deep merge onheader); effective only when builtins are active.
Business-endpoint mocks go through registerApiMock in a single dev-only file (e.g. app/plugins/dev-mock.ts); delete that file at integration time and component code stays untouched.
// Enable dev mock: built-in login / system-info presets come along too
request: {
mock: { enabled: true },
}
// Use only the registerApiMock mechanism, without built-in login / system-info fakes
request: {
mock: { enabled: true, builtins: false },
}
Full usage / matcher / built-in preset details: Dev request mock.
request.transport — encoding of the backend's encrypted payload (fw#382)
The outermost encoding of the backend's encrypted payload. Send and receive share it, so it must match the backend's convention. Defaults to 'base64', which suits the vast majority of backends; only a few H5 backends using a raw-binary protocol (e.g. the yc203 / yc202 line) need 'binary'.
'base64'(default):IV‖ciphertextis Base64-encoded to text and sent astext/plain.'binary':IV‖ciphertextis a raw binary byte stream with no Base64 (the request body sends raw bytes, the response is read asarrayBuffer, and decryption skipsBase64.parse).
The core crypto — AES-CBC / PKCS7 / gzip / HMAC-derived key — is identical in both modes; only this outermost encoding differs.
// Backend using a raw-binary encrypted protocol
request: {
transport: 'binary',
}
Symptom of a mismatch: if the backend returns base64 but 'binary' is configured (or vice versa) the payload won't decrypt, and the console throws 响应报文长度异常 (N bytes) … 多为 request.transport 与后端约定不符 or Invalid typed array length. The framework does not auto-detect this (every site hard-codes Content-Type: application/json, so it can't serve as a signal), so it must be set explicitly.
Server-side
nitroApiFetchshares one source of truth withrequest():nitroApiFetch, used in Nitro contexts such as sitemap / tasks, readsswiftWebFramework.request.transport/timeout/headersthrough Nitro'suseRuntimeConfig()(which works without an event) — the very same value the client-siderequest()uses. ConfigureswiftConfig.request.transportin one place and you are done; no bridge plugin or extra environment variable is needed.
request.headersapplies to bothrequest()(browser and SSR) andnitroApiFetch()(Nitro server routes / tasks), but not upload. The framework overrides the protocol headersContent-Type,deviceType,requestId,version,time, andlanguagecase-insensitively; global headers cannot replace them.To override temporarily on one machine without touching config, use Nitro's nested env convention:
NUXT_PUBLIC_SWIFT_WEB_FRAMEWORK_REQUEST_TRANSPORT=binary.
seo
The SEO submodule is permanently enabled (sitemap / schema-org / robots / OG meta — all on). Two fields are exposed here:
aiBotPolicy?: 'allow' | 'disallow'— controls whether AI training crawlers (GPTBot / ClaudeBot / CCBot / PerplexityBot) are allowed.'allow'(default) allows them;'disallow'explicitly disallows in robots.txt + metasitemap?: { exclude?: string[], mandatorySources?: string[] }— private-path exclusion for sitemap.xml, see below
seo.sitemap
The sitemap default exclude already includes common private paths (framework#85 GAP3):
/login, /register, /forgot,
/mine/**, /profile/**, /account/**,
/admin/**, /dashboard/**,
/search, /oauth/**
Append business-specific private paths via seo.sitemap.exclude (merged with framework defaults, not replacing) — framework's default private-path list cannot be removed, you can only add to it:
// swift.config.json
{
"framework": {
"seo": {
"sitemap": {
"exclude": ["/link", "/promo-draft", "/transit/**"]
}
}
}
}
Field:
exclude?: string[]— Append paths (micromatch glob syntax). Common paths like/login,/mine/**don't need re-declaring — framework already includes them.mandatorySources?: string[]— Disabled by default; exact same-origin mandatory JSON sources fail XML generation with a safe 503. See the Sitemap guide.
sitemap siteUrl (NUXT_PUBLIC_SITE_URL)
sitemap.xml's siteUrl is locked to the NUXT_PUBLIC_SITE_URL environment variable (not exposed under swiftConfig). framework runs a host-format check at module setup:
| Input | Result |
|---|---|
undefined / '' | ❌ not configured |
example.com (missing protocol) | ❌ not a valid URL |
<your-domain> (placeholder) | ❌ not a valid URL |
ftp://example.com | ❌ protocol must be http/https |
https://example.com/api (with path) | ❌ must not include path |
https://example.com?lang=zh | ❌ must not include query/hash |
https://example.com | ✅ |
https://example.com/ (trailing slash) | ✅ |
http://localhost:3000 (dev) | ✅ |
On failure: console.warn in dev / console.error in production build — build is not blocked (CI pipelines may inject the value post-build).
This value typically comes from ops / DevOps (not developers):
- Local dev:
.envsetNUXT_PUBLIC_SITE_URL=http://localhost:3000or a staging URL - Production: ops / CI inject the primary domain (e.g.
https://www.example.com)
For detailed diagnostics + nginx forwarding setup see nginx origin header forwarding.
Different semantics from useSeo's origin resolution: sitemap.xml always uses
NUXT_PUBLIC_SITE_URL(a stable primary domain — search engine sitemaps must output a single stable URL set); useSeo's canonical/og:url pins to it when set, and only falls back touseRequestURL().origin(following the user's visited domain) when it isn't — since framework#278 this variable is L2 of the origin priority chain, ahead of the request headers (sites with ISR or prerendered pages must set it, otherwise cached pages never seeHost). See framework#87 / #85 / #278.
ad
SwAd* ad-family-level config. Its only field for now is showLabel — the site-wide default of the "Ad" compliance badge (SwAdLabel):
true(default): everySwAd*component renders the badge whenshowAdLabelis not set. The default errs on the compliance-safe side — forgetting to turn it off just looks a bit ugly; forgetting to turn it on means unlabeled ads, which is a compliance risk.false: no badge by default site-wide — for sites whose ad creatives already carry a baked-in "Ad" label.- An explicit component-level
show-ad-labelprop always overrides this default (e.g. re-enable a single slot after turning it off globally).
// nuxt.config.ts — turn off globally for sites whose creatives carry a baked-in "Ad" label
swiftConfig: {
ad: { showLabel: false },
}
Upgrade note: sites whose creatives already carry the ad label and don't want the component badge must now opt out explicitly (global
ad.showLabel: false, or per-component:show-ad-label="false").
tracker
Group-wide operational analytics (WebSDK) foundation. When enabled, the framework lazy-loads the WebSDK on the client and takes over tracking — driving impression / click reporting for SwAd* ad components, plus business-side custom reporting via useTracker() / useAdTrack().
boolean—true(default) enables the foundation;falsedisables it entirely- Object
{ enabled?, autoPageView?, heartbeatInterval? }:enabled?: boolean— master switch, defaulttrueautoPageView?: boolean— auto-reportappPageViewon route change, defaulttrueheartbeatInterval?: number— video playback heartbeat interval (milliseconds): while playing, avideoEvent(video_behavior_key: 'video_heartbeat', payload carries accumulated watched secondswatch_time) fires every N milliseconds, powering "average watch time / valid play" metrics. Default30000(30 seconds);0disables the heartbeat (watch_timestill ships with the other playback events)
The
autoPageClickoption was removed with WebSDK v1.1.6 — the SDK auto-collectspage_click(richer fields, always on); see Tracking Foundation · SDK Auto-Collected Events.
// Enabled by default, no explicit config needed; to turn off auto pageView:
tracker: { autoPageView: false }
// Disable the tracking foundation entirely
tracker: false
Dual gate (silent no-op if either is missing): reporting only takes effect when both tracker.enabled is true AND system/info.tracker returns appId / encryptedConfig. When config is missing, the framework auto-degrades silently (component tracking still written, nothing sent at runtime) — no need to turn it off manually.
For the full event list and usage, see Tracking Foundation.
Related links
- Quick start · CLI command reference — the commands that edit these fields via
sw config set - SEO best practices · Sitemap — the practice behind the
seoconfig (includingseo.sitemap) - Dev request mock — full usage of
request.mock - Styling & tokens — the style layers injected by
styles/theme