Formatter

Bun's fast, built-in formatter for JavaScript, TypeScript, CSS, HTML, JSON, YAML, Markdown, and more

Bun ships with a fast, built-in code formatter. There's nothing to install and nothing to configure.

  • JavaScript, TypeScript, JSX, CSS, SCSS, Less, HTML, Vue, Angular, JSON, YAML, TOML, Markdown, MDX, GraphQL, and Handlebars
  • The same output as Prettier
  • Sorts imports, Tailwind CSS classes, and package.json
  • Works with your existing Prettier or oxfmt config

Run the formatter#

terminal
bun fmt
src/index.ts
src/server.ts
Formatted 2 files, 2 unchanged [3.47ms]

bun fmt rewrites the files in the current directory that need it and prints their names. It skips node_modules, anything in your .gitignore, minified files (*.min.js, *.min.css) and lock files such as package-lock.json.

To format specific files, pass paths or globs.

terminal
bun fmt src "test/**/*.ts"

Embedded code is formatted too: <script> and <style> in HTML and Vue, code blocks in Markdown, and css`...`, html`...`, and graphql`...` templates in JavaScript.

Check without writing#

--check lists the files that need formatting and writes nothing.

terminal
bun fmt --check
src/index.ts
src/server.ts
2 files need formatting. Run bun fmt to fix them.

The exit code is 1 if a file needs formatting, 0 if none do, and 2 if something went wrong, like a syntax error. That makes it the command for CI.

.github/workflows/format.yml
steps:
  - uses: actions/checkout@v4
  - uses: oven-sh/setup-bun@v2
  - run: bun fmt --check

Options#

Options live in bunfig.toml. To create the section, run bun fmt --init.

bunfig.toml
[fmt]
printWidth = 100
semi = false
singleQuote = true
OptionDefaultWhat it does
printWidth80Wrap lines at this column
tabWidth2Spaces per indentation level
useTabsfalseIndent with tabs
semitruePrint semicolons
singleQuotefalseUse single quotes
trailingComma"all""all", "es5", or "none"

All of Prettier's options work, under the same names. Each one is also a flag, like --print-width 100 or --no-semi.

Different options for some files#

bunfig.toml
[[fmt.overrides]]
files = ["*.test.ts", "scripts/**"]
options = { printWidth = 120 }

.editorconfig#

bun fmt reads indent_style, indent_size, tab_width, max_line_length, and end_of_line from .editorconfig. bunfig.toml overrides them.

Ignore files#

To skip more than .gitignore does, list the files in bunfig.toml. A .prettierignore file works too.

bunfig.toml
[fmt]
ignorePatterns = ["vendor/", "**/*.gen.ts"]

Ignore code#

bun fmt uses Prettier's comment, so existing ones keep working. It applies to the next statement, element, or rule.

// prettier-ignore
const matrix = [
  1, 0, 0,
  0, 1, 0,
  0, 0, 1,
];

In JSX it's {/* prettier-ignore */}, in CSS /* prettier-ignore */, in HTML and Markdown <!-- prettier-ignore -->, and in YAML and GraphQL # prettier-ignore.

Sort imports#

bunfig.toml
[fmt]
sortImports = true
Before
import { z } from "zod";
import { b, a } from "./util";
import { db } from "@/db";
import { readFile } from "node:fs/promises";
import { c } from "./util";
After
import { readFile } from "node:fs/promises";

import { z } from "zod";

import { db } from "@/db";

import { a, b, c } from "./util";
  • Imports are grouped: built-in modules, packages, aliases like @/, relative paths, styles.
  • Each group is sorted by path, and the names in { } are sorted too.
  • Two imports from the same module become one.
  • Side-effect imports like import "./setup" never move, and nothing moves past one.

To pick your own groups, use a table. See format.sortImports.

bunfig.toml
[fmt.sortImports]
groups = ["react", "builtin", "external", "internal", ["parent", "sibling", "index"]]
customGroups = [{ groupName = "react", elementNamePattern = ["react", "react-dom/**"] }]

Sort Tailwind CSS classes#

bunfig.toml
[fmt]
sortTailwindcss = true
<a className="p-4 flex m-2" /> 
<a className="m-2 flex p-4" /> 

Classes are sorted in Tailwind's recommended order, in class and className and in calls to clsx, cn, cva, twMerge, and a few more. The order comes from the tailwindcss package in your project, so version 3 or 4 has to be installed.

If your .prettierrc lists prettier-plugin-tailwindcss, sorting is already on, with that plugin's options and defaults.

To name your own functions or your stylesheet, use a table.

bunfig.toml
[fmt.sortTailwindcss]
stylesheet = "./src/app.css"
functions = ["clsx", "tw"]

Sort package.json#

bunfig.toml
[fmt]
sortPackageJson = true

Keys go in the conventional order: name, version, description, and so on.

Before
{
  "dependencies": { "zod": "^4.0.0" },
  "scripts": { "dev": "bun --hot src/index.ts" },
  "version": "1.0.0",
  "name": "app"
}
After
{
  "name": "app",
  "version": "1.0.0",
  "scripts": {
    "dev": "bun --hot src/index.ts"
  },
  "dependencies": {
    "zod": "^4.0.0"
  }
}

Format JSDoc comments#

bunfig.toml
[fmt]
jsdoc = true
Before
/**
 * adds two numbers
 * @returns {number} the sum
 * @param {number} b second number
 * @param {number} a   first number
 */
After
/**
 * Adds two numbers
 *
 * @param {number} a First number
 * @param {number} b Second number
 * @returns {number} The sum
 */

Standard input#

Pipe text through bun fmt. It prints the result and writes nothing. The path picks the language and the options. The file doesn't have to exist.

terminal
bun fmt --stdin-filepath src/index.ts < src/index.ts

No path to give? Name the parser instead. Your config still applies, except for what goes by a file name: overrides, .editorconfig, and ignore files.

terminal
bun run generate-types | bun fmt --parser typescript > types.ts

Git hooks#

--staged formats only the files you're about to commit and stages them again, so you don't need lint-staged.

.git/hooks/pre-commit
#!/bin/sh
bun fmt --staged

A file with both staged and unstaged changes isn't written, because staging it again would commit the rest. Bun names the file and exits with code 2.

--changed formats the files that differ from your default branch. Pass a ref to compare with something else.

terminal
bun fmt --changed
bun fmt --changed=origin/dev

Migrate from Prettier or oxfmt#

Already have a .prettierrc or an .oxfmtrc.json? bun fmt reads it and prints what that tool prints. Change the script and you're done.

package.json
{
  "scripts": {
    "format": "prettier --write .", 
    "format": "bun fmt" 
  }
}

If package.json has a fmt script, bun fmt runs that script. Inside the script, bun fmt is the formatter.

Svelte and Astro aren't built in. If your .prettierrc lists their plugin, Bun sends those files to your installed Prettier. To see which files those are, and which packages you can uninstall:

terminal
bun fmt --doctor

See the guides: Migrate from Prettier and Migrate from oxfmt.

CLI Usage#

bun fmt [flags] [files, directories, or globs]

Modes#

-c, --checkboolean

Write nothing. Exit with code 1 if a file needs formatting

-l, --list-differentboolean

Like --check, but print only the file names

--stdin-filepathstring

Format standard input as this file and print the result

--initboolean

Add a [fmt] section to bunfig.toml

--doctorboolean

Show what Bun does with your Prettier or oxfmt config, and which packages you can remove

Files#

--stagedboolean

Format only the files staged in Git, and stage them again

--changedstring

Format only the files that differ from a Git ref. Defaults to the default branch

--ignore-patternstring

Skip files that match this glob. Can be repeated

Options#

--print-widthnumberdefault:80

Wrap lines at this column

--tab-widthnumberdefault:2

Spaces per indentation level

--use-tabsboolean

Indent with tabs

--no-semiboolean

Only print semicolons where they are needed

--single-quoteboolean

Use single quotes

--trailing-commastringdefault:all

all, es5, or none

--sort-importsboolean

Sort and group imports

--sort-tailwindcssboolean

Sort Tailwind CSS classes

--sort-package-jsonboolean

Sort the keys of package.json

--jsdocboolean

Format JSDoc comments

--no-editorconfigboolean

Do not read .editorconfig

All of Prettier's options are flags too.

Output#

--log-levelstringdefault:log

silent, error, warn, log, or debug

--threadsnumber

Number of threads. Defaults to one per CPU core

bun fmt also accepts the flags of Prettier and oxfmt. Run bun fmt --help --all to see them.