Public environment variables (env.public)
Code that is shared between the browser and the server reads process.env.NEXT_PUBLIC_SITE_URL (Next's convention) or process.env.DUSK_PUBLIC_X.
Code that is shared between the browser and the server reads process.env.NEXT_PUBLIC_SITE_URL (Next's convention)
or process.env.DUSK_PUBLIC_X. Set the prefixes in dusk.config.ts:
export default defineConfig({
env: { public: { prefixes: ["NEXT_PUBLIC_"], processEnv: true } },
});
prefixeslists the variables that reach the browser, in addition toDUSK_PUBLIC_and Vite'senvPrefix. Each prefix is at least three characters;*is refused. The same names are then alsoimport.meta.env.NAMEin client code.processEnv: truegives the client bundleprocess.env. Every public variable a file reads asprocess.env.NAMEis replaced by its value at build time, and aprocess.envobject holding the public variables only is defined for dynamic reads. The server keeps the realprocess.env; no secret is ever defined in the browser.- The key
publicis reserved inenv: it is the public contract, not a server variable, soDATABASE_URLand the other declarations ofenvare unchanged. - A public name can also come from
.env.public(committed; see Env files while you develop), whatever its prefix. A name declared in.env.publicand in a server file fails with[ENV_PUBLIC_CONFLICT]. - The build checks what the browser gets. A public value that looks like a credential (a
sk_live_orsk_test_key, a private key block, a password inside a URL, a JWT) fails with[ENV_PUBLIC_VALUE]; the message names the variable and its file, never the value. A public name that reads like a credential (a name that containsSECRET,TOKEN,PASSWORD,PASS,KEY,DATABASE_URL,DSN,PRIVATEorCREDENTIAL, for exampleDUSK_PUBLIC_API_TOKEN) only gets a warning,[ENV_PUBLIC_NAME], which names the variable and never its value. A publishable key has such a name legitimately, so the name alone never fails the build; the value check above decides. List the name inallowto silence the warning. A server variable that looks like a credential (SECRET,TOKEN,KEY,DATABASE,COOKIE...) whose value appears in a client file fails with[ENV_SECRET_EXPOSED], naming the variable and the file. allowlists public names whose values are public on purpose (a vendor's publishable key, a preview link with a token). Their values are not checked by[ENV_PUBLIC_VALUE], their names get no[ENV_PUBLIC_NAME]warning, and they are not treated as a leak in the client output. Each name is a string:allow: ["DUSK_PUBLIC_STRIPE_PUBLISHABLE_KEY"].- A client read that the build cannot check fails with
[ENV_NOT_PUBLIC]. Checked forms:import.meta.env.NAME,import.meta.env["NAME"], a destructuring ofimport.meta.envorprocess.env(const { NAME } = import.meta.env), a variable that holds one of them (const e = import.meta.env; e.NAME), andprocess.env.NAMEorprocess.env["NAME"]for a name that is not public.process.env.NODE_ENVis allowed. A key that is not a string literal, such asimport.meta.env[key], cannot be checked, so it fails too, with a message that says so: read the public name directly. - A variable with the Next.js prefix,
NEXT_PUBLIC_, is server-only unlessNEXT_PUBLIC_is inenv.public.prefixes(or Vite'senvPrefix). Dusk does not read that prefix as public by default, so such a name never reaches the browser and nothing fails. When the app lists the prefix, the dev server prints one[ENV_NEXT_PUBLIC]note recommending the Dusk prefixDUSK_PUBLIC_. Renaming is the recommended path; keeping the prefix is supported. - In mode
test,.env.localis not read, as Next.js does not read it in tests;.env,.env.testand.env.test.localare. Put a value that a test needs in.env.test, not in a file a developer keeps local. - In
dusk devthe client entry loads a small shim first, soprocess.envworks the same way; it holds the public variables only. - A field of
defineEnvmarkedsecret: truecannot have a public prefix:DUSK_PUBLIC_always counts, and so does a prefix passed asdefineEnv(shape, { publicPrefixes: ["NEXT_PUBLIC_"] }). Such a schema fails with[ENV_SECRET_PUBLIC]when its module loads. The build does not evaluate the server schema, so this check is at server start (or wherever the module is first imported), not indusk build.parsed.publicholds the public names that are not secret.
Typings. src/env.public.d.ts is generated (names only) and rewritten by dusk dev and dusk build. It types each
public name twice: import.meta.env.NAME is readonly NAME: string (the build inlines it, so an assignment has no
effect), and process.env.NAME takes Node's own type, an optional, writable string (NAME?: string). A test or a script
may therefore assign or delete a public name in process.env, as the runtime allows.
Without processEnv nothing changes: import.meta.env.DUSK_PUBLIC_* remains the browser's way to read a variable.
Each process.env.NAME of a public name in client code is replaced by its value in every chunk: the literal is inlined.
Vite would otherwise replace a bare process.env with an empty object in the browser, so a read of a public name in a chunk
that loads before any runtime shim was undefined. No shim is needed, so the order of the entry's imports does not matter.
processEnv: true also defines the whole process.env object of the public variables. A browser shim that copies
import.meta.env into process.env can be deleted: import.meta.env is already inlined, and process.env reads are too.