Migrate from Prettier to bun fmt
bun fmt reads your .prettierrc and prints what Prettier 3 prints. In most projects you only change the command.
Start with --doctor. It shows what Bun will do with your config, and it doesn't format or write anything.
bun fmt --doctorUsing .prettierrc (Prettier config)
4 files are formatted natively
Built-in plugins: @ianvs/prettier-plugin-sort-imports
You can remove: @ianvs/prettier-plugin-sort-imports
Still needed: prettier (the "format" script runs it)
Not checked: other tools' configs, CI files, Git hooksChange the scripts.
{
"scripts": {
"format": "prettier --write .",
"format:check": "prettier --check .",
"format": "bun fmt",
"format:check": "bun fmt --check"
}
}bun fmt writes by default and formats the current directory, so --write . goes away. Other flags and arguments stay the same. Change CI and Git hooks too.
If your script is called fmt, don't skip this: bun fmt runs a fmt script when package.json has one, so you'd still get Prettier.
Watch out for two differences in hooks and shell scripts:
- No arguments means the current directory. Prettier prints an error there. So if the file list can be empty, as in
git diff --cached --name-only | xargs bun fmtorbun fmt $(git ls-files '*.foo'), Bun formats everything. Usebun fmt --stagedin a pre-commit hook, orxargs -r. bun fmt file.tswrites the file and prints its name.prettier file.tsprints the formatted code. To print it, pipe the code in:bun fmt --stdin-filepath file.ts < file.ts.
Piping into --parser works like in Prettier: generate | bun fmt --parser typescript > out.ts.
Check that nothing changes. With a Prettier config, --check prints what Prettier prints.
bun fmt --check$ bun fmt --check
Checking formatting...
All matched files use Prettier code style!Run --doctor again. No script runs Prettier anymore, so it can go too.
You can remove: @ianvs/prettier-plugin-sort-imports, prettier
Not checked: other tools' configs, CI files, Git hooksbun remove prettier @ianvs/prettier-plugin-sort-importsLeave .prettierrc as it is, including plugins.
Keep prettier if your editor's Prettier extension uses it.
Bun reads everything Prettier reads:
.prettierrcin JSON, YAML, TOML, JavaScript, or TypeScript,prettier.config.*, and"prettier"inpackage.json, withoverrides.editorconfig, and the.prettierignoreand.gitignorein the current directory// prettier-ignorecomments and the@formatand@noformatpragmas- Every flag of Prettier 3 except
--file-infoand--support-info
Bun doesn't load Prettier plugins. These are built in, with all their options, so leave them in plugins:
prettier-plugin-tailwindcss@trivago/prettier-plugin-sort-imports@ianvs/prettier-plugin-sort-importsprettier-plugin-organize-imports@prettier/plugin-oxcprettier-plugin-svelteprettier-plugin-packagejson
Svelte files are formatted the way prettier-plugin-svelte 4.1.1 formats them, whichever version you have installed. Other versions print a few components differently, so expect a small diff the first time if you were on one.
The plugin's options are those of 4.1.1: svelteSortOrder, svelteAllowShorthand, and svelteIndentScriptAndStyle. 4.1.1 has no svelteStrictMode, so Bun ignores it with a warning, like any unknown option.
With rangeStart or rangeEnd, a Svelte file is left unchanged, like an HTML or CSS file, because Prettier can't format part of one.
To leave Svelte files alone, set svelte = false in bunfig.toml.
package.json keys are sorted the way prettier-plugin-packagejson 3.0.2 sorts them with sort-package-json 3.7.1, and packageSortOrder is supported.
Bun puts the keys where the plugin puts them and leaves every value as you wrote it. The plugin parses the file and writes it back, which turns 1.0 into 1, 12345678901234567890 into 12345678901234567000, and "\u00e9" into "é". It also drops all but the last of two members with the same key, and moves keys like "10" to the front of their object. Bun does none of that. It also sorts a file with a trailing comma, where the plugin throws.
Astro and other languages that come from a plugin go to your installed Prettier. Keep prettier and every plugin in plugins installed. --doctor shows which files those are.
Using .prettierrc (Prettier config)
3 files are formatted natively
1 file goes to your installed Prettier: 1 *.astro
Built-in plugins: @ianvs/prettier-plugin-sort-imports
Plugins that use your installed Prettier: prettier-plugin-astro
Still needed: @ianvs/prettier-plugin-sort-imports, prettier, prettier-plugin-astro (some files go to them)With any other plugin, Bun skips the files that config covers and exits with code 2. A plugin could print them differently, and Bun won't guess. Pass --allow-unsupported to format them without the plugin.
error: 3 files were not formatted because the Prettier config uses plugins that bun fmt does not include: prettier-plugin-brace-style
Use --allow-unsupported to format them without the plugins.You don't have to keep .prettierrc. Move the options to bunfig.toml, under the same names, and delete the file. overrides become [[fmt.overrides]].
[fmt]
semi = false
singleQuote = trueWithout a Prettier config, bun fmt also formats TOML files, formats Svelte files without the plugin in a config, and --check prints a shorter report.
Differences from Prettier#
If bun fmt and Prettier 3 print different code, that's a bug. Open an issue. These differences are intentional:
- It writes by default.
--writeis accepted and does nothing. - No arguments means the current directory.
- Only changed files are listed, followed by a summary on stderr.
- An unknown flag is an error. Prettier warns and continues.
--cachedoes nothing. Every run formats every file.- Exit code
2means something went wrong, like a syntax error or a skipped file. Prettier uses1for some of these. - Bytes that aren't valid UTF-8 are kept. Prettier replaces them.
- Where Prettier's output changes what a file means, Bun's differs. That's rare. In a Handlebars template Prettier drops
<!DOCTYPE html>, and it joins the lines of a<script>, so a// commentswallows the code after it. In CSS it turnsFont\ AwesomeintoFont\Awesome. In Svelte the plugin drops the comment in{#if a /* b */}, the space in<hr />some <b>words</b>, and an empty{:catch}, and it empties the<script>after a comment that mentions<style>. Bun keeps all of them. - A function's only parameter doesn't move on the second run. When an object type in the return type doesn't fit on the line, Prettier gives the parameter its own line, then joins
m(a: string): {again the next time it runs. Bun prints the joined form the first time. - Symlinks that lead out of the repository aren't written through.
console.login your config file is hidden unless the config fails to load.
See also: