@k8o/oxc-config
v0.3.0
Published
Shareable oxlint + oxfmt configurations for TypeScript / React / Next.js / Backend / Tailwind / Test projects.
Maintainers
Readme
@k8o/oxc-config
Shareable oxlint + oxfmt configurations for TypeScript / React / Next.js / Backend / Tailwind / Test projects.
Strict by default. Composable via oxlint's native extends.
Browse the effective rules of every layer: https://oxc-config-preview.pages.dev
Install
With Vite+ (vite-plus)
Vite+ bundles oxlint, oxfmt, and oxlint-tsgolint, so you only need to add this config (and any JS plugins you use):
pnpm add -D vite-plus @k8o/oxc-config
# Only when you use the matching lint layer:
pnpm add -D oxlint-tailwindcss # tailwind
pnpm add -D eslint-plugin-regexp # regexp
pnpm add -D @k8o/html-nest # html-nestWith standalone oxlint / oxfmt
pnpm add -D oxlint @k8o/oxc-config
# Add only what you actually use:
pnpm add -D oxfmt # if you use the `fmt` preset
pnpm add -D oxlint-tsgolint # type-aware rules (typescript / react / nextjs / backend)
pnpm add -D oxlint-tailwindcss # the `tailwind` layer
pnpm add -D eslint-plugin-regexp # the `regexp` layer
pnpm add -D @k8o/html-nest # the `html-nest` layer
pnpm add -D eslint-plugin-playwright # the `playwright` layer
pnpm add -D eslint-plugin-storybook storybook # the `storybook` layerRequires oxlint ≥ 1.71. The config enables rules that only exist in recent oxlint, and oxlint fails to build a config that references an unknown rule, so older versions are not supported. Node ≥ 24.13.
Quick start
With Vite+
vp lint reads the lint: field of vite.config.ts, and vp fmt reads fmt:. Both accept the same shape as a standalone oxlint.config.ts / .oxfmtrc.json. Spread our presets in:
// vite.config.ts
import { defineConfig } from 'vite-plus';
import { fmt, nextjs, tailwind, test, TEST_GLOBS } from '@k8o/oxc-config';
export default defineConfig({
fmt,
lint: {
extends: [nextjs, tailwind],
options: {
reportUnusedDisableDirectives: 'error',
typeAware: true,
},
settings: {
react: { version: '19.0.0' },
},
overrides: [{ files: [...TEST_GLOBS], ...test }],
},
staged: {
'*.{js,ts,cjs,mjs,jsx,tsx,json,jsonc}': 'vp check --fix',
},
});package.json scripts:
{
"scripts": {
"lint": "vp lint",
"fmt": "vp fmt",
"check": "vp check", // = fmt + lint + tsc
"check:write": "vp check --fix",
},
}Notes for Vite+ users:
- Don't install
oxlint,oxfmt, oroxlint-tsgolintdirectly — Vite+ wraps them. JS plugins (oxlint-tailwindcss,eslint-plugin-regexp, …) stay separate installs. - Type-aware rules work out of the box (no separate
oxlint-tsgolintinstall needed). vp checkruns format + lint + tsc together — recommended entry for CI.- The
vite.config.tsloader path thatvp lintuses is plain Node ESM (no TS transform). If you need to compose presets across files, keep it inline or build the consumer first.
With standalone oxlint
// oxlint.config.ts
import { defineConfig } from 'oxlint';
import { nextjs, tailwind, test, TEST_GLOBS } from '@k8o/oxc-config';
export default defineConfig({
extends: [nextjs, tailwind],
options: {
reportUnusedDisableDirectives: 'error',
},
settings: {
react: { version: '19.0.0' },
},
overrides: [{ files: [...TEST_GLOBS], ...test }],
});For oxfmt, drop our fmt preset into a JS config, or use the generated JSON (below).
With a plain .oxlintrc.json / .oxfmtrc.json
.oxlintrc.json's extends takes file paths, not npm specifiers (oxc#14413), so JSON consumers cannot import this package. We ship a generated JSON variant of each root lint layer (and the fmt preset) for exactly this case:
// .oxlintrc.json
{
"extends": ["./node_modules/@k8o/oxc-config/dist/nextjs.oxlintrc.json"],
}// .oxfmtrc.json — copy the generated preset, or reference the file in a JS config
// (dist/fmt.oxfmtrc.json)Available JSON layers: base, typescript, react, nextjs, backend, tailwind, regexp, html-nest (as dist/<layer>.oxlintrc.json), plus dist/fmt.oxfmtrc.json. The override-style layers (test, storybook, playwright) are applied via overrides, so JSON consumers copy their rule blocks into an overrides entry by hand.
Layers
base ─┬─ typescript ─┬─ react ── nextjs
│ └─ (stop at typescript for pure TS libs)
│
└─ backend
test (apply via overrides on test globs)
tailwind (compose with react / nextjs via extends)
regexp (compose with any layer via extends)
html-nest (compose with any JSX layer via extends)
storybook (apply via overrides on story globs)
playwright (apply via overrides on e2e globs)
fmt (oxfmt preset, independent of lint layers)| Entry | Use for |
| ---------------------------- | --------------------------------------------------------------- |
| @k8o/oxc-config/base | Lowest common denominator (plain JS) |
| @k8o/oxc-config/typescript | Pure TypeScript libraries / CLIs |
| @k8o/oxc-config/react | Any React app or library |
| @k8o/oxc-config/nextjs | Next.js App Router |
| @k8o/oxc-config/backend | Node, Cloudflare Workers, Hono |
| @k8o/oxc-config/test | Vitest test files (use in overrides) |
| @k8o/oxc-config/tailwind | Tailwind CSS v4 (composes with React / Next.js) |
| @k8o/oxc-config/regexp | Regex safety / ReDoS (composes with any layer) |
| @k8o/oxc-config/html-nest | HTML nesting validity in JSX (composes with react / nextjs) |
| @k8o/oxc-config/storybook | Storybook story files (use in overrides) |
| @k8o/oxc-config/playwright | Playwright e2e specs (use in overrides) |
| @k8o/oxc-config/fmt | oxfmt preset (single quotes, sort imports, …) |
Also exported: TEST_GLOBS, STORYBOOK_GLOBS, PLAYWRIGHT_GLOBS — canonical glob arrays for the overrides entries so you don't hand-copy (and drift from) the file matrix.
Design principles
categoriesdeclared once — only inbase. Higher layers add specific rule overrides on top.- Files are pure deltas. Each layer lists only rules that differ from the category defaults (an off, a warn, a non-default option, or a cherry-pick). Rules that merely restate a category severity are not repeated — the snapshot suite guards against category drift.
nurseryis off. Leaving it at error silently escalates every new upstream nursery rule to an error on each oxlint minor bump.basesetsnursery: 'off'and cherry-picks the handful worth keeping.pluginsis replaced, not merged, by oxlint. Each layer lists every opt-in plugin it depends on (oxlint force-enableseslint/typescript/oxc/unicorn, so those are not listed).options.reportUnusedDisableDirectivesis root-only. Set it on the consumer's config, not on a shared layer.options.typeAware: truefromtypescriptonwards. Installoxlint-tsgolint(or use Vite+) to actually run those rules.
Type-aware linting
The typescript layer and everything above it set options.typeAware: true, which turns on oxlint-tsgolint (a typescript-go / TS7-based type checker). Caveats worth knowing:
- Standalone oxlint users must install
oxlint-tsgolint(it is an optional peer). Vite+ bundles it. - It runs a real type check, so it needs a resolvable
tsconfig.jsonand is meaningfully slower / more memory-hungry than the syntactic rules. - Its version is loosely coupled to oxlint's; when you bump oxlint, bump
oxlint-tsgolintin lockstep.
Recipes
Versioning
Pre-1.0 (0.x): treat any release as potentially breaking.
Because the categories are enabled wholesale, your effective rule set is also a function of the oxlint version you install, not just this package's version — upgrading your oxlint can surface new rules regardless of whether this package changed. To keep upgrades deliberate:
- Pin
oxlint/oxfmtexactly and bump them on purpose. - New rules this package adds ship as
minor; rule removals or severity bumps are called out in the changelog.
