An ESLint plugin for formatting code with oxfmt - A blazing fast formatter powered by Rust.
- ⚡️ Blazing Fast - Powered by oxfmt, written in Rust
- 🔧 Auto-fix - Automatically format code on save or via ESLint's fix command
- 🎯 ESLint Integration - Seamlessly integrates with ESLint v9+ flat config
- 📦 Zero Config - Works out of the box with sensible defaults
- 🧩 Config File Discovery - Auto-discovers
.oxfmtrc.json,.oxfmtrc.jsonc, andoxfmt.config.ts - 📝 EditorConfig Integration - Respects a subset of
.editorconfigoptions via oxfmt's strategy - 🎨 Highly Configurable - Supports all oxfmt formatting options
- 🌐 Multi-language Support - JavaScript, TypeScript, JSX, TSX and more
- ESLint:
>= 9.0.0(Only supports ESLint flat config) - Node.js:
^20.19.0 || >=22.12.0
npm install -D oxfmt eslint-plugin-oxfmtyarn add -D oxfmt eslint-plugin-oxfmtpnpm add -D oxfmt eslint-plugin-oxfmtAdd the plugin to your ESLint flat config file:
// eslint.config.mjs
import pluginOxfmt from 'eslint-plugin-oxfmt'
export default [
{
...pluginOxfmt.configs.recommended,
files: ['**/*.{js,ts,mjs,cjs,jsx,tsx}'],
},
]recommended includes eslint-parser-plain as languageOptions.parser for convenience.
If you need parser-agnostic composition (for example to keep jsonc-eslint-parser from another config), use the dedicated preset:
// eslint.config.mjs
import pluginOxfmt from 'eslint-plugin-oxfmt'
export default [
{
...pluginOxfmt.configs.recommendedWithoutParser,
files: ['**/*.{js,ts,mjs,cjs,jsx,tsx,json,jsonc}'],
},
]For CLI-like ignore/config behavior, use cliParity:
// eslint.config.mjs
import pluginOxfmt from 'eslint-plugin-oxfmt'
export default [
{
...pluginOxfmt.configs.cliParity,
files: ['**/*.{js,ts,mjs,cjs,jsx,tsx,json,jsonc,yaml,yml}'],
},
]You can customize the formatting options by configuring the rule:
// eslint.config.mjs
import pluginOxfmt from 'eslint-plugin-oxfmt'
export default [
{
...pluginOxfmt.configs.recommended,
files: ['**/*.{js,ts,mjs,cjs,jsx,tsx}'],
rules: {
'oxfmt/oxfmt': [
'error',
{
// Plugin options
useConfig: false,
configPath: 'configs/.oxfmtrc.json',
// Formatting options
semi: false,
singleQuote: true,
tabWidth: 2,
useTabs: false,
trailingComma: 'all',
printWidth: 100,
arrowParens: 'avoid',
// File handling
ignorePatterns: ['**/dist/**', '**/.next/**'],
// JSX specific options
jsxSingleQuote: false,
bracketSameLine: false,
singleAttributePerLine: false,
// Object formatting
bracketSpacing: true,
quoteProps: 'as-needed',
objectWrap: 'preserve',
// Line endings
endOfLine: 'lf',
insertFinalNewline: true,
// Prose / HTML
embeddedLanguageFormatting: 'auto',
htmlWhitespaceSensitivity: 'css',
proseWrap: 'preserve',
// JSDoc
jsdoc: {
commentLineStrategy: 'singleLine',
lineWrappingStyle: 'greedy',
separateTagGroups: false,
},
// Vue
vueIndentScriptAndStyle: false,
// Advanced
sortImports: {
order: 'asc',
newlinesBetween: true,
},
sortPackageJson: { sortScripts: true },
sortTailwindcss: {
attributes: ['class', 'className', ':class'],
functions: ['clsx', 'cn'],
preserveDuplicates: false,
preserveWhitespace: false,
},
},
],
},
},
]All options are optional and default to sensible values.
| Option | Type | Default | Description |
|---|---|---|---|
useConfig |
boolean |
true |
Load .oxfmtrc.json, .oxfmtrc.jsonc, or oxfmt.config.* via load-oxfmt-config. Set to false to rely only on inline options. |
configPath |
string |
— | Custom path to an oxfmt config file. Relative paths resolve from ESLint cwd; absolute paths are used as-is. Explicit configPath accepts .json, .jsonc, .ts, .mts, .cts, .js, .mjs, .cjs. |
editorconfig |
boolean | { onlyCwd?: boolean; cwd?: string } |
true |
Control .editorconfig loading. Use false to disable, or object form for advanced resolution strategy. |
ignorePath |
string | string[] |
— | Ignore file path(s) for CLI-style ignore resolution (same role as CLI --ignore-path). |
withNodeModules |
boolean |
false |
Include files under node_modules during ignore checks. |
disableNestedConfig |
boolean |
false |
Disable nested config discovery and resolve config from cwd / configPath only. |
respectOxfmtDefaultIgnores |
boolean |
true |
Respect oxfmt default ignores (.gitignore, .prettierignore, default ignored directories, default ignored lockfiles). |
Note:
cwdis taken from ESLint automatically; you usually do not need to set it manually..editorconfigmerge behavior follows oxfmt's documented strategy: https://oxc.rs/docs/guide/usage/formatter/config#editorconfig
When useConfig is true, the plugin loads config using load-oxfmt-config.
- Config discovery order (from
cwd, walking upward):.oxfmtrc.json→.oxfmtrc.jsonc→oxfmt.config.ts .editorconfigsupport: nearest.editorconfig(including section overrides) is merged into the final options- Set
editorconfig: falseto disable.editorconfigmerging - Set
editorconfig: { onlyCwd: true }to read only the currentcwd's.editorconfig(no upward traversal) - Set
editorconfig: { cwd: '/path/to/base' }to customize editorconfig resolution base directory configPathoverrides discovery and directly targets the specified config fileconfigPathsupports explicit file paths with extensions:.json,.jsonc,.ts,.mts,.cts,.js,.mjs,.cjs- ESLint rule options generally take highest priority because inline rule options are merged after loaded config.
- Rule-level
ignorePatternsare resolved relative to ESLintcwd; config-levelignorePatternsare resolved relative to the resolved config file directory. - When
useConfigistrue, configoverridesare applied first and rule-leveloverridesare appended after them (later entries win on conflicts). - When
useConfigisfalse, config discovery and configignorePatternsare skipped, while global ignores still apply whenrespectOxfmtDefaultIgnoresis enabled.
For detailed behavior, see:
| Option | Type | Default | Description |
|---|---|---|---|
semi |
boolean |
true |
Add semicolons at the end of statements |
singleQuote |
boolean |
false |
Use single quotes instead of double quotes |
tabWidth |
number |
2 |
Number of spaces per indentation level |
useTabs |
boolean |
false |
Use tabs for indentation |
printWidth |
number |
100 |
Maximum line length for wrapping |
ignorePatterns |
string[] |
[] |
Glob patterns to skip formatting. Rule option patterns are resolved from ESLint cwd; config file patterns are resolved from the config file directory. |
| Option | Type | Default | Description |
|---|---|---|---|
trailingComma |
'all' | 'es5' | 'none' |
'all' |
Where to add trailing commas |
| Option | Type | Default | Description |
|---|---|---|---|
arrowParens |
'always' | 'avoid' |
'always' |
Include parentheses around sole arrow function parameter |
| Option | Type | Default | Description |
|---|---|---|---|
jsxSingleQuote |
boolean |
false |
Use single quotes in JSX attributes |
bracketSameLine |
boolean |
false |
Put > on the same line in JSX |
singleAttributePerLine |
boolean |
false |
Force single attribute per line in JSX |
| Option | Type | Default | Description |
|---|---|---|---|
vueIndentScriptAndStyle |
boolean |
false |
Indent code inside <script> / <style> in Vue |
| Option | Type | Default | Description |
|---|---|---|---|
bracketSpacing |
boolean |
true |
Print spaces between brackets in object literals |
quoteProps |
'as-needed' | 'consistent' | 'preserve' |
'as-needed' |
When to quote object property names |
objectWrap |
'preserve' | 'collapse' |
'preserve' |
How to wrap object literals |
| Option | Type | Default | Description |
|---|---|---|---|
endOfLine |
'lf' | 'crlf' | 'cr' |
'lf' |
Line ending character(s) |
insertFinalNewline |
boolean |
true |
Insert a newline at the end of files |
| Option | Type | Default | Description |
|---|---|---|---|
embeddedLanguageFormatting |
'auto' | 'off' |
'auto' |
Format embedded code blocks (e.g., JS-in-Vue, CSS-in-JS) |
htmlWhitespaceSensitivity |
'css' | 'ignore' | 'strict' |
'css' |
Global whitespace sensitivity for HTML-like languages |
proseWrap |
'always' | 'never' | 'preserve' |
'preserve' |
Control prose wrapping in Markdown/MDX |
Use jsdoc to enable and configure JSDoc comment formatting. Pass true to enable defaults, or pass an object for custom behavior (for example jsdoc: {}).
| Option | Type | Default | Description |
|---|---|---|---|
jsdoc.commentLineStrategy |
'singleLine' | 'multiline' | 'keep' |
'singleLine' |
Comment block style policy |
jsdoc.lineWrappingStyle |
'greedy' | 'balance' |
'greedy' |
Wrapping strategy for long descriptions |
jsdoc.addDefaultToDescription |
boolean |
true |
Append default values to @param descriptions |
jsdoc.bracketSpacing |
boolean |
false |
Add spaces inside JSDoc type braces |
jsdoc.capitalizeDescriptions |
boolean |
true |
Capitalize the first letter of tag descriptions |
jsdoc.descriptionTag |
boolean |
false |
Emit @description tag instead of inline description |
jsdoc.descriptionWithDot |
boolean |
false |
Add a trailing dot to the end of descriptions |
jsdoc.keepUnparsableExampleIndent |
boolean |
false |
Preserve indentation in unparsable @example code |
jsdoc.preferCodeFences |
boolean |
false |
Prefer fenced code blocks over 4-space indentation |
jsdoc.separateReturnsFromParam |
boolean |
false |
Add a blank line between final @param and @returns |
jsdoc.separateTagGroups |
boolean |
false |
Add blank lines between different tag groups |
Tip: jsdoc: true is equivalent to enabling JSDoc with default settings.
| Option | Type | Default | Description |
|---|---|---|---|
sortImports |
boolean | object |
disabled | Experimental import sorting configuration |
sortPackageJson |
boolean | object |
true |
Experimental package.json sorting (object form: { sortScripts?: boolean }) |
sortTailwindcss |
boolean | object |
disabled | Experimental Tailwind CSS class sorting (enable with {} for defaults) |
Use sortImports: true to enable import sorting with defaults, or pass an object to customize behavior.
Available keys:
customGroups: Ordered custom group definitions{ elementNamePattern?: string[]; groupName?: string; modifiers?: string[]; selector?: string }[]groups: Ordered group list; supports nested arrays and boundary markers like{ newlinesBetween: boolean }ignoreCase: Ignore case when sorting (defaulttrue)internalPattern: Glob patterns for internal importsnewlinesBetween: Insert blank lines between groups (defaulttrue)order:'asc' | 'desc'(default'asc')partitionByComment: Split groups by comments (defaultfalse)partitionByNewline: Split groups by blank lines (defaultfalse)sortSideEffects: Sort side-effect imports (defaultfalse)
- Boolean to toggle (default
true). - Object form:
{ sortScripts?: boolean }(defaultfalse).
Enable experimental Tailwind CSS class sorting powered by prettier-plugin-tailwindcss (set sortTailwindcss: true for defaults, or pass an object for custom options):
// eslint.config.mjs
import pluginOxfmt from 'eslint-plugin-oxfmt'
export default [
{
...pluginOxfmt.configs.recommended,
files: ['**/*.{js,ts,jsx,tsx}'],
rules: {
'oxfmt/oxfmt': [
'error',
{
sortTailwindcss: {},
},
],
},
},
]You can pass the Tailwind plugin options to control which attributes/functions are sorted or keep duplicates:
{
"sortTailwindcss": {
"attributes": ["class", "className", ":class"],
"config": "./tailwind.config.js",
"functions": ["clsx", "cn"],
"preserveDuplicates": false,
"preserveWhitespace": false,
"stylesheet": "./src/app.css"
}
}Use overrides to apply different oxfmt options per file glob (later entries win on conflicts):
// eslint.config.mjs
import pluginOxfmt from 'eslint-plugin-oxfmt'
export default [
{
...pluginOxfmt.configs.recommended,
files: ['**/*.{js,ts,jsx,tsx}'],
rules: {
'oxfmt/oxfmt': [
'error',
{
overrides: [
{
files: ['**/*.test.ts'],
excludeFiles: ['**/fixtures/**'],
options: {
semi: false,
trailingComma: 'none',
},
},
{
files: ['packages/**/src/**/*.{ts,tsx}'],
options: {
printWidth: 90,
sortImports: {
newlinesBetween: true,
},
},
},
],
},
],
},
},
]This plugin provides a single rule that formats your code using oxfmt.
- Recommended:
error - Fixable: Yes (automatically applies formatting)
- Type: Layout
oxfmt/cli-parity tries to match oxfmt CLI behavior for files processed by ESLint.
It respects:
.oxfmtrc.json.oxfmtrc.jsoncoxfmt.config.*.editorconfigignorePatterns.gitignore.prettierignore- default ignored directories
- default ignored lockfiles
Note: ESLint still controls file discovery. Files excluded by ESLint will never reach this rule.
recommended: forceseslint-parser-plainfor matched filesrecommendedWithoutParser: parser-agnostic (safe to compose with language-specific parsers)cliParity: parser-agnostic preset tuned for CLI-like config/ignore behavior
When composing shareable configs, prefer recommendedWithoutParser if parser ownership belongs to another preset.
Add this to your .vscode/settings.json:
{
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit"
},
"eslint.validate": [
"javascript",
"javascriptreact",
"typescript",
"typescriptreact"
]
}oxfmt is a modern, blazing-fast formatter written in Rust as part of the Oxc project. It aims to be a drop-in replacement for Prettier with significantly better performance.
- Performance: 50-100x faster than Prettier
- Compatibility: Designed to be Prettier-compatible
- Modern: Built with modern JavaScript/TypeScript in mind
- Maintained: Part of the actively developed Oxc project