Skip to content
Ultracite
Esc
↑↓navigate↵open⌘Jpreview

The strict Biome config, in one line of extends

Biome ships 565 rules and turns on 215. Ultracite turns on 331 and adds the sorting, formatting and editor settings to match, all from one biome.jsonc.

Also onOxlintESLintChecked against Biome 2.5

{
  "$schema": "./node_modules/@biomejs/biome/configuration_schema.json",
  "extends": [
    "ultracite/biome/core",331 rules
    "ultracite/biome/react",12 rules
    "ultracite/biome/next"7 rules
  ]
}
350 rules, all errors
lineWidth
80
semicolons
"always"
trailingCommas
"es5"
formatWithErrors
true
organizeImports
"on"
useSortedClasses
"error"

Terminal

$ npx ultracite fix

src/component.tsx:42:27 lint/performance/noJsxPropsBind

× This function will be recreated on every render. Pass stable function references as props to avoid unnecessary rerenders.

> 42 │   onChange={() => onToggle(task.id)}
     │             ^^^^^^^^^^^^^^^^^^^^^^^

src/utils.ts:62:14 lint/performance/noAwaitInLoops

Checked 7 files in 7ms. Fixed 6 files.

Found 2 errors.

215 rules by default. 331 with Ultracite.

Ultracite keeps all but two of Biome’s recommended rules, then turns on most of the style, complexity and performance rules Biome leaves off. They catch what agents get wrong: barrel files, nested ternaries, awaits inside loops and forEach where for…of would do.

Rules in Biome 2.5
565
On by default, from Biome’s recommended set
215
On in Ultracite’s core preset, before any framework rules
331
On by defaultTurned on by UltraciteLeft off
  • SuspiciousCode that’s likely wrong or useless

    101 / 121

  • CorrectnessCode that’s guaranteed to be wrong or useless

    59 / 100

  • StyleOne consistent, idiomatic way to write it

    72 / 100

  • ComplexityCode that could be simpler

    45 / 51

  • A11yAccessibility problems in JSX and HTML

    37 / 39

  • PerformanceCode that could run faster

    11 / 16

  • SecurityPotential security flaws

    5 / 7

  • NurseryRules still in development

    1 / 131

safe fix, applied by fix
72
unsafe fix, with --unsafe
73
no fix, for the agent
186
type-aware, opt-in
6

Errors, never warnings

All 331 are set to error, including the recommended rules Ultracite keeps, because an agent takes a zero exit code as done and a warning doesn’t change it.

6 rules that read your project

The type-aware preset adds rules like noImportCycles, noUnresolvedImports and noDeprecatedImports. They need Biome to scan your whole project first, so they’re opt-in: init --type-aware adds the preset.

Biome 2.5.15 and Ultracite 8.0.0, counted with biome rage --linter. Groups are drawn to one scale. Biome also turns on a framework’s recommended rules when it finds that framework in your dependencies; Ultracite’s framework presets set those rules explicitly.

Lint, format and sort in one process

On Biome, ultracite check is one command that lints, checks formatting and checks sorting, spread across every core. The ESLint toolchain runs ESLint, Prettier and Stylelint one after another, in Node.

42 files

Biome

257 ms

ESLint + Prettier + Stylelint

3.91 s

420 files

Biome

379 ms

ESLint + Prettier + Stylelint

15.5 s
Faster than the ESLint toolchain on 420 files
41×
Added on Biome going from 42 to 420 files
+122ms
Added on ESLint for the same growth
+11.6s
Medians of five runs of ultracite check on an Apple M1 Pro, through each toolchain’s core preset: Biome 2.5.15 against ESLint 10.12.0, Prettier 3.9.10 and Stylelint 17.16.0, on the repo’s benchmark fixture copied to 42 and 420 files. Times include starting Node and the CLI. How Oxlint compares.

Formatting and sorting from the same binary

Biome’s formatter is built to match Prettier’s output, and its assist actions sort code instead of just reporting it. Ultracite turns on nine of them, so the import order, class order and key order an agent writes never come up in review.

As written

import { cn } from '@/lib/utils'
import { Slot } from '@radix-ui/react-slot'
import type { ReactNode } from 'react'

export function Button({ asChild, children }: { asChild?: boolean, children: ReactNode }) {
  const Comp = asChild ? Slot : 'button'
  return <Comp className={cn('text-sm px-4 bg-primary hover:bg-primary/90 rounded-md h-10 font-medium')}>{children}</Comp>
}

After ultracite fix

import { Slot } from "@radix-ui/react-slot";
import type { ReactNode } from "react";
import { cn } from "@/lib/utils";

export function Button({
  asChild,
  children,
}: {
  asChild?: boolean;
  children: ReactNode;
}) {
  const Comp = asChild ? Slot : "button";
  return (
    <Comp
      className={cn(
        "h-10 rounded-md bg-primary px-4 font-medium text-sm hover:bg-primary/90"
      )}
    >
      {children}
    </Comp>
  );
}
organizeImports
Imports by distance: packages first, then your @/ aliases, then relative paths, A–Z within each.
useSortedClasses
Tailwind classes sorted in class, className and cn, clsx, cva, tw, twMerge, twJoin and tv calls. Biome’s order puts variants like hover: last.
useSortedKeys
Object keys, JSX attributes, interface members, CSS properties and package.json fields, in order. check fails on any that aren’t.
formatWithErrors
On, so a file with a syntax error still gets formatted. Biome leaves it off by default.

Safe fixes, tuned for agents that edit in steps

Biome applies only its safe fixes unless you pass --unsafe. Ultracite makes three more safe so they run on save, and its agent hook holds one of them back.

Unsafe in Biome, safe in Ultracite

Applied by fix
  • noUnusedImportsDeletes imports nothing in the file uses
  • useBlockStatementsAdds braces to a one-line if, else, for or while body
  • useSortedClassesSorts Tailwind classes in class names and cn() calls

Skipped by the agent hook

Since 7.3.0

An agent adds an import in one edit and the code that uses it in the next. A hook that ran fix between them deleted the import as unused, and the next edit broke. On Biome, the hook init writes now runs:

$ npx ultracite fix --skip=correctness/noUnusedImports --hook

Where it came from. A Claude Code user reported the race in January 2026 (#483). Unused imports are still removed when you save or run ultracite fix yourself, and check still fails on them.

The same rules in your editor, your agent and CI

init points your editor at Biome and tells your agents how to run it, so a file passes or fails the same way wherever it’s checked.

  1. {
      "[typescriptreact]": {
        "editor.defaultFormatter": "biomejs.biome"
      },
      "editor.codeActionsOnSave": {
        "source.fixAll.biome": "explicit",
        "source.organizeImports.biome": "explicit"
      },
      "editor.formatOnSave": true
    }

    VS Code, Cursor, Devin Desktop

    Fix and format on save

    init makes Biome the formatter for 14 languages, from TypeScript and CSS to GraphQL and Vue, and applies its safe fixes and import sorting when you save.

  2. "TSX": {
      "code_actions_on_format": {
        "source.fixAll.biome": true,
        "source.organizeImports.biome": true
      },
      "formatter": {
        "language_server": { "name": "biome" }
      }
    }

    Zed

    Fix as you format

    Zed formats JavaScript, TypeScript and TSX through Biome’s language server, applying fixes and sorting imports as it goes.

  3. Terminal

    $ npx ultracite check

    src/api.ts:1:1 assist/source/organizeImports FIXABLE

    src/component.tsx:42:27 lint/performance/noJsxPropsBind

    src/utils.ts:5:56 assist/source/useSortedKeys FIXABLE

    …

    Found 10 errors.

    Coding agents

    Every problem names its fix

    Biome marks what it can fix FIXABLE, so an agent can tell what ultracite fix will settle from what’s left for it. init writes the rules into AGENTS.md, which all 40 agents read.

Watch the switch from ESLint

Two community walkthroughs of replacing ESLint and Prettier with Ultracite’s Biome preset in a real project.

  • The EASIEST Way To Switch From ESLint & Prettier to Biome — UltraciteBetter Stack

  • This New Tool Made Me Delete ESLint Without RegretOrcDev

Biome questions, answered

What does Ultracite add on top of Biome’s defaults?
Biome turns on 215 rules from its recommended set. Ultracite’s core preset turns on 331: it keeps every default except noAutofocus and noImportantStyles, sets each rule it touches to error, and turns on nine assist actions that sort imports, object keys, JSX attributes and more. The React preset adds 12 rules and Next.js 7. init also writes editor settings for VS Code and Zed, AGENTS.md for coding agents, and optional post-edit hooks.
How do I turn off a rule?
Set it in linter.rules in your biome.jsonc, under its group: "style": { "noNestedTernary": "off" }. Your own settings apply after the presets in extends, so they win. For one line, add // biome-ignore lint/style/noNestedTernary: <reason> above it; Biome requires the reason.
Do I still need Prettier?
No. Biome formats JavaScript, TypeScript, JSX, JSON, CSS and GraphQL itself, and Ultracite sets it to Prettier’s style: 80 columns, double quotes, semicolons and ES5 trailing commas. Biome doesn’t run Prettier plugins, so if you depend on one, pick Ultracite’s ESLint provider, which uses Prettier.
Should I use Biome or Oxlint?
Both are native and fast. In our benchmark, Biome checked 42 files in 257 ms to Oxlint’s 340 ms, because it lints and formats in one process; at 420 files Oxlint was slightly ahead, 344 ms to 379 ms. Pick Biome for one tool and one config file with sorting built in. Pick Oxlint for more rules (474 in its core preset to Biome’s 331), ESLint’s rule names and plugins, and type-aware rules on typescript-go.
Does it lint Vue, Svelte and Astro files?
Yes. The Vue, Svelte and Astro presets turn on Biome’s html.experimentalFullSupportEnabled, which Biome describes as full support for those files, and adjust rules for how each framework works: Svelte and Astro imports used only in markup aren’t reported as unused, for instance. Biome marks that support experimental; for mature template linting, the ESLint provider uses each framework’s own ESLint plugin.
How do I switch from ESLint and Prettier?
Run npx ultracite init --linter biome. It removes the ESLint, Prettier and Stylelint packages and config files it would have installed for those tools, writes biome.jsonc, and merges into your .vscode/settings.json rather than replacing it. To carry custom ESLint rules across, Biome’s own biome migrate eslint --write maps them to Biome’s names.
Does it support type-aware rules?
Biome reads types itself, without the TypeScript compiler. Install with npx ultracite init --linter biome --type-aware to add the type-aware preset, whose 6 rules need Biome to scan the whole project first: noPrivateImports, noUnresolvedImports, noImportCycles, noDeprecatedImports, useJsonImportAttributes and useArraySortCompare.
How do I exclude files?
Add negated globs to files.includes in your biome.jsonc, after "**": ["**", "!components/ui/**", "!public"]. Keep "**" first. Biome needs a positive glob to start from, so a list of only negated patterns matches no files at all. The core preset already leaves out build output, caches and generated files.

Ship less slop

One command sets up your linter, formatter, editor and agents. Works with Oxlint, Biome and ESLint.