JSON Tools

JSON Configuration Files: Best Practices & Alternatives

The Debuggers Engineering Team
10 min read

TL;DR

  • JSON is the default for configuration in the JavaScript ecosystem (package.json, tsconfig.json, eslintrc.json) but has real limitations: no comments, no variables, no environment support
  • JSON5 and JSONC add comments and relaxed syntax. Use JSONC for VS Code settings and JSON5 for application configs
  • Never commit secrets to JSON config files. Use environment variables and reference them in code
  • Use schema-backed configs (tsconfig, package.json have JSON Schemas) for IDE autocompletion and validation

Table of Contents

Configuration files displayed in a modern code editor with syntax highlighting

Where JSON Config Is Used

JSON is the dominant configuration format in the JavaScript/TypeScript ecosystem:

FilePurposeEcosystem
package.jsonProject metadata, dependencies, scriptsnpm/yarn/pnpm
tsconfig.jsonTypeScript compiler optionsTypeScript
.eslintrc.jsonLinting rulesESLint
.prettierrcCode formatting rulesPrettier
vercel.jsonDeployment configurationVercel
firebase.jsonFirebase project configFirebase
manifest.jsonPWA manifest, Chrome extensionsWeb Platform
.vscode/settings.jsonEditor settingsVS Code

Outside JavaScript, JSON configs appear in AWS CloudFormation templates, Jupyter notebooks, Swagger/OpenAPI specs (originally JSON), and many CLI tools.

JSON Config Limitations

No comments. This is the biggest limitation. You cannot explain why a setting exists, document valid values, or leave notes for other developers:

{
  "compilerOptions": {
    "strict": true,
    "target": "ES2022"
  }
}

Why is target set to ES2022 instead of ESNext? You cannot say without external documentation.

No trailing commas. Adding or removing the last property requires editing the line above to add/remove a comma. This creates noisy diffs in version control:

 {
   "name": "my-app",
-  "version": "1.0.0"
+  "version": "1.0.0",
+  "description": "My application"
 }

No variables or references. You cannot define a value once and reference it in multiple places. Every repetition is a maintenance risk.

No multi-line strings. Long values like paths or descriptions must be on a single line, reducing readability.

No environment support. JSON has no concept of "use this value in production and that value in development." You need external tooling for environment management.

JSON5: JSON with Comments and More

JSON5 extends JSON with features borrowed from ECMAScript 5:

{
  // Single-line comments
  name: 'my-app', // Unquoted keys and single quotes
  
  /* Multi-line
     comments */
  version: "2.0.0",
  
  port: 3000, // Trailing commas allowed
  
  description: "A multi-line \
string that spans \
multiple lines",
  
  // Hex numbers, Infinity, NaN
  maxConnections: 0xFF,
}

To use JSON5 in Node.js:

npm install json5
import JSON5 from 'json5';
import { readFileSync } from 'fs';

const config = JSON5.parse(readFileSync('config.json5', 'utf-8'));

JSON5 is ideal for application configuration files where developer readability matters more than strict compatibility.

JSONC: JSON with Comments

JSONC (JSON with Comments) is a more conservative extension. It only adds comments and trailing commas to standard JSON syntax.

VS Code uses JSONC for its settings files. When you open settings.json, VS Code parses it as JSONC, allowing comments.

VS Code settings example (JSONC):

{
  // Font settings
  "editor.fontSize": 14,
  "editor.fontFamily": "'Fira Code', monospace",
  
  // Disable minimap for cleaner workspace
  "editor.minimap.enabled": false,
  
  // Format on save - reduces code review noise
  "editor.formatOnSave": true,
}

TypeScript also supports JSONC in tsconfig.json:

{
  "compilerOptions": {
    // Strict mode catches more bugs at compile time
    "strict": true,
    
    // Target ES2022 for top-level await support
    "target": "ES2022",
    
    // Use Node16 module resolution for ESM compatibility
    "moduleResolution": "Node16",
  }
}

Developer configuring JSON settings with comments for team documentation

Environment-Specific Configuration

Since JSON does not support variables, use a layered configuration approach:

Pattern 1: Separate files per environment

config/
  default.json     # Shared settings
  development.json # Dev overrides
  production.json  # Prod overrides
  test.json        # Test overrides

Merge them at runtime:

import defaultConfig from './config/default.json';
import envConfig from `./config/${process.env.NODE_ENV}.json`;

const config = { ...defaultConfig, ...envConfig };

Pattern 2: Environment variables with JSON schema

import { z } from 'zod';

const ConfigSchema = z.object({
  port: z.coerce.number().default(3000),
  databaseUrl: z.string().url(),
  redisUrl: z.string().url().optional(),
  logLevel: z.enum(['debug', 'info', 'warn', 'error']).default('info'),
  corsOrigins: z.string().transform(s => s.split(',')),
});

const config = ConfigSchema.parse(process.env);

Never commit secrets to JSON config files. Database passwords, API keys, and tokens must come from environment variables or a secrets manager. Use our AES Encryption Tool to test encryption of sensitive configuration values.

Schema-Backed Configuration

Many JSON config files have official JSON Schemas that enable IDE autocompletion and error detection:

package.json uses SchemaStore:

{
  "$schema": "https://json.schemastore.org/package.json",
  "name": "my-app",
  "version": "1.0.0"
}

Adding the $schema property tells VS Code to provide autocompletion for all valid fields, show documentation tooltips, and flag invalid values with red underlines.

Common schema store URLs:

  • package.json: https://json.schemastore.org/package.json
  • tsconfig.json: https://json.schemastore.org/tsconfig
  • eslintrc: https://json.schemastore.org/eslintrc
  • prettierrc: https://json.schemastore.org/prettierrc

For more on JSON Schema syntax and creating your own schemas, see our JSON Schema guide.

Configuration Anti-Patterns

Anti-pattern 1: Massive monolithic config files

A single 500-line JSON config file is unreadable. Split by concern: database config, auth config, feature flags, logging config. Merge them at runtime.

Anti-pattern 2: Hardcoded secrets

{
  "database": {
    "password": "super-secret-password"
  }
}

This gets committed to Git, shared in CI logs, and exposed in backups. Use environment variables.

Anti-pattern 3: Duplicate values

{
  "api": {
    "baseUrl": "https://api.example.com",
    "authUrl": "https://api.example.com/auth",
    "usersUrl": "https://api.example.com/users"
  }
}

If the base URL changes, you must update three places. Use a single base URL and construct endpoint URLs in code.

Anti-pattern 4: No validation

Reading a config file without validating it means typos ("databse" instead of "database") silently become undefined values. Always validate config against a schema at startup.

Validate your JSON config files with our JSON Formatter to catch syntax errors before they cause runtime failures.

When to Use JSON vs YAML vs TOML

Use CaseRecommendedWhy
npm/Node.js configJSONEcosystem standard
Docker/K8sYAMLIndustry convention
Rust/Go CLI toolsTOMLDesigned for config
IDE settingsJSONCVS Code standard
App config with commentsJSON5 or YAMLBoth support comments
CI/CD pipelinesYAMLGitHub Actions, GitLab CI standard
API specsJSON or YAMLOpenAPI supports both

For the detailed comparison between formats, read: JSON vs XML vs YAML: When to Use What.

For the complete JSON developer reference, see our pillar guide: The Complete JSON Guide for Web Developers.

The Debuggers helps teams set up robust configuration management for production applications.

Frequently Asked Questions

Can I add comments to package.json?

Officially, no. npm's JSON parser is strict and will fail on comments. However, you can use a "//" key convention: "//": "This is a comment". npm ignores unknown root keys. Some developers also use a separate package.json5 file and transpile it to package.json. Neither approach is ideal, but the "//" convention is the most widely used workaround.

Should I use .env files or JSON for configuration?

Use .env files for secrets and environment-specific values (database URLs, API keys, feature flags). Use JSON for static, version-controlled configuration (compiler options, linting rules, project metadata). The two approaches complement each other: JSON for structure, .env for secrets.

How do I validate a JSON config file before deploying?

Add a validation step to your CI/CD pipeline that loads the config file and validates it against a Zod schema or JSON Schema. If validation fails, the deployment is blocked. This catches typos, missing required fields, and invalid value types before they reach production.

Is TOML better than JSON for configuration?

TOML was designed specifically for configuration files and has features JSON lacks: comments, date/time types, multi-line strings, and clear table syntax. For new projects outside the JavaScript ecosystem (Rust, Go, Python), TOML is often the better choice. For JavaScript projects, JSON is the established convention and has deeper tooling support.


Validate your JSON config files

Use our free JSON Formatter to catch syntax errors in configuration files. Convert config data to typed code with our JSON to TypeScript converter.

Need help with configuration management? The Debuggers provides software architecture consulting for modern web applications.

Need Help Implementing This in a Real Project?

Our team supports end-to-end development for web and mobile software, from architecture to launch.

json configurationjson config filespackage.json guidetsconfig.jsonjson5 vs jsonjsonc

Found this helpful?

Join thousands of developers using our tools to write better code, faster.