Front-end configuration files are easier to maintain when each one has a narrow job: .env supplies environment-specific values, .nvmrc coordinates Node versions, .gitignore excludes files Git should not start tracking, and ESLint reports code problems. Do not add Babel, Prettier, or another config because an old tutorial said every project needs one—add it when the build or team workflow needs it.
.env: configuration values, not a secret vault
DATABASE_URL=postgresql://localhost:5432/app
API_BASE_URL=http://localhost:3000
Node can load dotenv-style files into process.env; modern Node also supports CLI options such as --env-file. The syntax is convenient, but it does not make a value private.
node --env-file=.env server.js
Keep these boundaries clear:
- Do not commit real production secrets in
.envfiles. - Commit
.env.examplewith variable names and safe placeholder values. - Treat every value shipped in a browser bundle as public.
- If a secret reached Git history, rotate it immediately; adding the file to
.gitignoredoes not revoke it. - Let the deployment platform or a secret manager inject production values.
- Validate required environment variables at startup, not halfway through a user request.
const siteUrl = process.env.NEXT_PUBLIC_SITE_URL
if (!siteUrl) {
throw new Error("Missing NEXT_PUBLIC_SITE_URL")
}
Frameworks define their own file precedence and client-exposure conventions. In Next.js, a NEXT_PUBLIC_ prefix exposes a value to browser code, so it must never contain a database password or server token.
.nvmrc and engines: keep Node versions aligned
An .nvmrc at the project root can contain a version such as:
20
Then local users of nvm can run:
nvm install
nvm use
For a more explicit compatibility statement, include an engines range in package.json:
{
"engines": {
"node": ">=20 <23"
}
}
.nvmrc helps local nvm users; engines communicates supported versions to package managers and hosting platforms. Your CI workflow and Docker image should still use an explicit compatible version—do not assume they read .nvmrc automatically.
.gitignore: prevents new tracking, not history cleanup
A typical Node or Next.js project starts with rules like these:
node_modules/
.next/
dist/
coverage/
*.log
.DS_Store
.env
.env.*
!.env.example
Useful rules to remember:
/distonly targets a root-leveldist;dist/can match corresponding directories.**can match across nested directories.!re-includes a path, although a fully ignored parent directory may require an additional rule.- Shared rules belong in
.gitignore; personal machine-wide rules belong in Git's global exclude file.
An already tracked file is not removed just because you add it to .gitignore. Review the impact first, then remove only the index entry if that is truly intended:
git rm --cached path/to/file
To discover which ignore rule matches a path:
git check-ignore -v path/to/file
That command is a diagnostic tool; it does not undo a leaked secret or rewrite repository history. Follow a deliberate Git workflow before making tracked files disappear from a shared repository.
Babel: confirm that you need it
Modern frameworks such as Next.js and Vite already provide a transform pipeline. If you have no custom transform, unusual syntax, or library-build requirement, adding a .babelrc may create duplicate work or bypass framework optimizations.
When a project really needs Babel, choose the configuration scope intentionally:
babel.config.*is project-wide and can suit monorepos..babelrc.*is resolved relative to packages and files.- A
babelfield inpackage.jsonsuits very small configurations.
Keep the configuration minimal and explain why it exists in the repository documentation.
ESLint: use flat config for new work
ESLint's current configuration format is flat config: an eslint.config.js, .mjs, or .cjs file at the project root that exports an array of configuration objects.
// eslint.config.mjs
import js from "@eslint/js"
import { defineConfig } from "eslint/config"
export default defineConfig([
js.configs.recommended,
{
files: ["**/*.{js,mjs,cjs}"],
rules: {
"prefer-const": "error",
"no-unused-vars": ["warn", { argsIgnorePattern: "^_" }],
},
},
{
ignores: ["dist/**", ".next/**", "coverage/**"],
},
])
For React, TypeScript, or Next.js, start from the framework's current recommended configuration rather than copying this plain-JavaScript example blindly. In flat config, ignore patterns belong in a configuration object; old .eslintignore behavior does not automatically carry over. See the official ESLint configuration documentation when migrating an older project.
Let TypeScript, ESLint, and Prettier do different jobs
| Tool | Primary responsibility |
|---|---|
| TypeScript | Type checking |
| ESLint | Error patterns, code quality, and framework rules |
| Prettier | Formatting |
| EditorConfig | Baseline indentation and line-ending behavior across editors |
Avoid making ESLint and Prettier enforce the same formatting choices. It adds noise to review without improving the code. Use the editor to format consistently, then keep linting focused on correctness and project conventions.
A sensible repository baseline
project/
├── .env.example
├── .gitignore
├── .nvmrc
├── eslint.config.mjs
├── prettier.config.mjs
├── package.json
└── tsconfig.json
CI should run the checks the project actually depends on, typically in an order that gives quick feedback:
npm run lint
npm run typecheck
npm run format:check
npm test
npm run build
Use the same assumptions locally and in CI. A project that passes only on one contributor's machine is missing configuration, not merely documentation.
Where to go next
- Need a safe day-to-day sequence for staging and sharing changes? Read the practical Git workflow.
- Setting up a Mac for JavaScript work? Continue with the 30-minute Mac development setup.
- Want faster navigation through the repository you just configured? Use the VS Code shortcuts worth learning first.
For .env file parsing and the --env-file option, see the official Node.js environment-variable documentation. The goal is not a large config folder; it is a repository that behaves predictably for every contributor.
Continue reading
A Practical Git Workflow: Add, Commit, Fetch, Pull, and Push
Understand Git's working tree, staging area, local repository, and remote—then use add, commit, fetch, pull, push, branches, and restore deliberately.
10 minGit worktree: The Git Command That Made Me Use stash Much Less
Learn how Git worktree keeps multiple branches open at once, when it is better than stash or clone, and the commands needed for a practical workflow.
9 minpnpm in Practice: Installation, Migration, and Workspaces
Use pnpm for faster installs, smaller dependency storage, stricter resolution, npm migration, and practical workspace management across frontend projects.
Subscribe to FreeMac
Weekly picks: free Mac software reviews, trusted source updates, alternatives, and low-friction guides.