Next.js
Varlock provides a huge upgrade over the default Next.js environment variable tooling - adding validation, type safety, flexible multi-environment management, log redaction, leak detection, and more.
To integrate varlock into a Next.js application, you must use our @varlock/nextjs-integration
package. This package provides a drop-in replacement for @next/env
, the internal package that handles .env loading, plus a small config plugin which extends the Next.js webpack config to inject our additional security features.
-
Install varlock and the Next.js integration package
Terminal window npm install @varlock/nextjs-integration varlockTerminal window yarn add @varlock/nextjs-integration varlockTerminal window pnpm add @varlock/nextjs-integration varlock -
Run
varlock init
to set up your.env.schema
fileThis will guide you through setting up your
.env.schema
file, based on your existing.env
file(s). Make sure to review it carefully.Terminal window npm exec -- varlock initTerminal window yarn exec -- varlock initTerminal window pnpm exec varlock init -
Override
@next/env
with our drop-in replacementpackage.json {"overrides": {"@next/env": "npm:@varlock/nextjs-integration"}}package.json {"resolutions": {"**/@next/env": "npm:@varlock/nextjs-integration"},}In a monorepo, this override must be done in the monorepo’s root package.json file!
pnpm-workspace.yaml overrides:"@next/env": "npm:@varlock/nextjs-integration"This must be set in
pnpm-workspace.yaml
, which lives at the root of your repo, regardless of whether you are using a monorepo or not.package.json {"pnpm": {"overrides": {"@next/env": "npm:@varlock/nextjs-integration"}}}In a monorepo, this override must be done in the monorepo’s root package.json file!
-
Enable the Next.js config plugin
At this point, varlock will now load your .env files into
process.env
. But to get the full benefits of this integration, you must addvarlockNextConfigPlugin
to yournext.config.ts
(or.js
) file.next.config.ts import type { NextConfig } from "next";import { varlockNextConfigPlugin } from '@varlock/nextjs-integration/plugin';const nextConfig: NextConfig = {// your existing config...};export default nextConfig;export default varlockNextConfigPlugin()(nextConfig);
Accessing Environment Variables
Section titled “Accessing Environment Variables”You can continue to use process.env.SOMEVAR
as usual, but we recommend using Varlock’s imported ENV
object for better type-safety and improved developer experience:
import { ENV } from 'varlock/env';
console.log(process.env.SOMEVAR); // 🆗 still worksconsole.log(ENV.SOMEVAR); // ✨ recommended
Type-safety and IntelliSense
Section titled “Type-safety and IntelliSense”To enable type-safety and IntelliSense for your env vars, enable the @generateTypes
root decorator in your .env.schema
. Note that if your schema was created using varlock init
, it will include this by default.
# @generateTypes(lang='ts', path='env.d.ts')# ---# your config items...
This generates types for both process.env
and ENV
.
Why use ENV
instead of process.env
?
Section titled “Why use ENV instead of process.env?”- Non-string values (e.g., number, boolean) are properly typed and coerced
- All non-sensitive items are replaced at build time (not just
NEXT_PUBLIC_
) - Better error messages for invalid or unavailable keys
- Enables future DX improvements and tighter control over what is bundled
Managing Multiple Environments
Section titled “Managing Multiple Environments”Varlock can load multiple environment-specific .env
files (e.g., .env.development
, .env.preview
, .env.production
).
By default, the environment flag is determined as follows (matching Next.js):
test
ifNODE_ENV
istest
development
if runningnext dev
production
otherwise
Instead, we recommend explicitly setting your own environment flag using the @envFlag
root decorator, e.g. APP_ENV
. See the environments guide for more information.
Setting the Environment Flag
Section titled “Setting the Environment Flag”When running locally, or on a platform you control, you can set the env flag explicitly as an environment variable. However on some cloud platforms, there is a lot of magic happening, and the ability to set environment variables per branch is limited. In these cases you can use functions to transform env vars injected by the platform, like a current branch name, into the value you need.
Local/Custom Scripts
Section titled “Local/Custom Scripts”You can set the env var explicitly when you run a command, but often you will set it in package.json
scripts:
"scripts": { "build:preview": "APP_ENV=preview next build", "start:preview": "APP_ENV=preview next start", "build:prod": "APP_ENV=production next build", "start:prod": "APP_ENV=production next start", "test": "APP_ENV=test jest"}
Vercel
Section titled “Vercel”You can use the injected VERCEL_ENV
variable to match their concept of environment types:
# @envFlag=APP_ENV# ---# @type=enum(development, preview, production)VERCEL_ENV=# @type=enum(development, preview, production, test)APP_ENV=fallback($VERCEL_ENV, development)
For more granular environments, use the branch name in VERCEL_GIT_COMMIT_REF
(see Cloudflare example below).
Cloudflare Workers Build
Section titled “Cloudflare Workers Build”Use the branch name in WORKERS_CI_BRANCH
to determine the environment:
# @envFlag=APP_ENV# ---WORKERS_CI_BRANCH=# @type=enum(development, preview, production, test)APP_ENV=remap($WORKERS_CI_BRANCH, production="main", preview=regex(.*), development=undefined)
Managing Sensitive Config Values
Section titled “Managing Sensitive Config Values”Next.js uses the NEXT_PUBLIC_
prefix to determine which env vars are public (bundled for the browser). Varlock lets you control this with the @defaultSensitive
root decorator.
To keep the prefix behavior:
# @defaultSensitive=inferFromPrefix('NEXT_PUBLIC_')# ---FOO= # sensitiveNEXT_PUBLIC_FOO= # non-sensitive, due to prefix
Or, set a default and explicitly mark items:
# @defaultSensitive=true# ---SECRET_FOO= # sensitive by default# @sensitive=falseNON_SECRET_FOO=
Extra setup for standalone
Section titled “Extra setup for standalone”⚠️ This is only needed if you are using output: standalone
The standalone build process will not copy all our .env
files to the .next/standalone
directory, so we must copy them manually. Add this to your build command:
{ "scripts": { "build": "next build && cp .env.* .next/standalone", }}
you may need to adjust if you don’t want to copy certain .local files
Standalone builds do not copy dependency binaries, and varlock depends on the CLI to load.
So wherever you are booting your standalone server, you will also need to install the varlock binary and boot your server via varlock run
varlock run -- node .next/standalone/server.js