Skip to content

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

env
# _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

VariableVisibilityRequiredPurpose
NUXT_API_KEYserver-onlyyesInternal key for server-to-server calls from Nuxt (e.g. SSR fetches against /api/*)
NUXT_PUBLIC_API_BASEpublicyesREST API base URL. Visible on both client and server
NUXT_PUBLIC_SITE_URLpublicyesCanonical site URL for Open Graph, sitemap, Schema.org
NUXT_PUBLIC_THEMEpublicyesActive 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:

ts
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:

ts
const config = useRuntimeConfig()
const apiKey = config.apiKey              // server-only
const apiBase = config.public.apiBase     // client + server

Theme selection: NUXT_PUBLIC_THEME

env
NUXT_PUBLIC_THEME=vue-base        # or projekt01, customer-theme-name

The value is used internally for project-specific asset resolution, e.g.:

  • Admin CSS: the compiled_css endpoint 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.vue over 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:

env
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:

ts
// 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:

BucketEnv varBehaviorWhen to use
standardNUXT_PURGECSS_SAFELIST_STANDARDExact class-name match (including the prefix)Single classes: ma-button, project-card
greedyNUXT_PURGECSS_SAFELIST_GREEDYMatch inside composite selectorsCombined classes: .widget_217 .is-active, row_42 col-6
deepNUXT_PURGECSS_SAFELIST_DEEPKeep child selectorsDescendant: .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

ClassBucket
.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:

css
.ma-header { ... }
.ma-footer { ... }
.highlight-red { color: red; }
.highlight-red:hover { color: darkred; }
.cms-block > h2 { margin: 0; }

Matching .env entries:

env
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:

TopicWhere configured?
DB credentialsconfig.local.php (PHP)
Update serverconfig.local.php (PHP)
CORS allowlistAdmin UI /admin/api-keys → DB table
2FAAdmin UI per user
Design tokensAdmin 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