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