# 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.

```sh terminal icon="terminal"
bun fmt --doctor
```

```txt
Using .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 hooks
```

---

Change the scripts.

```json package.json icon="file-json"
{
  "scripts": {
    "format": "prettier --write .", // [!code --]
    "format:check": "prettier --check .", // [!code --]
    "format": "bun fmt", // [!code ++]
    "format:check": "bun fmt --check" // [!code ++]
  }
}
```

`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 fmt` or `bun fmt $(git ls-files '*.foo')`, Bun formats everything. Use `bun fmt --staged` in a pre-commit hook, or `xargs -r`.
- **`bun fmt file.ts` writes the file and prints its name.** `prettier file.ts` prints 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.

```sh terminal icon="terminal"
bun fmt --check
```

```txt
$ 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.

```txt
You can remove: @ianvs/prettier-plugin-sort-imports, prettier
Not checked:    other tools' configs, CI files, Git hooks
```

```sh terminal icon="terminal"
bun remove prettier @ianvs/prettier-plugin-sort-imports
```

Leave `.prettierrc` as it is, including `plugins`.

Keep `prettier` if your editor's Prettier extension uses it.

---

Bun reads everything Prettier reads:

- `.prettierrc` in JSON, YAML, TOML, JavaScript, or TypeScript, `prettier.config.*`, and `"prettier"` in `package.json`, with `overrides`
- `.editorconfig`, and the `.prettierignore` and `.gitignore` in the current directory
- `// prettier-ignore` comments and the `@format` and `@noformat` pragmas
- Every flag of Prettier 3 except `--file-info` and `--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-imports`
- `prettier-plugin-organize-imports`
- `@prettier/plugin-oxc`
- `prettier-plugin-svelte`
- `prettier-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`](/runtime/bunfig#format-svelte).

`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.

```txt
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.

```txt
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`](/format#options), under the same names, and delete the file. `overrides` become [`[[fmt.overrides]]`](/runtime/bunfig#format-overrides).

```toml bunfig.toml icon="settings"
[fmt]
semi = false
singleQuote = true
```

Without 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](https://github.com/oven-sh/bun/issues). These differences are intentional:

- **It writes by default.** `--write` is 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.
- **`--cache` does nothing.** Every run formats every file.
- **Exit code `2` means something went wrong**, like a syntax error or a skipped file. Prettier uses `1` for 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 `// comment` swallows the code after it. In CSS it turns `Font\ Awesome` into `Font\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.log` in your config file is hidden** unless the config fails to load.

---

See also:

- [`bun fmt`](/format)
- [Migrate from ESLint to bun lint](/guides/lint/migrate-from-eslint)
