JSON Schema for Config Files: VS Code, schemastore.org

Last updated:

JSON Schema is not just for API payloads. It is the technology behind VS Code's autocomplete and error highlighting for tsconfig.json, package.json, GitHub Actions workflows, and hundreds of other config file formats. This guide explains how to wire up schemas to your config files — whether you're using an existing schema from SchemaStore or writing your own.

The $schema Keyword

The $schema key is a special property you can add to any JSON file. Its value is a URI pointing to a JSON Schema document. VS Code reads this URI and uses the schema to power autocomplete, hover docs, and inline error squiggles — no extensions or settings changes required.

// package.json — explicit schema reference
{
  "$schema": "https://json.schemastore.org/package",
  "name": "my-app",
  "version": "1.0.0",
  "scripts": {
    "start": "node index.js"
  }
}

// tsconfig.json — TypeScript ships its own schema
{
  "$schema": "https://json.schemastore.org/tsconfig",
  "compilerOptions": {
    "target": "ES2022",
    "strict": true
  }
}

// .github/workflows/ci.yml — GitHub Actions schema
// # yaml-language-server: $schema=https://json.schemastore.org/github-workflow
name: CI
on: [push]
jobs:
  build:
    runs-on: ubuntu-latest

The $schema key is harmless to tools that consume the config — node, tsc, and eslint all ignore it. It is purely an editor hint. For YAML files, use the yaml-language-server modeline comment instead (covered in section 6).

How VS Code resolves the schema URI

VS Code's built-in JSON language server fetches the schema URI on first use and caches it. For https:// URIs it makes an outbound HTTPS request. For ./ or / paths it resolves relative to the workspace. The cache is stored in VS Code's global storage; schemas are re-fetched when stale. In air-gapped environments, use a local path or a workspace-relative URL.

SchemaStore.org: 500+ Ready-Made Schemas

SchemaStore.org is an open-source repository of JSON Schema files maintained by the community. VS Code queries SchemaStore's catalog automatically: when you open a file named tsconfig.json, VS Code matches the filename against SchemaStore's fileMatch patterns and applies the schema transparently — no $schema key needed.

Config fileSchemaStore URL
package.jsonhttps://json.schemastore.org/package
tsconfig.jsonhttps://json.schemastore.org/tsconfig
.eslintrc.jsonhttps://json.schemastore.org/eslintrc
docker-compose.ymlhttps://json.schemastore.org/docker-compose
.prettierrchttps://json.schemastore.org/prettierrc
.babelrchttps://json.schemastore.org/babelrc
github-workflow.ymlhttps://json.schemastore.org/github-workflow

You can browse the full catalog at schemastore.org/json. Each entry shows the schema URL, file match patterns, and a description. The project is hosted on GitHub and contributions are welcome via pull request.

VS Code: Autocomplete and Validation

VS Code has two ways to associate schemas with files: the $schema key inside the file (section 1), and the json.schemas workspace or user setting. The setting approach is useful when you cannot modify the config file — for example, a vendor config or a file where $schema would cause a parse error.

// .vscode/settings.json — workspace-level schema associations
{
  "json.schemas": [
    {
      // Apply SchemaStore package schema to package.json
      "fileMatch": ["package.json"],
      "url": "https://json.schemastore.org/package"
    },
    {
      // Apply a local schema to all *.myapp.json files
      "fileMatch": ["**/*.myapp.json"],
      "url": "./schemas/myapp-config.schema.json"
    },
    {
      // Inline schema directly in settings (small schemas only)
      "fileMatch": [".featureflags.json"],
      "schema": {
        "type": "object",
        "additionalProperties": { "type": "boolean" }
      }
    }
  ]
}

The fileMatch array supports glob patterns. "**/.myapprc" matches .myapprc at any directory depth. "url" accepts both HTTPS URLs and workspace-relative paths (starting with ./).

Validating JSONC (JSON with Comments)

Files like tsconfig.json and .vscode/settings.json use JSONC — JSON extended with // and /* */ comments. VS Code treats these as a distinct language mode. To apply a schema to a .jsonc file, add it to jsonc.schemas (not json.schemas):

{
  "jsonc.schemas": [
    {
      "fileMatch": ["*.config.jsonc"],
      "url": "./schemas/myapp-config.schema.json"
    }
  ]
}

Writing a Schema for Your Own Config File

If your tool ships a custom config file format, providing a JSON Schema dramatically improves the developer experience. Here is a complete example for a hypothetical myapp.config.json:

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://example.com/myapp-config.schema.json",
  "title": "MyApp Config",
  "description": "Configuration file for MyApp v2",
  "type": "object",
  "properties": {
    "port": {
      "type": "integer",
      "minimum": 1,
      "maximum": 65535,
      "default": 3000,
      "description": "HTTP port to listen on"
    },
    "logLevel": {
      "enum": ["debug", "info", "warn", "error"],
      "default": "info",
      "description": "Minimum log level"
    },
    "database": {
      "type": "object",
      "properties": {
        "url": {
          "type": "string",
          "format": "uri",
          "description": "PostgreSQL connection string"
        },
        "poolSize": {
          "type": "integer",
          "minimum": 1,
          "maximum": 100,
          "default": 10
        }
      },
      "required": ["url"],
      "additionalProperties": false
    },
    "features": {
      "type": "object",
      "description": "Feature flags — any key, boolean value",
      "additionalProperties": { "type": "boolean" }
    }
  },
  "required": ["port", "database"],
  "additionalProperties": false
}

Key schema design tips for config files:

  • Add description to every property — VS Code shows this as hover documentation
  • Add default values so users know what they'll get if they omit a key
  • Use additionalProperties: false at the top level to catch typos in key names
  • Use enum for string fields with a fixed set of values — VS Code shows a dropdown
  • Set $id to the URL where you'll serve the schema so $ref resolution works correctly

Submitting to SchemaStore

Once your schema is stable, submit it to SchemaStore so VS Code automatically applies it for all users of your tool. Fork github.com/SchemaStore/schemastore, add your schema file to src/schemas/json/, and register it in src/api/json/catalog.json:

// src/api/json/catalog.json — add an entry like this:
{
  "name": "MyApp Config",
  "description": "Configuration for MyApp v2+",
  "fileMatch": ["myapp.config.json", ".myapprc", ".myapprc.json"],
  "url": "https://json.schemastore.org/myapp-config"
}

CI Validation with ajv-cli

Editor validation is great for development, but CI validation ensures config files are never merged in an invalid state. ajv-cli is a command-line wrapper around the Ajv JSON Schema validator.

# Install
npm install -D ajv-cli ajv-formats

# Validate a single file
npx ajv-cli validate -s myapp-config.schema.json -d myapp.config.json

# Validate multiple files with a glob
npx ajv-cli validate -s myapp-config.schema.json -d "configs/*.json"

# Enable Draft 2020-12 and string formats (email, uri, date-time)
npx ajv-cli validate \
  --spec=draft2020 \
  -c ajv-formats \
  -s myapp-config.schema.json \
  -d myapp.config.json

# Output: myapp.config.json valid
# Exit code 0 = valid, 1 = invalid

GitHub Actions integration

# .github/workflows/validate-config.yml
name: Validate Config Files
on: [push, pull_request]
jobs:
  validate:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: '20'
      - run: npm ci
      - name: Validate myapp.config.json
        run: |
          npx ajv-cli validate \
            --spec=draft2020 \
            -c ajv-formats \
            -s schemas/myapp-config.schema.json \
            -d myapp.config.json

Programmatic validation in tests

// validate-config.test.ts
import Ajv from 'ajv/dist/2020'
import addFormats from 'ajv-formats'
import schema from './schemas/myapp-config.schema.json'
import config from './myapp.config.json'

const ajv = new Ajv()
addFormats(ajv)
const validate = ajv.compile(schema)

test('myapp.config.json is valid', () => {
  const valid = validate(config)
  if (!valid) console.error(validate.errors)
  expect(valid).toBe(true)
})

YAML Config Files with JSON Schema

JSON Schema validates data structure independently of serialization format. The same schema that validates a JSON config validates the equivalent YAML config. VS Code uses the Red Hat YAML extension to provide schema-driven autocomplete for YAML files.

Modeline comment (per-file)

# yaml-language-server: $schema=https://json.schemastore.org/github-workflow
name: CI
on:
  push:
    branches: [main]
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

VS Code yaml.schemas setting (project-wide)

// .vscode/settings.json
{
  "yaml.schemas": {
    "https://json.schemastore.org/github-workflow": [
      ".github/workflows/*.yml",
      ".github/workflows/*.yaml"
    ],
    "./schemas/myapp-config.schema.json": [
      "myapp.*.yml",
      "config/*.yaml"
    ]
  }
}

CI validation for YAML configs

ajv-cli validates JSON only. For YAML, convert to JSON first with js-yaml, or use @stoplight/spectral-cli which supports YAML natively with JSON Schema rulesets:

# Convert YAML to JSON then validate with ajv-cli
node -e "
  const yaml = require('js-yaml')
  const fs = require('fs')
  const data = yaml.load(fs.readFileSync('myapp.config.yml', 'utf8'))
  fs.writeFileSync('/tmp/myapp.config.json', JSON.stringify(data))
"
npx ajv-cli validate -s schemas/myapp-config.schema.json -d /tmp/myapp.config.json

Schema Versioning and $id

As your config file format evolves, you need to manage multiple schema versions without breaking existing consumers. The $id keyword is the key to this.

// Version 1 schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://example.com/schemas/myapp-config/v1.json",
  "title": "MyApp Config v1",
  ...
}

// Version 2 schema (additive changes)
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://example.com/schemas/myapp-config/v2.json",
  "title": "MyApp Config v2",
  ...
}

// Config file pinning to a specific version
{
  "$schema": "https://example.com/schemas/myapp-config/v1.json",
  "port": 3000
}

Versioning strategies:

  • URL versioning: /schemas/myapp/v1.json, /schemas/myapp/v2.json — explicit, easy to pin
  • Latest alias: always serve the latest at /schemas/myapp/latest.json — easy to consume but no pinning
  • Semantic versioning in $id: include major version only, e.g. /v1/, so minor/patch updates are transparent
  • Never remove old schema URLs — configs in the wild may reference them for years

$id and $ref resolution

When a schema uses relative $ref values, the base URI for resolution is the schema's $id. Without $id, the base URI is the URL from which the schema was fetched — which may differ between environments. Setting $id explicitly makes your schema portable:

{
  "$id": "https://example.com/schemas/myapp-config/v2.json",
  "$defs": {
    "DatabaseConfig": { ... }
  },
  "properties": {
    // Relative $ref resolves against $id base
    "database": { "$ref": "#/$defs/DatabaseConfig" },
    // Cross-schema $ref to the v1 Address type
    "address": { "$ref": "https://example.com/schemas/shared/v1.json#/$defs/Address" }
  }
}

Definitions

$schema
A JSON property whose value is a URI pointing to the JSON Schema document that describes the file. VS Code and other editors use this URI to enable autocomplete, hover documentation, and inline validation. The key is ignored by most runtime tools that consume the config.
SchemaStore
An open-source community project at schemastore.org that maintains a catalog of 500+ JSON Schema files for popular config formats. VS Code queries the SchemaStore catalog automatically and applies matching schemas to recognized filenames without requiring a $schema key or any user configuration.
json.schemas
A VS Code setting (in settings.json) that maps file glob patterns to schema URLs or inline schema objects. Allows schema association without modifying the config file. Works in both user-level settings (global) and workspace .vscode/settings.json (project-specific).
ajv-cli
A command-line interface for the Ajv JSON Schema validator. Accepts a schema file (-s) and one or more data files (-d), returns exit code 0 for valid and exit code 1 for invalid. Used in CI pipelines to block merges when config files fail schema validation.
yaml-language-server
A language server (used by VS Code's Red Hat YAML extension) that provides schema-driven autocomplete and validation for YAML files. Supports a modeline comment syntax — # yaml-language-server: $schema=<url> — to associate a schema with a specific YAML file without modifying the file structure.

FAQ

What does the "$schema" key do in a JSON config file?

The $schema key is a URI pointing to a JSON Schema document. VS Code reads this URI and uses the schema to power autocomplete, hover documentation, and error highlighting — with no extension or configuration required. The key is safe to add to any JSON config: tools like node, tsc, and eslint simply ignore it. For YAML files, use the # yaml-language-server: $schema=... modeline comment instead.

What is SchemaStore and how do I use it?

SchemaStore (schemastore.org) is an open-source community project hosting 500+ JSON Schema files for popular config formats. VS Code automatically queries the SchemaStore catalog and applies matching schemas to known filenames — tsconfig.json gets schema validation with no setup at all. You can also reference schemas explicitly by adding {""$schema": "https://json.schemastore.org/package""} to package.json. Contributions are accepted via pull request to the SchemaStore GitHub repository.

How do I add JSON Schema to VS Code without modifying the config file?

Use the json.schemas setting in .vscode/settings.json. Add an entry with a fileMatch glob array and a url pointing to your schema. This is ideal for internal configs, vendor configs, or any file where adding $schema would cause issues. The setting works at both user level (global, in your home directory's settings.json) and workspace level (.vscode/settings.json checked into the project).

How do I write a JSON Schema for my own config file?

Start with $schema, $id, title, and type: "object". Add a properties block describing each config key with its type, constraints, default, and description. Set additionalProperties: false to catch typos in key names. Use enum for fields with a fixed set of values. Once stable, submit your schema to SchemaStore so VS Code applies it automatically for all users of your tool.

How do I validate config files in CI with ajv-cli?

Install ajv-cli as a dev dependency: npm install -D ajv-cli ajv-formats. Run npx ajv-cli validate -s schema.json -d config.json. Exit code 0 means valid, exit code 1 means invalid — CI pipelines use this to block merges automatically. Add --spec=draft2020 -c ajv-formats to enable Draft 2020-12 and string format validation. For YAML configs, convert to JSON first with js-yaml before passing to ajv-cli.

How does JSON Schema work with YAML config files?

JSON Schema validates data structure and types regardless of serialization format — the same schema works for both JSON and YAML configs. In VS Code with the Red Hat YAML extension, add a # yaml-language-server: $schema=<url> comment at the top of the YAML file, or configure yaml.schemas in .vscode/settings.json to apply schemas to file globs. For CI validation, convert YAML to JSON with js-yaml then validate with ajv-cli, or use a YAML-native validator like @stoplight/spectral-cli.

What is the $id keyword and why does it matter for schemas?

$id sets the canonical URI for a schema. It serves as the base URI for resolving relative $ref values within the schema, and as a stable identifier for caching and cross-schema references. For schemas you publish (e.g., on SchemaStore or your CDN), $id should match the URL where the schema is served. When versioning schemas, embed the version in the $id URI — this lets you maintain multiple versions without breaking existing consumers who pin to a specific URL.

How do I submit my schema to SchemaStore?

Fork github.com/SchemaStore/schemastore, add your schema JSON to src/schemas/json/, and register it in src/api/json/catalog.json with a name, description, fileMatch array, and url. The SchemaStore CI validates all schemas with ajv, so ensure yours is valid JSON Schema Draft-07 or later. Once merged, VS Code and any editor with built-in SchemaStore support will automatically apply your schema to matching filenames worldwide — zero configuration required for your tool's users.

Further reading and primary sources

  • SchemaStore.org500+ JSON Schema files for popular config file formats
  • VS Code JSON SchemaVS Code JSON schema association and validation documentation
  • ajv-cliCommand-line interface for Ajv JSON Schema validator