JavaScript Beautifier
Paste minified or single-line JavaScript and get it back indented and split into readable statements.
About the JavaScript Beautifier
This is the opposite direction from the JavaScript Minifier: it expands compressed code back into readable form. Under the hood it's a small tokenizer, not a find-and-replace script, so it reads strings, template literals, line and block comments, and regex literals as atomic units before deciding how to lay anything out. That matters because a slash can mean division or the start of a regex depending on what came before it, and this tool checks the preceding token (things like =, (, return, or ,) to tell the two apart rather than guessing. Indentation tracks bracket depth from { } ( ) [ ], and each statement lands on its own line.
The actual grammar rule behind the slash ambiguity
Whether / means "divide" or "start a regex" comes down to a real rule in JavaScript's grammar: a regex literal can only appear where the language expects a new expression to begin, never where it expects an operator. After an identifier, a number, a closing ), or a closing ], JavaScript is expecting an operator next, so a following slash has to be division. But after an opening (, a comma, an =, or keywords like return or typeof, the grammar is expecting a fresh expression, exactly where a regex is legal. This tool's REGEX_START_TOKENS set encodes precisely that list of expression-expecting contexts, and checking the immediately preceding token against it is what correctly separates a / b (division) from return /foo/g (a regex) without ever needing to fully understand the surrounding code.
Why the regex scanner tracks character classes separately
Once the tokenizer decides it's looking at a regex literal, it still has to find where that regex actually ends, and a raw, unescaped / is legal inside a character class like [a/b] without terminating the regex. The scanner tracks whether it's currently inside square brackets specifically for this reason, a / encountered while inClass is true gets treated as ordinary regex content, not the closing delimiter, so a pattern like /[html/xml]/ is captured correctly as one complete token instead of getting cut off early. After finding the real closing slash, it also scans forward for any trailing flag letters (g, i, m, and so on) so they stay attached to the regex token rather than getting split off as separate identifiers.
Why template literals are treated as one opaque block
Backtick-delimited template literals are captured as a single atomic token from the opening backtick to the matching closing one, including everything inside any ${...} interpolation. That's a deliberate scope limitation: this tokenizer doesn't recursively parse expressions embedded inside template interpolations, so a long or complex expression inside ${ } comes through exactly as written rather than being reformatted or reindented along with the surrounding code. For typical short interpolations this is invisible, but a template literal with a genuinely large expression inside its ${ } won't get the same beautification treatment the rest of the file does.
Why function calls and control structures get different parenthesis spacing
The renderer specifically checks what came immediately before an opening (: if it follows an identifier or a closing )/] that isn't a keyword like if, for, or while, it's treated as a function call and gets no space before the parenthesis, producing myFunction(x). If it follows one of those control-flow keywords instead, a space is inserted, producing the more conventional if (x) or while (x). This mirrors the spacing convention most style guides and formatters already follow, distinguishing "calling something" from "a control structure's condition" purely from the token that came right before the parenthesis.
Why only curly braces trigger a new line, not parentheses or brackets
All three bracket types, { }, ( ), and [ ], are tracked for indentation depth, but only an opening { actually forces a line break and bumps subsequent code onto a new indented line. Parentheses and square brackets just quietly track how deep the current expression is nested without forcing a break. That's why a function call with several arguments, doSomething(a, b, c), stays on one line rather than exploding each argument onto its own line, while an actual code block always gets its own indented section, matching how JavaScript is conventionally written by hand.
Frequently Asked Questions
How does the tool know whether a "/" means division or the start of a regex?
It checks the token that came immediately before the slash against a set of "expression-expecting" contexts, like after (, =, return, or a comma. JavaScript's grammar only allows a regex literal where a new expression is expected, so this token-context check correctly distinguishes a/b (division) from return /foo/g (a regex).
Can a "/" inside a regex character class like [a/b] break the parsing?
No, the scanner tracks whether it's currently inside square brackets while reading a regex literal, and treats a slash found inside that character class as ordinary content rather than the closing delimiter, so patterns like /[html/xml]/ are captured correctly as one token.
Does the beautifier reformat expressions inside template literal interpolations?
No, a template literal is captured as one complete token from opening to closing backtick, including everything inside any ${ } interpolation. Content inside ${ } passes through exactly as written rather than being reformatted along with the surrounding code.
Why does the output write myFunction(x) with no space, but if (x) with a space?
The renderer checks what precedes an opening parenthesis: after an identifier or closing bracket that isn't a control-flow keyword, it's treated as a function call with no space before the parenthesis. After keywords like if, for, or while, a space is inserted instead, matching common style-guide conventions.
Why doesn't a function call with several arguments get split across multiple lines?
Only an opening curly brace forces a new line and indentation bump. Parentheses and square brackets track nesting depth for indentation purposes but don't force a line break on their own, so doSomething(a, b, c) stays on one line while actual code blocks still get properly indented.
What happens if the beautifier encounters code it can't parse correctly?
The whole beautification process is wrapped in error handling, so instead of crashing or silently producing corrupted output, it displays a clear "Could not beautify" message describing what went wrong.