Configuration
Understand Ultracite's default presets, how framework-specific layers work, and which overrides to keep when your project needs custom rules.
One of Ultracite’s best features is that it’s zero-config out of the box — you can just extend the base preset and get going. However, every project is different, and you might want to customize certain rules or adapt Ultracite for different frameworks.
Framework Presets
Ultracite provides framework-specific configurations that you can extend in addition to the base core configuration. This allows you to add framework-specific linting rules without bloating the base config or dealing with irrelevant rules.
The concept is the same across all supported linters — pull in the core preset, then layer on any framework presets you need — though the exact syntax differs per linter (Biome uses an extends array, while ESLint and Oxlint use imports; see the provider pages for each linter’s syntax). For example, if you’re using Biome with React and Next.js, you can pull in the following presets:
{
"extends": [
"ultracite/biome/core",
"ultracite/biome/react",
"ultracite/biome/next",
],
}
Default Configuration
When you include the core preset, you’re pulling in Ultracite’s base configuration. This preset includes framework-agnostic rules and settings. Some notable aspects:
- TypeScript strictness: Enables strict checks and lint rules requiring robust typing. Discourages use of
any, requires handling ofnull/undefined, and prefers explicit types where needed. - Best practices: Common rules like no unused vars, no explicit eval, no prototype pollution, etc.
- Accessibility: ARIA attributes, semantic HTML, keyboard navigation, and other a11y rules are enabled by default.
- Formatting conventions: 2-space indentation, 80-character line width, semicolons, trailing commas, and double quotes.
All these defaults aim to enforce consistent style and prevent common errors without manual configuration.
Type-Aware Rules
Ultracite ships an optional type-aware preset for Biome that enables project-graph and type-aware rules — noPrivateImports, noUndeclaredDependencies, noUnresolvedImports, noImportCycles, noDeprecatedImports, useImportExtensions, useJsonImportAttributes, and useArraySortCompare. Note that useImportExtensions requires relative imports to include their file extension. These rules require Biome’s scanner, so they’re slower and shipped as a separate layer:
{
"extends": ["ultracite/biome/core", "ultracite/biome/type-aware"],
}
You can also enable this at setup time with npx ultracite init --type-aware. For Oxlint, the same flag installs oxlint-tsgolint for type-aware linting instead.
Safe Fixes (Biome)
With Biome, ultracite fix runs biome check --write, which applies every rule’s safe fixes. Fixes that Biome marks as unsafe, because they can change behavior, only run with ultracite fix --unsafe. The Biome preset sets "fix": "safe" on a few rules whose fixes Biome marks unsafe by default, so plain ultracite fix applies them too. These fixes are non-breaking in practice:
| Rule | Description |
|---|---|
noUnusedImports |
Removes unused import statements |
useBlockStatements |
Wraps single-line control flow bodies in curly braces |
useSortedClasses |
Sorts Tailwind CSS classes into a consistent order |
To stop ultracite fix applying one of these, override the rule in your config without the fix property (its fix goes back to Biome’s default and only runs with --unsafe), or set "fix": "none" to turn the fix off entirely.
Overriding Rules
Each linter has its own way to disable or modify rules. For example, to disable the useButtonType rule for Biome, you can do the following:
{
"extends": ["ultracite/biome/core"],
"linter": {
"rules": {
"a11y": {
"useButtonType": "off",
},
},
},
}