| | |

Prettier 18 ๐ŸŽจ The JavaScript API โ€” prettier.format() and prettier.check()

โฑ๏ธ Estimated read: 12 min | |

The JavaScript API is the programmatic interface to Prettier. It does not spawn a CLI process, does not read from stdin, and does not format every file in a directory. It takes a string, a set of options, and returns a result. The two functions that matter for most integrations are prettier.format(), which returns the formatted string, and prettier.check(), which returns a boolean indicating whether the input is already formatted. The API changed in Prettier 3.0: format() is now asynchronous and returns a Promise. Code written for Prettier 2.x that calls format() synchronously receives a Promise object instead of a string, and the failure mode is a type error downstream, not an error from Prettier itself .

The check() function is the programmatic equivalent of the CLI’s --check flag. It returns Promise<boolean>, resolving to true if the source matches Prettier’s output and false if it does not. It does not return a diff, does not write the formatted output anywhere, and does not tell you what is wrongโ€”only whether something is wrong. This makes it the correct tool for CI gates and pre-commit checks, where the goal is to fail the build rather than to format the code .

Key point: Both format() and check() are asynchronous in Prettier 3. The format() function returns the formatted string; the check() function returns a boolean. Neither resolves configuration automaticallyโ€”resolveConfig() must be called separately if the options should come from a .prettierrc file.


Why the API is asynchronous

The parser loading problem. Prettier supports many languages, and each language has its own parser. Loading every parser at startup would make the module initialization slow and the memory footprint large. The asynchronous API allows Prettier to load the parser for the requested file type on demand, which keeps the initial import light and the first format call responsive. The change in 3.0 made this lazy loading the default behavior rather than an optimization .

The plugin architecture. Prettier 3.0 replaced the custom parser API with a plugin system. Plugins are loaded asynchronously, and the formatting pipeline must wait for the plugin’s parser to be available before it can parse the source. A synchronous API cannot wait for a Promise, so the entire pipeline became asynchronous. The migration note from the Prettier repository states that the --plugin flag and the plugins option should be used instead of the removed custom parser API .

The configuration resolution problem. The format() function does not read .prettierrc files by default. It uses the options passed to it and nothing else. The filepath option is used to infer the parser from the file extension, but it does not trigger a configuration file search. To format a file with the project’s configuration, the caller must first call prettier.resolveConfig(filePath) and pass the resolved options to format() . This separation is deliberate: the API is a formatting primitive, and configuration resolution is a separate step that the caller controls.

The trade-off. The asynchronous API is correct for the plugin architecture, but it breaks every synchronous integration written for Prettier 2.x. A build script that called prettier.format(source, options) and used the return value directly must be rewritten to await the result. A common migration error is a TypeError: Expected input to be a string, got object in a downstream function, because the “object” is the unresolved Promise .


a. Using prettier.format() with resolved configuration

The correct pattern for formatting a file with the project’s configuration is to resolve the config first, then call format() with the resolved options and the filepath option to infer the parser.

// format-file.mjs โ€” formatting a file with resolved config
import { readFile } from "node:fs/promises";
import * as prettier from "prettier";

async function formatFile(filePath) {
  // Resolve the configuration for the file.
  // This searches for .prettierrc from the file's directory upward.
  // It returns null if no config is found.
  const options = await prettier.resolveConfig(filePath);

  // Read the source file as a string.
  const source = await readFile(filePath, "utf8");

  // format() is asynchronous in Prettier 3.
  // The filepath option is used to infer the parser from the extension.
  // The spread merges the resolved options with the filepath.
  const formatted = await prettier.format(source, {
    ...options,
    filepath: filePath,
  });

  return formatted;
}

const result = await formatFile("src/app.js");
console.log(result);

The filepath option serves two purposes: it infers the parser from the extension, and it provides the path that the parser uses for error messages. Without it, the parser option must be passed explicitly. The resolveConfig() function returns null if no configuration file is found, and spreading null into an object is safe because it contributes no properties .

// format-string.mjs โ€” formatting an in-memory string
import * as prettier from "prettier";

// When formatting a string that is not associated with a file,
// the parser must be passed explicitly.
// There is no file extension to infer from.
const formatted = await prettier.format("const x=1", {
  parser: "babel",
  semi: false,
});

// The result is "const x=1\n" โ€” no semicolon, with a trailing newline.
console.log(formatted);

The parser option is required for in-memory strings. The valid parser names include "babel", "babel-ts", "typescript", "json", "css", "markdown", and others . The filepath option can be used instead of parser if the string has a logical file path, even though the file does not exist on disk.


b. Using prettier.check() for CI gates

The check() function is the programmatic equivalent of eslint --check for formatting. It returns a boolean, and it does not require the caller to compare strings.

// check-file.mjs โ€” checking formatting in CI
import { readFile } from "node:fs/promises";
import * as prettier from "prettier";

async function isFormatted(filePath) {
  const options = await prettier.resolveConfig(filePath);
  const source = await readFile(filePath, "utf8");

  // check() returns Promise<boolean>.
  // true: the source matches Prettier's output.
  // false: the source does not match.
  const formatted = await prettier.check(source, {
    ...options,
    filepath: filePath,
  });

  return formatted;
}

const files = ["src/app.js", "src/utils.js", "src/index.css"];

for (const file of files) {
  const ok = await isFormatted(file);
  if (!ok) {
    console.error(`${file} is not formatted`);
    process.exitCode = 1;
  }
}

The check() function does not return the diff or the formatted output. It only returns the boolean. If the caller needs to know what changed, format() must be called and the result compared to the original. The two functions are complementary: check() for the gate, format() for the fix.

The check() function is listed in the official API documentation alongside format(), and the documentation describes it as “similar to the --check or --list-different parameter in the CLI” and “useful for running Prettier in CI scenarios” . The CLI’s --check flag is the equivalent command-line interface; the API function is the programmatic version of the same behavior.


Complete Example Session

// ============================================
// PART 1: THE IMPORTS AND ASYNC PATTERN
// ============================================

// format.mjs
// Prettier 3 exports format and check as asynchronous functions.
// The import is a namespace import because there is no default export
// for the API functions in the ESM build.
import * as prettier from "prettier";
import { readFile } from "node:fs/promises";

// ============================================
// PART 2: RESOLVING CONFIGURATION
// ============================================

// resolveConfig searches for .prettierrc from the file's directory upward.
// It returns the options object or null if no config is found.
const filePath = "src/app.js";
const options = await prettier.resolveConfig(filePath);

// If no config is found, options is null.
// The spread of null is safe and contributes nothing.
console.log("Resolved options:", options);

// ============================================
// PART 3: READING THE SOURCE
// ============================================

const source = await readFile(filePath, "utf8");

// ============================================
// PART 4: FORMATTING WITH format()
// ============================================

// format() returns a Promise<string> in Prettier 3.
// The filepath option infers the parser from the .js extension.
const formatted = await prettier.format(source, {
  ...options,
  filepath: filePath,
});

console.log("Formatted output:");
console.log(formatted);

// ============================================
// PART 5: CHECKING WITH check()
// ============================================

// check() returns a Promise<boolean>.
// It does not return the diff or the formatted output.
const isFormatted = await prettier.check(source, {
  ...options,
  filepath: filePath,
});

if (!isFormatted) {
  console.error(`${filePath} is not formatted`);
  process.exitCode = 1;
}

// ============================================
// PART 6: THE SYNCHRONOUS MISTAKE (PRETTIER 2.x STYLE)
// ============================================

// โŒ This is the Prettier 2.x pattern.
// In Prettier 3, format() returns a Promise, not a string.
// const result = prettier.format(source, { parser: "babel" });
// console.log(result); // Promise { <pending> }

// The downstream function that expects a string receives a Promise,
// and the error is often "Expected input to be a string, got object".

// ============================================
// PART 7: THE EXPLICIT PARSER FOR STRINGS
// ============================================

// When the source is a string without a file path,
// the parser must be passed explicitly.
const inMemory = await prettier.format("const x=1", {
  parser: "babel",
  semi: false,
});

console.log(inMemory); // "const x=1\n"

// ============================================
// PART 8: THE CACHE CLEARING
// ============================================

// resolveConfig caches the file system structure for performance.
// In editor integrations that watch the file system,
// the cache may need to be cleared.
// prettier.clearConfigCache();

// ============================================
// PART 9: THE CI GATE PATTERN
// ============================================

// A CI script that checks all files and exits with code 1
// if any file is not formatted.
async function checkAll(files) {
  let hasErrors = false;

  for (const file of files) {
    const opts = await prettier.resolveConfig(file);
    const src = await readFile(file, "utf8");
    const ok = await prettier.check(src, { ...opts, filepath: file });

    if (!ok) {
      console.error(`${file} is not formatted`);
      hasErrors = true;
    }
  }

  if (hasErrors) {
    process.exitCode = 1;
  }
}

// ============================================
// PART 10: THE VERIFICATION CHECKLIST
// ============================================

// [ ] The import is a namespace import: import * as prettier from "prettier";
// [ ] format() is awaited: const result = await prettier.format(...);
// [ ] check() is awaited: const ok = await prettier.check(...);
// [ ] resolveConfig is called before format/check if options come from .prettierrc.
// [ ] The filepath option is passed to infer the parser.
// [ ] The parser option is passed explicitly for in-memory strings.
// [ ] The result of format() is used, not the Promise object.

The ten parts covered the asynchronous pattern, the configuration resolution, the format() call, the check() call, the synchronous mistake, the explicit parser, the cache clearing, the CI gate, and the verification checklist. The through-line is that the API is a primitive, and configuration resolution is a separate step.


Quick Reference

API functions

FunctionReturn typePurpose
prettier.format(source, options)Promise<string>Returns the formatted string
prettier.check(source, options)Promise<boolean>Returns whether the source is formatted
prettier.resolveConfig(filePath)Promise<object | null>Resolves the configuration for a file

Options for format/check

OptionPurpose
filepathInfers the parser from the extension
parserExplicit parser for in-memory strings
semi, singleQuote, etc.Formatting options (same as .prettierrc)

Parser inference

InputHow to infer
File on diskPass filepath
In-memory string with a logical pathPass filepath
In-memory string without a pathPass parser explicitly

Best Practices

โœ… Do This:

// Await the result of format()
const formatted = await prettier.format(source, options);         // โœ…

// Await the result of check()
const ok = await prettier.check(source, options);                 // โœ…

// Resolve config before formatting
const options = await prettier.resolveConfig(filePath);           // โœ…

// Pass filepath to infer the parser
await prettier.format(source, { ...options, filepath: filePath }); // โœ…

โŒ Don’t Do This:

// Use the Prettier 2.x synchronous pattern
const result = prettier.format(source, options); // Promise, not string // โŒ

// Forget the parser for in-memory strings
await prettier.format("const x=1", {}); // No parser, error              // โŒ

// Expect check() to return a diff
const diff = await prettier.check(source); // boolean only               // โŒ

Common Pitfalls

PitfallWhy It HappensFix
Expected input to be a string, got objectformat() not awaitedAdd await
parser is requiredIn-memory string without filepathPass parser or filepath
Configuration is ignoredresolveConfig() not calledResolve config and spread into options
check() returns false but no diffcheck() only returns a booleanUse format() and compare strings
Editor integration sees stale configConfig cache not clearedCall prettier.clearConfigCache()

Real-World Examples

1. Basic format

const formatted = await prettier.format(source, { parser: "babel" });

2. Format with resolved config

const opts = await prettier.resolveConfig(filePath);
await prettier.format(source, { ...opts, filepath: filePath });

3. Check formatting

const ok = await prettier.check(source, { ...opts, filepath: filePath });

4. CI gate

if (!(await prettier.check(src, opts))) process.exitCode = 1;

5. In-memory string

await prettier.format("const x=1", { parser: "babel" });

6. CSS parser

await prettier.format(css, { parser: "css" });

7. Markdown parser

await prettier.format(md, { parser: "markdown" });

8. Clear config cache

prettier.clearConfigCache();

9. Format with options override

await prettier.format(src, { ...opts, semi: false, filepath });

10. TypeScript parser

await prettier.format(ts, { parser: "typescript" });

Visual

The API Call Flow

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  FORMATTING A FILE WITH THE API                               โ”‚
โ”‚                                                              โ”‚
โ”‚  resolveConfig(filePath)                                     โ”‚
โ”‚         โ”‚                                                    โ”‚
โ”‚         โ–ผ                                                    โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”                                    โ”‚
โ”‚  โ”‚ Options object      โ”‚  From .prettierrc                  โ”‚
โ”‚  โ”‚ (or null)           โ”‚                                    โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜                                    โ”‚
โ”‚             โ”‚                                                โ”‚
โ”‚             โ–ผ                                                โ”‚
โ”‚  format(source, { ...options, filepath })                    โ”‚
โ”‚         โ”‚                                                    โ”‚
โ”‚         โ–ผ                                                    โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”                                    โ”‚
โ”‚  โ”‚ Promise<string>     โ”‚  โ† must be awaited                 โ”‚
โ”‚  โ”‚                     โ”‚                                    โ”‚
โ”‚  โ”‚ The formatted code  โ”‚                                    โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜                                    โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

format() vs check()

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  TWO FUNCTIONS, TWO RETURN TYPES                              โ”‚
โ”‚                                                              โ”‚
โ”‚  prettier.format()              prettier.check()            โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”       โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”     โ”‚
โ”‚  โ”‚ Promise<string>     โ”‚       โ”‚ Promise<boolean>    โ”‚     โ”‚
โ”‚  โ”‚                     โ”‚       โ”‚                     โ”‚     โ”‚
โ”‚  โ”‚ The formatted code  โ”‚       โ”‚ true: already       โ”‚     โ”‚
โ”‚  โ”‚                     โ”‚       โ”‚   formatted         โ”‚     โ”‚
โ”‚  โ”‚ Use to apply fixes  โ”‚       โ”‚ false: needs        โ”‚     โ”‚
โ”‚  โ”‚                     โ”‚       โ”‚   formatting        โ”‚     โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜       โ”‚                     โ”‚     โ”‚
โ”‚                                 โ”‚ Use for CI gates    โ”‚     โ”‚
โ”‚                                 โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜     โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

The Async Migration

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  PRETTIER 2.x vs 3.x                                          โ”‚
โ”‚                                                              โ”‚
โ”‚  Prettier 2.x                  Prettier 3.x                  โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”       โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”     โ”‚
โ”‚  โ”‚ const result =      โ”‚       โ”‚ const result =      โ”‚     โ”‚
โ”‚  โ”‚   prettier.format() โ”‚       โ”‚   await prettier    โ”‚     โ”‚
โ”‚  โ”‚                     โ”‚       โ”‚     .format()       โ”‚     โ”‚
โ”‚  โ”‚ result is a string  โ”‚       โ”‚                     โ”‚     โ”‚
โ”‚  โ”‚                     โ”‚       โ”‚ result is a string  โ”‚     โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜       โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜     โ”‚
โ”‚                                                              โ”‚
โ”‚  The missing await is the migration bug.                    โ”‚
โ”‚  The error is often downstream, not in the format call.     โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Summary

ItemValue
format() returnPromise<string>
check() returnPromise<boolean>
resolveConfig() returnPromise<object | null>
Parser inferencefilepath option
In-memory parserparser option required
Async changePrettier 3.0
Config cacheclearConfigCache()

Key takeaways:

  • The API is asynchronous in Prettier 3. format() returns a Promise, not a string. Code that uses the return value directly receives a Promise object, and the failure surfaces downstream as a type error. The fix is to await the result .
  • check() returns a boolean, not a diff. It is the programmatic equivalent of the CLI’s --check flag. It tells you whether the source is formatted, not what is wrong. Use it for CI gates and pre-commit hooks .
  • Configuration is not resolved automatically. format() and check() use the options passed to them. To use the project’s .prettierrc, call resolveConfig(filePath) first and spread the result into the options .
  • The parser is inferred from filepath. When formatting a file on disk, pass the path as filepath. When formatting an in-memory string without a path, pass the parser option explicitly. The valid parser names include "babel", "typescript", "json", "css", and "markdown" .
  • The API is a primitive, not a CLI. It does not format directories, does not read ignored files, and does not provide a diff. It formats one string at a time with the options you give it. The CLI is built on top of this primitive; the primitive is what integrations use.

Remember: The JavaScript API is the layer beneath the CLI. It takes a string and options, and it returns a result. The two functions that matter are format() for the formatted output and check() for the boolean gate. Both are asynchronous, and neither resolves configuration automatically. The migration from Prettier 2.x is a migration from synchronous to asynchronous, and the missing await is the failure mode. The API is not a replacement for the CLI; it is the primitive that the CLI is built on.

Was this helpful?


Stop using slow, ad-bloated tool sites! ๐Ÿคฎ

๐Ÿ”Ž Search “KandZ Tools” on Google to use many professional utilities for free.

KandZ.me is the ultimate minimalist hub for:
โœ… Finance (Mortgage, Interest, Inflation)
โœ… Tech (Base64, JSON, Dev Suite, IP)
โœ… Health (BMI, BMR, TDEE)
โœ… Productivity (Timer, Workspace, QR)

โšก๏ธ Fast & Private
๐Ÿ”’ No data leaves your device
๐Ÿ’Ž 100% Free

๐Ÿ”— Use it now: https://tools.kandz.me
๐Ÿ”– Bookmark itโ€”youโ€™ll need it later!