A messy SQL query becoming neatly formatted across two monitors, with a large red cat resting nearby.

If you have ever pulled a query out of an application log or an ORM and found a single 400-character line crammed with SELECT, JOIN, and a dozen AND conditions, you already know the problem sql-formatter solves. It is a small, focused library that takes an unformatted SQL string and gives you back a consistently indented, readable version. Think of it as Prettier, but for SQL.

The important thing to understand up front is what it does and does not do. sql-formatter is a whitespace and layout formatter. It re-indents your query, normalizes keyword casing, and intelligently breaks long expressions across lines. It does not validate your SQL, rewrite it, or transpile between dialects. Your query keeps exactly the same meaning it had before, just with much better manners.

That narrow focus is a feature. The library has roughly ten years of history, around 2.8 million weekly downloads, and a lean dependency tree of just two packages. It powers editor extensions, database admin panels, query-log prettifiers, and the humble "Format SQL" button you have probably clicked in some web tool. If you need clean SQL text across a pile of dialects with essentially zero configuration, this is the pragmatic default choice.

Why It Earns Its Keep

A few capabilities make sql-formatter worth reaching for instead of hand-rolling something:

  • Broad dialect coverage. It understands 19+ SQL variants from a single package, including PostgreSQL, MySQL, MariaDB, SQLite, BigQuery, Snowflake, Redshift, Spark, T-SQL, PL/SQL, Trino, DuckDB, ClickHouse, and more.
  • Real parsing, not regex guesswork. Since version 4 it uses a proper tokenizer plus a nearley grammar, and it borrows Prettier's layout algorithm to decide where long lines should wrap.
  • Runs everywhere. It ships ESM, CommonJS, and TypeScript types, works in any bundler, has a prebuilt browser bundle, and includes a command-line tool.
  • Tiny footprint. Two dependencies total, so it will not bloat your bundle or your install.
  • Configurable house style. Indentation, keyword casing, data-type casing, operator spacing, and line-break behavior are all tunable to match your team's conventions.

Getting It Installed

The package is published as sql-formatter.

npm install sql-formatter
yarn add sql-formatter

If you want the command-line tool available everywhere, install it globally or reach for npx:

npm install -g sql-formatter

From Ransom Note to Readable

The whole library is anchored by a single format function. Hand it a string and it hands you a tidy one back.

import { format } from 'sql-formatter';

const messy = 'SELECT id, name, email FROM users WHERE active = 1 AND age > 18';

console.log(format(messy));
// SELECT
//   id,
//   name,
//   email
// FROM
//   users
// WHERE
//   active = 1
//   AND age > 18

No setup, no configuration object, no async ceremony. That is genuinely all most people ever need. The default dialect is plain sql, a lowest-common-denominator grammar that handles the vast majority of everyday queries.

Speaking the Right Dialect

The moment your SQL uses something vendor-specific, you will want to tell the formatter which dialect it is reading. You do that with the language option, and you can pass styling preferences alongside it in the same object.

import { format } from 'sql-formatter';

const result = format('select id, name from users where active = 1', {
  language: 'postgresql',
  keywordCase: 'upper',
  tabWidth: 2,
  linesBetweenQueries: 2,
});

console.log(result);
// SELECT
//   id,
//   name
// FROM
//   users
// WHERE
//   active = 1

Here language: 'postgresql' makes the parser aware of Postgres-specific syntax, keywordCase: 'upper' standardizes reserved words to uppercase, and tabWidth controls how many spaces make up one indentation level. If you ever see a confusing "Parse error", the usual culprit is a mismatched dialect, so reach for the right language value first.

Dialing In Your House Style

The options object is where sql-formatter becomes a team tool rather than a personal convenience. A handful of the most useful knobs:

import { format } from 'sql-formatter';

const styled = format('select count(*) as total from orders where status = \'paid\'', {
  language: 'mysql',
  keywordCase: 'upper',
  functionCase: 'lower',
  dataTypeCase: 'upper',
  logicalOperatorNewline: 'before',
  expressionWidth: 60,
  newlineBeforeSemicolon: true,
});

A few of these are worth calling out. keywordCase, functionCase, and dataTypeCase each take 'preserve', 'upper', or 'lower', letting you uppercase keywords while keeping functions lowercase, for instance. logicalOperatorNewline decides whether AND and OR sit at the start ('before') or end ('after') of a line. expressionWidth is the maximum line length before a parenthesized expression wraps, and newlineBeforeSemicolon pushes the trailing ; onto its own line.

There is also an identifierCase option, but treat it with caution. It is marked experimental because changing the casing of identifiers can actually alter meaning in case-sensitive contexts, such as quoted Postgres identifiers. For most projects, leaving identifiers alone is the safe call.

Inlining Real Values with Parameters

One genuinely handy trick is parameter substitution. When you log a parameterized query, you usually see placeholders like ? or $1 rather than the values that actually ran. The params option lets you splice those values back in, which is great for producing a human-readable record of the real query.

Positional placeholders take an array:

import { format } from 'sql-formatter';

format('SELECT * FROM tbl WHERE foo = ?', {
  params: ["'bar'"],
});
// ...WHERE foo = 'bar'

Named placeholders take an object, and you tell the formatter which prefix to recognize via paramTypes:

import { format } from 'sql-formatter';

format('SELECT * FROM users WHERE name = :name AND age < :age', {
  language: 'postgresql',
  paramTypes: { named: [':'] },
  params: { name: "'John'", age: '30' },
});

One critical caveat: the substituted value is inserted verbatim. The library does not escape or quote anything for you, which is why the examples above wrap strings in their own quotes. This is a formatting convenience for display and logging, not a SQL-injection-safe parameter binder. Never use it as a substitute for real prepared statements.

If your placeholders use a non-standard syntax, such as templating-style {var} markers, you can describe them with a custom regex:

import { format } from 'sql-formatter';

format('SELECT * FROM t WHERE lname = {lname} AND age > {age}', {
  paramTypes: {
    custom: [{ regex: String.raw`\{[a-zA-Z0-9_]+\}`, key: (t) => t.slice(1, -1) }],
  },
  params: { lname: "'Doe'", age: '25' },
});

Formatting from the Command Line

The package also ships a sql-formatter binary, which makes it easy to wire SQL formatting into scripts, pre-commit hooks, or your editor's save action. It reads from a file or from stdin.

# Format a file and print the result to stdout
sql-formatter query.sql

# Pick a dialect and rewrite the file in place
sql-formatter query.sql -l postgresql --fix

# Pipe SQL straight in
echo 'select * from tbl' | sql-formatter

For shared team settings, drop a .sql-formatter.json file in your project with your preferred options. It even comes with a JSON Schema, so editors like VS Code and Zed will autocomplete the available keys for you. Point the CLI at it with the -c flag, or let editor extensions pick it up automatically.

Leaving Some SQL Untouched

Occasionally you have a hand-tuned block you would rather the formatter not touch, such as a carefully aligned table of values. You can fence off any region with special comments:

/* sql-formatter-disable */
SELECT  a,  b,  c
FROM    legacy_view;
/* sql-formatter-enable */

Everything between those two markers is preserved exactly as written, while the rest of the file gets the usual treatment. This is also a useful escape hatch for templating languages like Jinja or Handlebars whose {{ }} syntax the formatter does not natively understand.

Building a "Format SQL" Button

Because format is a synchronous, dependency-light function, it pairs beautifully with an in-browser code editor. A "Format SQL" button in a React app is essentially one call wired to a state setter:

import { format } from 'sql-formatter';
import { useState } from 'react';

function SqlEditor() {
  const [sql, setSql] = useState('select * from users where id = 1');

  const prettify = () =>
    setSql((current) => format(current, { language: 'postgresql', keywordCase: 'upper' }));

  return (
    <div>
      <textarea value={sql} onChange={(e) => setSql(e.target.value)} />
      <button onClick={prettify}>Format SQL</button>
    </div>
  );
}

Swap the textarea for CodeMirror or Monaco and you have the core of a respectable SQL workbench. Because the formatter is pure and fast, you can even run it on every keystroke or on blur without worrying about performance.

A Note on Maintenance and Alternatives

It is worth knowing that sql-formatter is officially in maintenance mode. Bug fixes still land regularly, with version 15.8.0 shipping in May 2026, but large new features are unlikely. The maintainer is steering future energy toward prettier-plugin-sql-cst, a stricter, more opinionated successor built on a full concrete syntax tree. For the overwhelming majority of real-world formatting needs today, though, sql-formatter remains the dependable, do-everything choice.

If your needs differ, a couple of neighbors are worth knowing. prettier-plugin-sql is the right pick when you specifically want SQL handled inside your existing Prettier configuration, and it delegates to sql-formatter under the hood. node-sql-parser is a different category of tool entirely: it builds a real abstract syntax tree so you can programmatically inspect and transform queries, with reformatting as a side effect rather than the goal.

Wrapping Up

sql-formatter is the kind of library that quietly earns a permanent spot in your toolbox. It does one thing, does it across nearly twenty dialects, and asks almost nothing of you to get started. Whether you are cleaning up logged queries, enforcing a consistent house style in code review, or wiring a format button into a database UI, a single format call gets you most of the way there. Pull it in, pick your dialect, and never read a ransom-note query again.