Using Prettier and ESLint for JavaScript and TypeScript Formatting

Set up consistent code formatting and linting in your TypeScript projects with this comprehensive guide to modern tooling.

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.

What Each Tool Handles

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.

Ready to Improve Your Code Quality?

Digital Thrive helps development teams implement modern tooling and best practices for TypeScript-first development.