Migrate remix eslint-config to ESLint v9
Custom ESLint v9 flat config for Remix from scratch.
This weekend I finally found time to upgrade the old deprecated @remix-run/eslint-config
in my Remix projects (not yet migrated to React Router v7).
I first attempted to remove the package entirely and adopt the recommended configuration from the official Remix template (https://github.com/remix-run/remix/blob/main/templates/remix/.eslintrc.cjs). However, after running the ESLint migration tool (https://eslint.org/docs/latest/use/getting-started) npm init @eslint/migrate-config
, the output was problematic. This was likely because some dependencies weren’t yet compatible with ESLint’s new flat configuration system.
Instead, I opted for a manual approach, installing and configuring each plugin individually. Here’s the outcome:
Install new dependencies
npm install --save-dev @eslint/js @remix-run/dev @typescript-eslint/eslint-plugin @typescript-eslint/parser eslint eslint-plugin-react eslint-plugin-simple-import-sort eslint-plugin-tailwindcss globals tailwindcss typescript-eslint
Changes in the dev dependencies should look similar to these:
"devDependencies": {
+ "@eslint/js": "^9.20.0",
- "@remix-run/dev": "^2.13.1",
- "@remix-run/eslint-config": "^2.13.1",
+ "@remix-run/dev": "^2.15.3",
@@ -89,16 +89,22 @@
+ "@typescript-eslint/eslint-plugin": "^8.23.0",
+ "@typescript-eslint/parser": "^8.23.0",
- "eslint": "^9.3.0",
+ "eslint": "^9.20.0",
+ "eslint-plugin-react": "^7.37.4",
+ "globals": "^15.14.0",
+ "typescript-eslint": "^8.23.0",
},
Migrate old config file
Now we can remove .eslintrc.cjs
and replace it with the new flat config eslint.config.js
:
import jseslint from "@eslint/js";
import reactPlugin from "eslint-plugin-react";
import globals from "globals";
import tseslint from "typescript-eslint";
/** @type {import('eslint').Linter.Config[]} */
export default [
{ files: ["**/*.{js,mjs,cjs,ts,jsx,tsx}"] },
{ languageOptions: { globals: { ...globals.browser, ...globals.node } } },
jseslint.configs.recommended,
...tseslint.configs.recommended,
reactPlugin.configs.flat.recommended,
reactPlugin.configs.flat["jsx-runtime"]
];
Optionally, this is how it looks like if you would like to include the eslint-plugin-tailwindcss
and eslint-plugin-simple-import-sort
plugins:
import jseslint from "@eslint/js";
import reactPlugin from "eslint-plugin-react";
import simpleImportSort from "eslint-plugin-simple-import-sort";
import globals from "globals";
import tseslint from "typescript-eslint";
import tailwind from "eslint-plugin-tailwindcss";
/** @type {import('eslint').Linter.Config[]} */
export default [
{ files: ["**/*.{js,mjs,cjs,ts,jsx,tsx}"] },
{ languageOptions: { globals: { ...globals.browser, ...globals.node } } },
jseslint.configs.recommended,
...tseslint.configs.recommended,
reactPlugin.configs.flat.recommended,
reactPlugin.configs.flat["jsx-runtime"],
{
plugins: {
"simple-import-sort": simpleImportSort,
},
rules: {
"simple-import-sort/imports": "error",
"simple-import-sort/exports": "error",
},
},
...tailwind.configs["flat/recommended"],
];