Environment variables
Nuxt themes load their runtime configuration from an .env file in the theme root (_theme/{theme}/.env). This page lists every variable the CMS evaluates — from API endpoint through theme selection to project-specific PurgeCSS safelist extensions.
Complete .env example
# _theme/vue-base/.env
# API & site
NUXT_API_KEY=123456
NUXT_PUBLIC_API_BASE=http://newmeta.local/api
NUXT_PUBLIC_SITE_URL=http://newmeta.local
NUXT_PUBLIC_THEME=vue-base
# Optional: project-specific PurgeCSS safelist
NUXT_PURGECSS_SAFELIST_STANDARD=project-,custom-
NUXT_PURGECSS_SAFELIST_GREEDY=highlight-
NUXT_PURGECSS_SAFELIST_DEEP=cms-Each variable is explained individually below.
API and site variables
| Variable | Visibility | Required | Purpose |
|---|---|---|---|
NUXT_API_KEY | server-only | yes | Internal key for server-to-server calls from Nuxt (e.g. SSR fetches against /api/*) |
NUXT_PUBLIC_API_BASE | public | yes | REST API base URL. Visible on both client and server |
NUXT_PUBLIC_SITE_URL | public | yes | Canonical site URL for Open Graph, sitemap, Schema.org |
NUXT_PUBLIC_THEME | public | yes | Active theme name — controls theme-specific asset resolution |
Never expose NUXT_API_KEY as public
NUXT_API_KEY must not be prefixed with NUXT_PUBLIC_. Nuxt's runtime config treats PUBLIC_* variables as client-exposed — every browser visitor could read the key. The key lives correctly under runtimeConfig.apiKey (not under runtimeConfig.public).
How Nuxt reads them
nuxt.config.ts binds them through runtimeConfig:
runtimeConfig: {
apiKey: process.env.NUXT_API_KEY,
public: {
apiBase: process.env.NUXT_PUBLIC_API_BASE,
siteUrl: process.env.NUXT_PUBLIC_SITE_URL || '',
theme: process.env.NUXT_PUBLIC_THEME || 'vue-base',
}
}Access from code:
const config = useRuntimeConfig()
const apiKey = config.apiKey // server-only
const apiBase = config.public.apiBase // client + serverTheme selection: NUXT_PUBLIC_THEME
NUXT_PUBLIC_THEME=vue-base # or projekt01, customer-theme-nameThe value is used internally for project-specific asset resolution, e.g.:
- Admin CSS: the
compiled_cssendpoint for menu-editor styles reads_theme/{theme}/public/css/menu.less - Custom overrides: the vueRegistry rebuild prefers
_theme/{theme}/app/custom/{plugin}/widgets/.../template/index.vueover the plugin's original file
For dev/build, every theme is a standalone Nuxt project with its own npm install — the NUXT_PUBLIC_THEME value should match the folder name.
Project-specific PurgeCSS safelist
In a production build, PurgeCSS removes every CSS rule whose classes don't appear in the HTML/templates. Dynamically generated classes (e.g. row_42, col_108, widget_217 from the Pagebuilder) already have default safelist entries in the theme. Project-specific classes can be added without forking nuxt.config.ts:
NUXT_PURGECSS_SAFELIST_STANDARD=ma-,project-,custom-
NUXT_PURGECSS_SAFELIST_GREEDY=highlight-
NUXT_PURGECSS_SAFELIST_DEEP=cms-Each variable takes a comma-separated list of prefixes. The nuxt.config.ts turns every prefix into a ^prefix regex and merges it into the matching bucket:
// nuxt.config.ts (excerpt)
function extraSafelistFromEnv(envKey: string): RegExp[] {
return (process.env[envKey] || '')
.split(',')
.map(s => s.trim())
.filter(Boolean)
.map(prefix => new RegExp('^' + prefix))
}
const projectExtraStandard = extraSafelistFromEnv('NUXT_PURGECSS_SAFELIST_STANDARD')
const projectExtraGreedy = extraSafelistFromEnv('NUXT_PURGECSS_SAFELIST_GREEDY')
const projectExtraDeep = extraSafelistFromEnv('NUXT_PURGECSS_SAFELIST_DEEP')The three buckets
PurgeCSS knows three safelist types with different behavior:
| Bucket | Env var | Behavior | When to use |
|---|---|---|---|
standard | NUXT_PURGECSS_SAFELIST_STANDARD | Exact class-name match (including the prefix) | Single classes: ma-button, project-card |
greedy | NUXT_PURGECSS_SAFELIST_GREEDY | Match inside composite selectors | Combined classes: .widget_217 .is-active, row_42 col-6 |
deep | NUXT_PURGECSS_SAFELIST_DEEP | Keep child selectors | Descendant: .cms-block > *, .cms-container .inner |
The internal defaults place e.g. ^row_/, ^col_/, ^widget_/ (Pagebuilder wrappers) in greedy, because they appear in combinations. Details: Widgets › Custom-CSS Convention.
Decision guide
| Class | Bucket |
|---|---|
.ma-button (used on its own) | standard |
.project-theme-dark (toggle class on body) | standard |
.widget_217 .is-active (wrapper combination) | greedy |
.highlight-red (with :hover, [active] variants) | greedy |
.cms-block > * (universal child styling) | deep |
.cms-container .inner (fixed descendant chain) | deep |
Example scenario
A project ships its own CSS file project-styles.css containing:
.ma-header { ... }
.ma-footer { ... }
.highlight-red { color: red; }
.highlight-red:hover { color: darkred; }
.cms-block > h2 { margin: 0; }Matching .env entries:
NUXT_PURGECSS_SAFELIST_STANDARD=ma-
NUXT_PURGECSS_SAFELIST_GREEDY=highlight-
NUXT_PURGECSS_SAFELIST_DEEP=cms-Without safelist entries, these classes would be stripped in the production build — styles appear in dev but not in prod. A classic hard-to-debug build failure.
Non-Nuxt variables
The CMS backend (PHP) reads its config from config.php and config.local.php, not from the theme .env. Details: Configuration.
Some overlaps inside the Task Manager / Update Manager:
| Topic | Where configured? |
|---|---|
| DB credentials | config.local.php (PHP) |
| Update server | config.local.php (PHP) |
| CORS allowlist | Admin UI /admin/api-keys → DB table |
| 2FA | Admin UI per user |
| Design tokens | Admin UI /admin/design → DB column website.custom_less |
The Nuxt .env is only relevant to the theme itself.
Common issues
.env in _public/ or the project root
Nuxt loads .env only from the theme root (_theme/{theme}/.env), not from the project root. If you drop the file in the wrong folder, every env variable reads as undefined.
NUXT_PUBLIC_API_BASE without a trailing /api
The value must include the /api path: http://newmeta.local/api, not http://newmeta.local. Client code that builds URLs like ${apiBase}/pages otherwise gets http://newmeta.local/pages — a classic 404.
.env changes not picked up
Nuxt loads .env on server start — changes need a dev-server restart (Ctrl+C + npm run dev again). Production builds need a fresh npm run build.
Safelist prefix with dash (ma-) vs. without (ma)
NUXT_PURGECSS_SAFELIST_STANDARD=ma also matches .mango, .maze, .mail. If you want to protect only classes with the ma- prefix, include the dash explicitly: ma-. The regex is ^ma- — stringent.
Empty env value overrides the default
NUXT_PUBLIC_THEME= (empty) produces an empty string in Nuxt, not the fallback. In nuxt.config.ts there's || 'vue-base' — the fallback only kicks in when the variable is completely undefined, not when it's an empty string. Either leave the line out entirely or set a valid value.
See also
- Theme structure — where
.envlives inside the theme folder - Design tokens —
website.custom_lessvia/admin/design - Nuxt aliases —
#extensions,#widgets/plugin-registry - Deployment — production build, env vars for production
- Getting Started › Configuration — base setup of
.env - Widgets › Custom-CSS Convention — PurgeCSS bucket details for Pagebuilder wrappers