Quickly switch between .env files. Copies
.env.<environment> to .env.local (or a custom target) and
tracks the active environment via a header comment. Works with
Next.js, Vite, Remix, and any project that uses .env files.
npm install -g dotswitchOr use directly without installing:
npx dotswitch use staging# Switch to .env.staging → .env.local
dotswitch use staging
# Interactive picker (when no env specified)
dotswitch use
# Skip backup of existing .env.local
dotswitch use production --no-backup
# Force switch even if already active
dotswitch use staging --force
# Preview what would happen without making changes
dotswitch use staging --dry-rundotswitch lsAvailable environments:
▸ staging (active)
production
preview
JSON output for scripts:
dotswitch ls --json
# [{"name":".env.staging","env":"staging","active":true},{"name":".env.production","env":"production","active":false}]dotswitch currentJSON output:
dotswitch current --json
# {"active":"staging"}Pipe-friendly — outputs the plain env name when not a TTY:
ENV=$(dotswitch current)# Restore .env.local from .env.local.backup
dotswitch restore# Compare current .env.local against .env.production
dotswitch diff production
# Compare two environments directly
dotswitch diff staging production
# Show actual values (not just key names)
dotswitch diff staging production --show-values
# JSON output
dotswitch diff staging production --jsonAll commands support:
| Flag | Description |
|---|---|
-p, --path <dir> |
Project directory (defaults to cwd) |
--json |
Output as JSON (machine-readable) |
ls and use also support:
| Flag | Description |
|---|---|
-r, --root |
Source env files from the main repo root (worktree) |
use also supports:
| Flag | Description |
|---|---|
-f, --force |
Switch even if already active |
--no-backup |
Skip .env.local backup |
-n, --dry-run |
Preview what would happen |
Create a dotswitch.config.ts (or .js/.mjs/.cjs) in your project
root to customize behavior. Everything is optional — dotswitch
works out of the box without a config file.
// dotswitch.config.ts
import { defineConfig } from "dotswitch";
export default defineConfig({
target: ".env.local",
exclude: [".env.test"],
hooks: {
"main": "production",
"staging/*": "staging",
"dev*": "development",
},
});// dotswitch.config.js
/** @type {import("dotswitch").DotswitchConfig} */
export default {
target: ".env.local",
exclude: [".env.test"],
hooks: {
"main": "production",
"staging/*": "staging",
},
};// dotswitch.config.cjs
const { defineConfig } = require("dotswitch");
module.exports = defineConfig({
target: ".env.local",
exclude: [".env.test"],
});dotswitch searches for config files in this order (first match wins):
dotswitch.config.tsdotswitch.config.jsdotswitch.config.mjsdotswitch.config.cjs
| Field | Default | Description |
|---|---|---|
target |
".env.local" |
File to write the active env to |
exclude |
[] |
Additional env files to hide from ls |
hooks |
{} |
Branch-to-env mappings for git hook auto-switching |
By default dotswitch writes to .env.local, but some
frameworks use .env directly. Set the target field to
change this:
// dotswitch.config.ts
import { defineConfig } from "dotswitch";
export default defineConfig({
target: ".env",
});Automatically switch environments when you check out a branch.
- Add branch mappings to your config:
// dotswitch.config.ts
import { defineConfig } from "dotswitch";
export default defineConfig({
hooks: {
"main": "production",
"staging/*": "staging",
"develop": "development",
},
});- Install the git hook:
dotswitch hook installNow git checkout staging/feat-login will automatically run
dotswitch use staging.
"main"— exact branch name match"staging/*"— matchesstaging/prefix (e.g.,staging/feat-x)"dev*"— matches any branch starting withdev
dotswitch hook removeSwitch environments across multiple packages at once using glob patterns:
# Switch all apps to staging
dotswitch use staging --path "./apps/*"
# Check status across packages
dotswitch ls --path "./packages/*"Each directory is processed independently with labeled output.
dotswitch works transparently in
git worktrees. When
you run any command from a worktree, it automatically resolves
back to the main repo where your .env.* files and
config live.
# From a worktree, all commands operate on the main repo
cd /path/to/my-worktree
dotswitch ls # lists envs from the main repo
dotswitch use staging # switches in the main repo
dotswitch hook install # installs hook in the shared .git/hooksExplicit --path arguments are rebased automatically, so
monorepo globs also work from worktrees:
dotswitch use staging --path "./apps/*"When a worktree has its own .env.* files, dotswitch operates
locally by default. Use --root / -r to source env files
from the main repo instead:
cd /path/to/my-worktree
# List envs from the main repo (even if the worktree has local ones)
dotswitch ls --root
# Copy .env.staging from the main repo into the worktree as .env.local
dotswitch use staging --rootThis is useful when you want to quickly pull an environment configuration from the main repo into your worktree without copying files manually.
When you run dotswitch use staging, it:
- Backs up your existing
.env.localto.env.local.backup - Copies
.env.stagingto.env.local - Prepends a
# dotswitch:stagingheader to track the active environment
The header comment is how dotswitch ls and
dotswitch current know which environment is active.
dotswitch exports its core functions for use in scripts:
import {
listEnvFiles,
switchEnv,
getActiveEnv,
restoreEnvLocal,
loadConfig,
defineConfig,
parseEnvContent,
diffEnvMaps,
resolveProjectRoot,
} from "dotswitch";
const files = listEnvFiles(process.cwd());
const active = getActiveEnv(process.cwd());
switchEnv(process.cwd(), "staging", { backup: true });In worktree-aware scripts, resolve the project root first:
const projectRoot = resolveProjectRoot(process.cwd());
const files = listEnvFiles(projectRoot);
switchEnv(projectRoot, "staging", { backup: true });- Node.js >= 20
MIT
