Understanding Prettier and ESLint: Complementary Tools
Before diving into the configuration, it's essential to understand the fundamental difference between these two powerful tools. Prettier and ESLint serve distinct but complementary purposes in a TypeScript development workflow.
Prettier is an opinionated code formatter that enforces consistent style by parsing your code and reprinting it with its own rules. It handles indentation, line length, semicolons, quotes, trailing commas, and more. For TypeScript projects, Prettier supports .ts, .tsx, .js, and .jsx files, handling TypeScript-specific syntax seamlessly.
ESLint is a static code analysis tool that identifies problematic patterns in JavaScript and TypeScript code. It checks for errors, enforces coding standards, and can auto-fix many issues. In TypeScript projects, ESLint with typescript-eslint provides deep integration with TypeScript's type system, catching issues that go beyond simple formatting.
The key insight is that these tools work best when they don't overlap. Let Prettier handle formatting concerns while ESLint focuses on code quality and potential bugs.
Why Use Both?
Using ESLint for formatting creates conflicts and confusion. The modern approach is to let Prettier handle style concerns while ESLint catches code issues like unused variables, potential bugs, and best practice violations. eslint-config-prettier disables formatting rules in ESLint that conflict with Prettier, ensuring they work together harmoniously.
For teams adopting TypeScript-first development practices, this separation of concerns is essential for maintaining clean, consistent codebases.
Prettier Formatting
Handles line width, quotes, semicolons, indentation, trailing commas, and bracket spacing automatically.
ESLint Analysis
Catches unused variables, type errors, accessibility issues, code complexity, and potential runtime bugs.
TypeScript Integration
typescript-eslint provides deep type checking and TypeScript-specific rule enforcement.
Auto-Fix Capabilities
Both tools can automatically fix many issues, saving time on manual corrections.
Setting Up Prettier in Your TypeScript Project
Prettier provides excellent support for TypeScript and requires minimal configuration to get started.
Installation
Add Prettier as a development dependency to your project:
npm install --save-dev prettier
# or
yarn add --dev prettier
Prettier Configuration
Create a .prettierrc file or prettier.config.js in your project root. Here are the essential options for TypeScript projects:
{
"printWidth": 100,
"tabWidth": 2,
"semi": true,
"singleQuote": true,
"trailingComma": "es5",
"bracketSpacing": true,
"arrowParens": "avoid",
"parser": "typescript"
}
Configuration explained:
- printWidth: Line length limit (100 characters works well for most screens)
- tabWidth: 2 spaces for TypeScript indentation
- semi: Always use semicolons
- singleQuote: Prefer single quotes over double
- trailingComma: Use trailing commas where valid in ES5
- bracketSpacing: Add spaces inside object literals
- arrowParens: Omit parentheses around single-argument arrow functions
Ignoring Files
Create a .prettierignore file to exclude generated files and dependencies:
node_modules
dist
build
coverage
.next
.eslintrc.js
*.config.js
When configuring your TypeScript build tooling, ensure your Prettier settings complement your tsconfig.json configuration for optimal consistency across your development workflow.
Setting Up ESLint with TypeScript
ESLint 9+ uses the new Flat Config format, which provides a cleaner and more intuitive configuration experience for TypeScript projects.
Installation
Install the necessary packages for TypeScript linting:
npm install --save-dev eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin eslint-config-prettier eslint-plugin-import
# or
yarn add --dev eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin eslint-config-prettier eslint-plugin-import
ESLint Flat Configuration
Create eslint.config.js in your project root:
import tseslint from 'typescript-eslint';
import prettierConfig from 'eslint-config-prettier';
import importPlugin from 'eslint-plugin-import';
export default tseslint.config(
// Use TypeScript parser and recommended rules
...tseslint.configs.recommended,
// Configure TypeScript files
{
files: ['**/*.ts', '**/*.tsx'],
languageOptions: {
parser: tseslint.parser,
ecmaVersion: 'latest',
sourceType: 'module',
globals: {
node: true,
es2021: true
}
},
plugins: {
import: importPlugin,
'@typescript-eslint': tseslint.plugin
},
rules: {
// Import rules for better module handling
'import/no-unresolved': 'error',
'import/named': 'error',
'import/default': 'error',
'import/export': 'error',
// TypeScript-specific rules
'@typescript-eslint/no-explicit-any': 'warn',
'@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_' }],
'@typescript-eslint/explicit-function-return-type': 'off',
'@typescript-eslint/no-non-null-assertion': 'warn'
}
},
// Prettier config must be last to disable conflicting rules
prettierConfig
);
TypeScript ESLint Configuration Levels
The typescript-eslint package provides three configuration levels:
- recommended: Essential rules that catch common issues
- strict: Stricter rules for better code quality
- stylistic: Rules for consistent code style
Start with recommended and gradually add stricter rules as your team adapts. For a comprehensive approach to code quality, consider how these tools integrate with your wider development practices.
Editor Integration for Seamless Workflow
Configure your editor to automatically format and lint code on save, creating a frictionless development experience.
VS Code Settings
Create a .vscode/settings.json file in your project root:
{
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit"
},
"eslint.validate": [
"javascript",
"typescript"
],
"prettier.documentSelectors": [
"**/*.ts",
"**/*.tsx",
"**/*.js",
"**/*.jsx"
]
}
Recommended VS Code Extensions
Share these extensions with your team by creating .vscode/extensions.json:
{
"recommendations": [
"esbenp.prettier-vscode",
"dbaeumer.vscode-eslint"
]
}
When team members open the project, VS Code will prompt them to install these extensions automatically.
Cursor/Other VS Code-Based Editors
Since Cursor is built on VS Code, the same configuration files work identically. The editor extension ecosystem is fully compatible. Configuring your editor properly eliminates inconsistent formatting across team members and prevents the common tooling configuration issues that can slow down development.
Automating Consistency with Git Hooks
Set up pre-commit hooks to ensure all code meets your standards before it enters the repository.
Installing husky and lint-staged
npm install --save-dev husky lint-staged
npx husky install
Configuring Pre-commit Hooks
Add a pre-commit hook that runs Prettier and ESLint on staged files:
npx husky add .husky/pre-commit "npx lint-staged"
package.json Configuration
Add the lint-staged configuration:
{
"lint-staged": {
"**/*.ts": ["prettier --write", "eslint --fix"],
"**/*.tsx": ["prettier --write", "eslint --fix"],
"**/*.js": ["prettier --write", "eslint --fix"],
"**/*.json": ["prettier --write"]
}
}
This configuration ensures that:
- Only staged files are checked (not the entire project)
- Prettier runs first to format the code
- ESLint then checks for issues and auto-fixes what it can
- If ESLint finds non-fixable issues, the commit is blocked
Performance Consideration
For large projects, pre-commit hooks can slow down commits. Consider running the full lint check in CI instead, using pre-commit hooks only for formatting fixes. This approach aligns with modern frontend build optimization practices for faster development cycles.
CI/CD Integration for Enforcement
Add linting and formatting checks to your continuous integration pipeline to prevent bad code from merging.
GitHub Actions Workflow
Create .github/workflows/lint.yml:
name: Code Quality
on:
push:
branches: [main, develop]
pull_request:
branches: [main, develop]
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Check Prettier formatting
run: npm run format:check
- name: Run ESLint
run: npm run lint
npm Scripts
Add these scripts to your package.json:
{
"scripts": {
"format": "prettier --write .",
"format:check": "prettier --check .",
"lint": "eslint . --ext .ts,.tsx,.js,.jsx",
"lint:fix": "eslint . --ext .ts,.tsx,.js,.jsx --fix"
}
}
Why CI Enforcement Matters
CI enforcement ensures that:
- No exceptions are made for any contributor
- All pull requests meet the same standards
- Issues are caught before they reach production
- New team members automatically learn the standards
For TypeScript projects, consistent tooling enhances the benefits of type-safe development, catching potential issues early in the development cycle and maintaining code quality across distributed teams.
Frequently Asked Questions
Should I use ESLint for formatting?
No. ESLint deprecated all formatting rules in 2023. Use Prettier for formatting and ESLint for code quality. This separation prevents conflicts and confusion.
How do I handle Prettier and ESLint conflicts?
Install eslint-config-prettier and extend it as the last config in your ESLint setup. This disables all ESLint rules that Prettier handles, eliminating conflicts.
What ESLint config should I start with?
Start with the recommended configurations from typescript-eslint. These catch common issues without being overwhelming. Add stricter rules gradually as your team adapts.
How do I ignore files for both tools?
Prettier uses .prettierignore, ESLint uses .eslintignore, and both respect .gitignore. Keep these files in sync for consistent behavior.