Migrate remix eslint-config to ESLint v9

Posted 3/2/20252 minute read

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"],
];
32
0