Code Formatting

JavaScript Formatter

Format, compress and pretty-print JavaScript code.

输入 JavaScript
Formatted result
格式化结果将显示在这里

What is JavaScript Formatter?

JavaScript formatter beautifies JS code with proper indentation, making it easier to read, debug, and maintain.

Features

  • Format: Beautify with indentation
  • Minify: Compress for production
  • Syntax Highlighting: Color-coded output

Use Cases

  • Code review and debugging
  • Minified code analysis
  • Code style standardization

This tool reprints JavaScript through js-beautify: it takes a minified bundle, a pasted snippet, or a file with mixed indentation and emits one statement per line at an indent of 1, 2, or 4 spaces. It also shows a whitespace-collapsed compress view. Everything runs in the browser, with no upload and no syntax validation.

Whitespace reprinting, not parsing

js-beautify walks the source as a token stream and decides where to put line breaks and indentation; it never builds an AST. Three consequences are visible in the panels. Invalid code still formats, because function(){ const = ; comes back re-indented and the error row stays empty — the library only throws on rare tokenizer failures, so this page cannot validate syntax. Quote style, semicolons and parentheses are left exactly as typed; nothing is added or removed. And indentation is a fixed choice of 1, 2 or 4 spaces with indent_char pinned to a space, while wrap_line_length is set to 0, so an eight-call method chain of 94 characters stays on one line instead of wrapping.

Where reprinting moves meaning through ASI

Automatic semicolon insertion gives some line breaks meaning, and reprinting can relocate them. A return alone on one line followed by {a:1}} returns undefined; the formatter pulls the brace back up to return { and the function now returns the object. The mirror case: let a = b followed by a line beginning with (function(){})() means assign, then call the IIFE, but beautify joins them into let a = b(function() {})(), which calls b. Both results are valid JavaScript, so node --check passes and nothing looks broken. Lines that start with a parenthesis, bracket, template literal, plus, minus or slash after a finished statement are the dangerous shapes; terminate the previous statement with a semicolon before reformatting third-party code.

Why the compress panel is not a minifier

Compress never parses. It calls js-beautify with indent_size: 0 and preserve_newlines: false, deletes /* */ and // to end of line with two regexes, then collapses runs of whitespace into a single space. Strings and regex literals are not exempted, so const url = "https://example.com/api" loses everything from the double slash onward, and /\/\/x/g is cut at its first slash pair. The same collapse removes line breaks: two separate const declarations merge into const a = 1 const b = 2, a SyntaxError, and a multi-line template literal is flattened, changing its value. The percentage is a raw character delta against your input, not gzip size.

Worked example

Reprinting a one-line function at the default 2-space indent

Input
function greet(name){if(!name){return "hi";}const url="https://example.com/api";return url+name;}
Output
function greet(name) {
  if (!name) {
    return "hi";
  }
  const url = "https://example.com/api";
  return url + name;
}

Breaks are inserted after {, ; and }, and single spaces appear only where tokens would otherwise touch (const url, url + name). Each nesting level adds exactly two spaces because indent_size defaults to 2 and no line exceeds the zero wrap limit. The string is untouched here; the same input in the compress panel is truncated to "https:.

Limits and pitfalls

  • Indentation is limited to 1, 2 or 4 spaces: indent_char is fixed to a space, so tabs and other widths are not offered.
  • wrap_line_length is hard-wired to 0, so a long promise chain or ternary is never wrapped regardless of column width.
  • The compress panel removes // and /* */ with regexes, corrupting string literals, regex literals and template literals that contain them.
  • Compress also joins lines, so ASI-dependent code — a lone return, a statement starting with a parenthesis — can become a SyntaxError or silently change meaning.
  • No validation happens: malformed input is still beautified and the error row usually stays empty, and the textarea has no length cap, so the whole reprint recomputes on every keystroke.

Related tools

When it goes wrong

The compression percentage disappeared, and the compressed box is longer than what I pasted.

CauseCompress beautifies first with indent_size: 0, and that pass still pads operators: const a=1; becomes const a = 1;. Comments are the only thing removed, so already-minified input grows. The ratio is Math.round((1 - compressed / original) * 100), it goes negative, and the label is rendered only when it is above zero.

FixRead the two character counts in the panel header instead; the built-in sample goes from 48 to 59. For a build that ships, use npx esbuild app.js --minify, which this panel cannot stand in for.

The output came back with < div className = "a" > in it, and the page I saved throws a SyntaxError.

Causejs-beautify tokenises < and > as comparison operators, so a JSX element gets spaced apart the way a < b is. Nothing raises an error, which is why the error row stays silent while the markup is destroyed.

FixSend that file through npx prettier --parser babel (or --parser typescript for .tsx), and only come back to this page for files with no markup in them.

The character count in the panel is far below the file size the editor reports for the same code.

CauseinputCode.length counts UTF-16 code units: a CJK character is 1 unit here but 3 bytes in UTF-8, and an emoji is 2 units. The compression ratio is computed from that same count, so every figure on this page is characters, never bytes.

FixMeasure bytes with wc -c file.js or new Blob([code]).size, and judge transfer size from the gzip or brotli output, since collapsing whitespace buys much less on a real bundle than the panel implies.

With a large bundle pasted, every keystroke lags visibly, as if the whole file were being reprocessed.

CauseThe output is a Vue computed over the raw textarea with no length cap: each keystroke re-runs js-beautify across the entire input, and a watcher then hands the whole result to Prism for re-highlighting.

FixFormat in chunks rather than pasting the whole file, or run npx js-beautify -r on disk first and paste only the region you need to inspect.

AI Explain spins for a while, then shows a red error, while the formatted result on the right is perfectly fine.

CauseThat button is the one part of the page that is not local: it POSTs the complete textarea to /ai/generate with an auth header, so an expired session, a rate limit or an offline tab fails it. Formatting and compression never leave the browser.

FixKeep the right pane and ignore the banner. If the snippet holds keys, tokens or customer data, do not press the button at all, because that request uploads the code.

One paste in a tab, or one of the real tools sitting behind it

SituationThis page (js-beautify in the browser)Real alternative
A pasted bundle only has to become readable againPaste, pick 1, 2 or 4 spaces, copy. Nothing to install and no config file, and quotes or semicolons come back exactly as typed.In a repo that already has a .prettierrc, run npx prettier --write app.js: it also rewrites quotes and semicolons, so the layout matches what the team commits instead of drifting from it.
The source is tab-indented, or the house width is 3 or 8Three widths only, and indent_char is pinned to a space, so leading tabs are handed back as spaces.Prettier takes any --tab-width together with --use-tabs; the js-beautify CLI exposes --indent-with-tabs, a flag this page never passes.
The file is JSX or TSXThe ES tokenizer reads angle brackets as operators: <div className="a"> returns as < div className = "a" > and stops compiling.Prettier with --parser babel, or --parser typescript for TSX, is the one that understands elements and generics; keep this page for plain .js.
The result ships, so the character count has to actually dropCompression deletes comments and folds whitespace only. The built-in 48-character sample one-liner comes back at 59 characters, because the zero-indent pass re-inserts spaces around operators.npx esbuild app.js --minify shortens locals, drops dead branches and rewrites syntax; reach for it whenever a smaller number is the actual goal rather than a tidier view.
A whole directory needs the same treatmentOne textarea, one paste, no file input and no directory walk, so a 40-file folder costs 40 round trips through the clipboard.npx js-beautify -r src/**/*.js runs the identical engine across every expanded match and writes in place; only the default indent differs, 4 there against 2 here.
The reformat has to be shown harmless before a commitOne output pane and nothing to diff against: a statement that moved in or out of a block is invisible until it breaks.Do an in-place CLI pass and read git diff, or paste both versions into the text-diff tool when the original is not in a repo yet.

Frequently Asked Questions

Can formatting break my JavaScript?
No. Built on js-beautify, the tool only re-arranges whitespace and line breaks — no syntax rewriting. Note that minified code still has variable names like a and b after formatting: that renaming happened during minification, and only a source map can restore original names.
How does this relate to Prettier and ESLint?
This tool is for quickly reading deployed minified code or tidying pasted snippets. For team projects use Prettier (consistent formatting) plus ESLint (code quality), enforced in CI. They complement each other: quick look versus engineering guarantees.
2 spaces, 4 spaces, or tabs?
Follow the existing project; 2 spaces is the JS mainstream. What matters is repo-wide consistency enforced automatically via .editorconfig and Prettier, not which style is "correct".

Content reviewed: