I wanted to set eslint along with prettier in a new ts project with the latest instructions. I was getting wrong/hallucinated results from AI search, many articles had outdated information and finally, the official documentation was not as clear as it could have been.
The confusion in the official docs was around whether the 'plugins' section in eslint.config.js was needed when using the 'extends' section. It wasn't needed (for this basic configuration), as 'extends' items automatically pull in rules and plugins, etc.
It turned out to be pretty straight forward, and here's a step by step guide helping you to do it in just a few minutes!
Install eslint and typescript-eslint.
From typescript-eslint.io/getting-started and typescript-eslint.io: use with prettier
-
bunx create-vite some-project-nameand choose something with TypeScript. I chose framework: React, variant: typescript, use rolldown-vite: No, Install with bun: Yes. - Depending on what you select when running the previous line, this line may not be needed:
bun add eslint @eslint/js typescript typescript-eslint - For vscode integration, install
dbaeumer.vscode-eslint("eslint" from Microsoft)
Next, install Prettier.
From prettier.io/docs/install and github.com/prettier/eslint-config-prettier
bun add --dev --exact prettierbun --eval "fs.writeFileSync('.prettierrc','{}\n')"bun --eval "fs.writeFileSync('.prettierignore','# Ignore artifacts:\nbuild\ncoverage\n')"bunx prettier . --checkbun add --dev eslint-config-prettier-
bunx eslint-config-prettier eslint.config.js(makes sure eslint is working)
Wrap up by setting up a husky pre-commit hook!
This will prettify all staged files automatically. Also from prettier.io/docs/install.
bun --eval "fs.writeFileSync('.husky/pre-commit','bunx lint-staged\n')"bunx husky initbun add --dev husky lint-staged- Add the
lint-stagedproperty to package.json (see below) - For vscode integration, install
esbenp.prettier-vscode("prettier-vscode" from prettier.io/Prettier)
I generally like to commit the empty project at this point.
package.json
{
"name": "some-project-name",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "tsc -b && vite build",
"lint": "eslint .",
"preview": "vite preview",
"prepare": "husky"
},
"lint-staged": {
"**/*": "prettier --write --ignore-unknown"
},
"dependencies": {
"react": "^19.2.0",
"react-dom": "^19.2.0"
},
"devDependencies": {
"@eslint/js": "^9.39.2",
"@types/node": "^24.10.1",
"@types/react": "^19.2.5",
"@types/react-dom": "^19.2.3",
"@vitejs/plugin-react": "^5.1.1",
"eslint": "^9.39.2",
"eslint-config-prettier": "^10.1.8",
"eslint-plugin-react-hooks": "^7.0.1",
"eslint-plugin-react-refresh": "^0.4.24",
"globals": "^16.5.0",
"husky": "^9.1.7",
"lint-staged": "^16.2.7",
"prettier": "3.7.4",
"typescript": "^5.9.3",
"typescript-eslint": "^8.53.0",
"vite": "^7.2.4"
}
}
eslint.config.js
import js from "@eslint/js";
import globals from "globals";
import reactHooks from "eslint-plugin-react-hooks";
import reactRefresh from "eslint-plugin-react-refresh";
import tseslint from "typescript-eslint";
import { defineConfig, globalIgnores } from "eslint/config";
import eslintConfigPrettier from "eslint-config-prettier/flat";
export default defineConfig([
globalIgnores(["dist"]),
{
files: ["**/*.{ts,tsx}"],
extends: [
js.configs.recommended,
tseslint.configs.strict,
tseslint.configs.stylistic,
reactHooks.configs.flat.recommended,
reactRefresh.configs.vite,
eslintConfigPrettier,
],
languageOptions: {
ecmaVersion: 2020,
globals: globals.browser,
},
},
]);
Top comments (0)