JSONC to JSON — Remove Comments from JSON

Examples:

Last updated: 2026-09-17

Paste JSONC (JSON with comments) to strip all // and /* */ comments and trailing commas, producing valid standard JSON. Comments inside string values are preserved. Everything runs locally in your browser.

What this tool does

Strips // line comments and /* */ block comments from JSONC input, removes trailing commas, and produces valid standard JSON. Comment-like text inside string values is preserved. The tool reports how many comments were removed so you can verify nothing important was lost.

Example

Input (tsconfig.json with comments):

{
  // Compiler options
  "compilerOptions": {
    "target": "ES2020", // modern target
    "strict": true,
    /* module resolution */
    "moduleResolution": "node"
  }
}

Output:

{
  "compilerOptions": {
    "target": "ES2020",
    "strict": true,
    "moduleResolution": "node"
  }
}

The tool reports “Removed 3 comments” so you know exactly what was stripped.

JSON vs JSONC vs JSON5

FeatureJSONJSONCJSON5
CommentsNo// and /* */// and /* */
Trailing commasNoYesYes
Single quotesNoNoYes
Unquoted keysNoNoYes
Hex numbersNoNoYes
Multi-line stringsNoNoYes
Used byAPIs, data interchangetsconfig, VS Code, devcontainersConfig files, modern tooling

Common JSONC files

All of these files use JSONC but tools that consume them have their own parsers. When you need to feed them to a standard JSON parser (like JSON.parse in Node.js or json.loads in Python), this tool strips the comments and trailing commas first. This matters in pipelines that diff a checked-in configuration against a generated baseline, in documentation generators that read the raw file, and in migration scripts that move settings between editors without carrying the explanatory comments along.

Converting tsconfig.json

tsconfig.json is the file people most often paste into this tool. TypeScript treats it as JSONC, so teams annotate every compiler option with a line comment explaining why it was chosen. A realistic project configuration might look like this:

{
  // Root of the TypeScript project
  // Keep this in sync with the build script in package.json
  "compilerOptions": {
    "target": "ES2021",                 // ship to modern browsers only
    "lib": ["ES2021", "DOM", "DOM.Iterable"], // type the platform we run on
    "module": "ESNext",                 // let the bundler handle module output
    "moduleResolution": "Bundler",      // matches Vite and Webpack 5
    "strict": true,                     // turn on every strict flag
    "noUncheckedIndexedAccess": true,   // force undefined checks on array access
    "noImplicitOverride": true,         // require the override keyword
    "exactOptionalPropertyTypes": true, // forbid explicit undefined on optionals
    /* Project layout */
    "rootDir": "src",
    "outDir": "dist",
    "resolveJsonModule": true,          // import .json files as modules
    "esModuleInterop": true,            // allow default imports from CommonJS
    "skipLibCheck": true,               // faster builds, trust shipped types
    "isolatedModules": true,            // each file compiles independently
    "verbatimModuleSyntax": true        // preserve import / export shapes
  },
  "include": ["src"],
  "exclude": [
    "node_modules",
    "dist",
    "**/*.test.ts",   // tested by vitest, not the compiler
    "vite.config.ts"  // built separately
  ]
}

The trailing comma after "vite.config.ts" is legal in JSONC but would throw a parse error in JSON.parse. After conversion the output is plain JSON that any linter, schema validator, or runtime loader can read. The tool counts every // line comment and every /* */ block comment as one removal, so the report for this file reads “Removed 18 comments”.

Converting VS Code settings.json

Workspace settings live in .vscode/settings.json and are written as JSONC. Teams check them in with comments explaining which editor behaviour is enforced and why. A typical file:

{
  // Formatting — enforced on save across the whole team
  "editor.formatOnSave": true,
  "editor.defaultFormatter": "esbenp.prettier-vscode",
  "editor.tabSize": 2, // matches the Prettier config
  "editor.insertSpaces": true,

  // TypeScript in the editor
  "typescript.tsdk": "node_modules/typescript/lib",
  "typescript.enablePromptUseWorkspaceTsdk": true,
  "typescript.preferences.importModuleSpecifier": "relative",
  "typescript.updateImportsOnFileMove": "always",

  /* Files we never want opened by mistake */
  "files.exclude": {
    "**/.git": true,
    "**/.DS_Store": true,
    "**/node_modules": true,
    "**/dist": true
  },
  "search.exclude": {
    "**/package-lock.json": true, // too noisy in search results
    "**/yarn.lock": true
  }
}

Pasting this into the tool removes every comment and the trailing comma after the last entry in files.exclude. The cleaned JSON can be loaded by a script that diffs workspace settings against a shared baseline, or by a CI check that forbids certain keys.

Converting devcontainer.json

A dev container definition in .devcontainer/devcontainer.json mixes VS Code extension settings, Docker build arguments, and lifecycle hooks, all annotated with comments. A representative file:

{
  "name": "Node 20 service container",
  // Pull the official image and add our toolchain on top
  "image": "mcr.microsoft.com/devcontainers/javascript-node:20",
  "features": {
    "ghcr.io/devcontainers/features/docker-outside-of-docker:1": {}, // sibling Docker
    "ghcr.io/devcontainers/features/github-cli:1": {}                // gh inside the box
  },
  "forwardPorts": [3000, 5173], // app server and the dev server
  "portsAttributes": {
    "3000": { "label": "app", "onAutoForward": "notify" },
    "5173": { "label": "vite", "onAutoForward": "openBrowser" }
  },
  /* Run after the container is created, once */
  "postCreateCommand": "npm install",
  // Run before each attach
  "postStartCommand": "git config --global --add safe.directory ${containerWorkspaceFolder}",
  "customizations": {
    "vscode": {
      "extensions": [
        "dbaeumer.vscode-eslint",
        "esbenp.prettier-vscode",
        "ms-python.python" // for the occasional script
      ],
      "settings": {
        "editor.formatOnSave": true
      }
    }
  },
  "remoteEnv": {
    "LOCAL_WORKSPACE_FOLDER": "${localWorkspaceFolder}"
  }
}

Running this through the tool leaves the structure intact and strips the comments and the trailing comma after the last extension. The output is safe to hand to JSON.parse inside a Node script that generates container documentation from the definition.

How the parser distinguishes comments from strings

The conversion is a single left-to-right scan over the input. At every position the parser asks one question: are we currently inside a quoted string? The answer decides whether a / character starts a comment or is just text.

  1. String mode. When the parser meets a double quote, it copies the quote and switches into string mode. It keeps copying characters until it hits the closing double quote. While inside the string, // and /* are treated as ordinary characters and copied verbatim, so a value like "https://example.com" or "a /* b */" is preserved exactly. The parser also honours the backslash escape character: when it sees a backslash inside a string, it copies the backslash and the next character together, so an escaped quote \" does not end the string.

  2. Line comment mode. Outside a string, a / immediately followed by another / starts a line comment. The parser skips forward until the next newline character, counts one comment, and resumes normal scanning on the newline.

  3. Block comment mode. Outside a string, a / immediately followed by * starts a block comment. The parser skips forward until it finds */, counts one comment, and continues after the closing slash. Block comments can span many lines.

  4. Default. Any other character is copied to the output unchanged.

After the scan, two cleanup passes run on the result. A regular expression removes a comma that appears immediately before a closing } or ], allowing whitespace between them. Then each line is trimmed of trailing whitespace, and lines that are empty after trimming are dropped, so removing a comment does not leave a blank gap. Finally the cleaned text is passed to JSON.parse; if it succeeds the output is returned, and if it fails the cleaned text is still returned alongside the parse error.

Common mistakes when stripping comments

Reading the reported comment count

After conversion the tool prints “Removed N comments” where N is the number of comment markers stripped from the input. Each // line comment counts as one, regardless of how many lines it occupies, and each /* */ block comment also counts as one, even when it spans dozens of lines. A file with five short inline notes and one large header block therefore reports six removals.

The count is computed during the scan, before the trailing-comma and empty-line cleanup passes run, so it reflects exactly what was removed from the source rather than what the finished output looks like. If the scan finishes but JSON.parse then rejects the cleaned text, the count is still reported alongside the error, because the comments were genuinely removed even though leftover syntax prevented validation. Comparing the reported number against the comments you expected to see is a quick way to confirm the conversion matched your intent before you paste the output anywhere.

Limitations and what this tool does not do

The converter targets JSONC only. It strips comments and trailing commas and then validates with JSON.parse, so anything else that standard JSON rejects will still fail. Single-quoted strings, unquoted object keys, hexadecimal literals, leading decimal points, and multi-line template strings are JSON5 features and are not transformed. If your file uses them, the cleaned text is returned with the parse error so you can decide whether to rewrite those parts by hand or switch to a JSON5 parser.

The tool does not reformat, minify, or re-indent the output. It preserves the indentation of the original lines and only trims trailing whitespace from each line. Run a formatter or minifier afterwards if you want a different layout. Nested block comments are not supported because JSONC itself does not allow them; a /* */ block ends at the first */, and any later */ becomes ordinary text that will likely break validation. Finally, the converter does not interpret comment directives such as @ts-expect-error — those are comments, so they are removed along with everything else between the markers. The entire conversion runs locally inside your browser; nothing is uploaded, which makes the tool safe for proprietary configuration files.

Do it in code

Node.js (strip-json-comments):

const stripJsonComments = require('strip-json-comments');
const cleanJson = stripJsonComments(jsoncString);
const config = JSON.parse(cleanJson);

Python:

import re, json
def strip_comments(jsonc):
    # Remove // line comments (not inside strings)
    jsonc = re.sub(r'//.*?$', '', jsonc, flags=re.MULTILINE)
    # Remove /* */ block comments
    jsonc = re.sub(r'/\*.*?\*/', '', jsonc, flags=re.DOTALL)
    # Remove trailing commas
    jsonc = re.sub(r',(\s*[}\]])', r'\1', jsonc)
    return json.loads(jsonc)

CLI (jq with strip-json-comments):

npx strip-json-comments tsconfig.json | jq '.'

Edge cases handled

Frequently asked questions

Why does JSON not allow comments?
The JSON specification (RFC 8259) deliberately excludes comments to keep the format simple and data-only. Douglas Crockford, JSON's creator, removed comments to prevent parsing directives from being embedded in data. Use JSONC or JSON5 if you need comments in your configuration.
What is JSONC?
JSONC (JSON with Comments) is a non-standard extension used by tools like TypeScript (tsconfig.json) and VS Code (settings.json). It allows // and /* */ comments and trailing commas, which standard JSON does not. There is no formal JSONC specification — it is a convention adopted by tooling.
Are comments inside string values preserved?
Yes. The tool correctly distinguishes between comments (outside strings) and comment-like text inside string values. A string value that contains a double slash is preserved as-is because the slashes are inside a quoted string. The parser tracks string boundaries so comment markers between quotes are treated as ordinary characters.
How are trailing commas handled?
Trailing commas after the last item in an object or array are removed automatically. This is the most common cause of JSON parse errors when copying from JSONC files into a standard JSON parser. The tool removes a comma that sits immediately before a closing brace or bracket, with any whitespace between them.
Can I use this for tsconfig.json?
Yes. tsconfig.json files are JSONC and contain comments. This tool strips the comments and trailing commas, producing clean JSON that any standard JSON parser can read. The same applies to VS Code settings.json, devcontainer.json, and jsconfig.json files.
What is the difference between JSONC and JSON5?
JSONC only adds comments and trailing commas to standard JSON. JSON5 goes much further — it allows unquoted keys, single-quoted strings, hexadecimal numbers, leading decimal points, multi-line strings, and more. This tool handles JSONC, not JSON5. Passing JSON5 input will likely fail validation because JSON5 features are not converted.
How do I strip comments from JSONC in Node.js?
Use the strip-json-comments package: run stripJsonComments(jsoncString). It removes // and /* */ comments while preserving them inside string values. Install with npm install strip-json-comments. This tool does the same thing in the browser.
What happens when the cleaned output fails validation?
The tool still returns the cleaned text along with the error message from JSON.parse so you can inspect and fix the remaining issue by hand. Common remaining issues are JSON5 syntax the converter does not transform, such as single-quoted strings or unquoted object keys. The reported comment count still reflects how many comments were removed before validation ran.

Related tools