JSON alternatives for configuration files

| 7 min read

JSON is one of the most mainstream, universal data formats out there. It is loved because of its simplicity and because it is human-readable, making it easy to use. It is also criticized because it is verbose, it doesn’t support comments, it has limited data types, and it doesn’t enforce a data structure using a schema.

So, is there anything better than JSON? Before we can answer that question, we have to understand that JSON is used in two very different cases. We’ll discuss these two categories in two separate articles:

  1. JSON alternatives for configuration files (this article)
  2. JSON alternatives for data transfer and storage (click here to read)

These two categories come with different needs. For example, for configuration files it is important that data format is easy to read and supports comments for documentation. For data transfer and storage, it is important that the data format is concise and can be serialized and de-serialized in a fast way.

In this article we discuss the first category: JSON alternatives for configuration files. Click here to read the article about JSON alternatives for data instead. We’ll first discuss how well JSON itself does for configuration, and then discuss a number of alternatives.

JSON

When to use: JSON can be fine to use for configuration files of programming projects because it is so easy to parse and use.

Website: https://www.json.org/

Using JSON for configuration files is popular in the JavaScript ecosystem. For example npm uses a file package.json to configure dependencies, entry files, and other settings of a JavaScript or Node.js project. Many tools in the JavaScript ecosystem support configuration via a JSON file. For example for transpilers (TypeScript, Babel), bundlers (Webpack), test frameworks (Vitest, Mocha, Jest), linters (ESLint, Prettier), and more.

About JSON itself: it’s not bad for configuration, it’s OK. It is flexible enough to allow configuring variables and nested variables in a structured way. An upside is that it is easy to parse programmatically, and it is very well-supported by many tools and languages.

The downsides of using JSON for a configuration file are that it doesn’t support comments, and that it is relatively verbose, requiring double quotes around all keys and values. If you’re pragmatic, you can probably live with those limitations. JSON is not perfect for configuration, but it can do the job.

Here is an example of a package.json file for a JavaScript project:

{
  "name": "json-config-example",
  "version": "1.0.0",
  "description": "A demo of a package.json file",
  "main": "index.js",
  "type": "module",
  "scripts": {
    "test": "vitest src",
    "build": "tsc"
  },
  "keywords": ["demo", "package.json"],
  "author": "Jos de Jong",
  "license": "ISC",
  "devDependencies": {
    "typescript": "5.2.2",
    "vitest": "0.34.6"
  },
  "dependencies": {
    "lodash-es": "4.17.21"
  }
}

JavaScript

When to use: JavaScript is perfect for configuration files in a JavaScript project.

Website: https://developer.mozilla.org/en-US/docs/Web/JavaScript

A common alternative to JSON for configuration in a JavaScript project is: JavaScript. Using JavaScript for a configuration file solves all the downsides that JSON has for this purpose: you can insert comments, omit quotes around keys. And you get a lot of extra flexibility to organize the configuration, import and extend another config file, or do dynamic things in the configuration. All in all, it is a very pragmatic solution to improve upon JSON without reinventing the wheel.

Here is a Rollup config file, where you can see that a dynamic JavaScript plugin for JSON is used:

// rollup.config.mjs
import json from '@rollup/plugin-json'

export default {
  input: 'src/main.js',
  output: {
    file: 'bundle.js',
    format: 'cjs'
  },
  plugins: [json()]
}

Most of the aforementioned JavaScript tools do support both JSON and JavaScript for their configuration, and in general it is not hard to switch from one to the other or vice versa.

Many JavaScript tools are moving from JSON towards JavaScript because this gives much more flexibility. Since the user is already in a JavaScript environment, it is a very natural fit. One possible risk is that the freedom that JavaScript gives you can also result in complicated configuration with lots of dependencies and dynamic cases, making it hard to maintain.

DotEnv

When to use: DotEnv is perfect for a simple list with configuration variables for a backend.

Website: https://www.dotenv.org/

Where JavaScript configuration files come with more flexibility but only shines in a JavaScript environment, DotEnv is a really simple and limited solution. It can hold a list of variables with a value, and optionally a comment. It loads these variables as environment variables in the backend application. These variables are normally secrets, like a database url and password, and environment specific variables that differ in a local vs development vs production environment. What makes DotEnv so powerful is that it is simple and universal. It can be used in almost all backend environments and frameworks.

Here is an example:

# example .env file
STRIPE_API_KEY=ced0bc6b733f
TWILIO_API_KEY=1f1f56df3ae3

Yaml

When to use: please don’t use Yaml, there are much better alternatives.

Website: https://yaml.org/

Yaml is a widely used configuration language. It was intended as a replacement of JSON for configuration. It labels itself as a “human-friendly” language, but in practice it is quite hard to write, error-prone, and hard to debug. There are a lot of critics about Yaml. Indentation is significant and it supports multiline strings. All in all, the standard is quite ambiguous and too flexible for its own good. It is not always clear how to indent and when to use a minus sign in front of a value.

Here is an example of a Yaml configuration file found on Wikipedia:

receipt: Oz-Ware Purchase Invoice
date: 2012-08-06
customer:
  first_name: Dorothy
  family_name: Gale

items:
  - part_no: A4786
    descrip: Water Bucket (Filled)
    price: 1.47
    quantity: 4

  - part_no: E1628
    descrip: High Heeled "Ruby" Slippers
    size: 8
    price: 133.7
    quantity: 1

bill-to: &id001
  street: |
    123 Tornado Alley
    Suite 16
  city: East Centerville
  state: KS

ship-to: \*id001

specialDelivery: >
  Follow the Yellow Brick
  Road to the Emerald City.
  Pay no attention to the
  man behind the curtain.

JSON5

When to use: JSON5 is good for: configuration files, especially when migrating them from JSON.

Website: https://json5.org/

JSON5 is a solid alternative to JSON. It is an extension of JSON and specifically aims to make it easy for humans to write configuration. More and more JavaScript tools support JSON5 as an alternative for JSON, for example Webpack, Babel, and pnpm. JSON5 is a superset of JSON, so migrating an existing configuration file from JSON to JSON5 is just a matter of changing the file extension. Most important features solve most of the aforementioned limitations in JSON: quotes around keys are optional, comments are supported, and there is support for multi-line strings and trailing commas.

The package.json file shown before can look as follows in JSON5:

{
  name: 'json-config-example',
  version: '1.0.0',
  description: 'A demo of a package.json file',
  main: 'index.js',
  type: 'module',
  scripts: {
    test: 'vitest src',
    build: 'tsc'
  },
  keywords: ['demo', 'package.json'],
  author: 'Jos de Jong',
  license: 'ISC',
  devDependencies: {
    typescript: '4.9.3',
    vitest: '0.34.6'
  },
  dependencies: {
    'lodash-es': '4.17.21'
  }
}

Note that JSON5 is not a newer version of JSON. JSON doesn’t have versions: JSON is just JSON and will always be.

Toml

When to use: Toml is perfect for configuration files.

Website: https://toml.io/

Where JSON5 and Yaml were designed to improve upon using JSON for configuration, Toml is designed for configuration files from the ground up. This shows when you look at an example. It is easy to read and unambiguous. It doesn’t have all the nesting that JSON, JSON5 and Yaml have. Yet it is much more flexible than DotEnv, supporting groups, nested variables, and arrays in a compact way. On the downside, Toml is criticized for being too complicated and difficult to parse, like Yaml. Toml is for example used by the programming languages Rust and Python.

Here an example:

# This is a TOML document

title = "TOML Example"

[owner]
name = "Tom Preston-Werner"
dob = 1979-05-27T07:32:00-08:00

[database]
enabled = true
ports = [ 8000, 8001, 8002 ]
data = [ ["delta", "phi"], [3.14] ]
temp_targets = { cpu = 79.5, case = 72.0 }

[servers.alpha]
ip = "10.0.0.1"
role = "frontend"

[servers.beta]
ip = "10.0.0.2"
role = "backend"

Conclusion about JSON alternatives for configuration files

Always try to find the most simple, solid solution that fits your use case. When taking a sophisticated solution or a non-mainstream solution, it is more likely to get into trouble. For example, though they are limited, JSON and DotEnv (or simply environment variables) are simple, proven, and universally usable. They are therefore a safe and solid choice.

If you need more, you can probably use JavaScript, JSON5, or Toml. It is important though to check whether it is supported by the tooling and environments that you’re using.