ESLint Flat Config is now the standard. While the old .eslintrc format still
works, almost all the recent documentation, plugins, and online references have
moved to eslint.config.js. Staying on the old format only makes life harder
when you're trying to keep your tooling up to date.
I finally migrated the linting packages in my monorepo once the flat config
system proved itself to be stable. My main goal was to avoid bugs caused by
extends resolution and to build a configuration structure that actually aligns
with how ESLint works internally.
The shift in logic is pretty significant. We used to rely on a "cascade" system
via extends or overrides. In the new system, everything is just an array of
configuration objects. Order is everything here; the last object in the array
wins if there are conflicting rules.
This change in mindset is vital, especially when handling ignores. In the old
system, we used ignorePatterns. In the new one, if you put ignores inside
the same object as files, it only applies to that specific block. To make
ignores a global rule, it has to sit in its own separate object. I actually
messed this up once, which resulted in my dist/ folder still getting linted
even though I thought I had it covered.
To speed up the migration, I took a few specific steps. First, I mapped out
every single extends and plugin. In the new system, you can't just call
plugin:react/recommended and hope for the best; you have to import the actual
configuration object directly.
Second, I didn't just delete the old config files immediately. I kept
eslint.config.js alongside the old files and made sure our entire CI pipeline
was running smoothly before finally ditching .eslintrc. Leaving two different
sources of truth for too long just leads to inconsistent rules.
I also restructured our shared config packages. Instead of exporting a single
object, I now export an array so users can just spread it into their own configs
easily. For TypeScript integration, I used functions from typescript-eslint to
keep things tidy.
There are a few things to watch out for, like the loss of the root: true
concept, which was used to stop ESLint from searching parent directories. In a
monorepo environment, losing this actually makes more sense because it allows
every package to manage its own configuration independently.
Plugin compatibility was another hurdle. Not every plugin supports flat config out of the box. Some provide ready-to-use arrays, but others are still stuck in the old format. If you see errors about "unknown keys," it's usually a sign you're trying to force an old structure into the new system. The fix is to correct your import method, not just silence the error.
Also, if your ignore rules aren't managed correctly, you'll get flooded with
errors from files you never intended to lint in the first place.
This migration definitely requires an upfront time investment, but it's much better than clinging to outdated configurations. Good luck with your migration.