Guardas un archivo, Prettier lo reformatea, y ESLint marca de inmediato un error en la línea que Prettier acaba de tocar. Las dos herramientas están configuradas, las dos están activas, y en vez de ayudarte se pelean entre sí.

Las dos herramientas hacen trabajos distintos. El problema es que ambas tienen una opinión sobre una sola cosa: cómo debe verse el código en la pantalla.

Qué comprueba realmente ESLint

ESLint analiza tu código en un árbol de sintaxis abstracta y ejecuta reglas sobre su estructura. La mayor parte de su valor viene de reglas que detectan bugs reales: una variable que nunca se define, un return inalcanzable después de otro return, un import que quedó sin usar tras una refactorización, una clave duplicada en un objeto que sobrescribe la primera en silencio. Son comprobaciones semánticas: ESLint lee lo que el código hace.

ESLint también incluye reglas de estilo, como exigir comillas simples o punto y coma y limitar la longitud de línea — aunque desde la versión 8.53 están en desuso a favor del proyecto independiente ESLint Stylistic, y de todas formas no detectan bugs. Capturan desacuerdos de estilo, y es justo esa parte la que choca con Prettier.

Qué hace realmente Prettier

Prettier no lee tu código para entender su significado. Lo analiza, descarta el formato original y reimprime todo el archivo según sus propias reglas de indentación, longitud de línea, estilo de comillas y dónde cortar las líneas largas. Ejecútalo dos veces sobre la misma entrada y obtienes una salida idéntica byte a byte: ese es el objetivo. Apenas hay nada que configurar, por diseño: un .prettierrc, un puñado de opciones, y cada archivo del proyecto se ve igual sin importar quién lo escribió.

Ejecuta prettier --check . en CI y te dice si cada archivo coincide con ese formato canónico. No sabe ni le importa si el código funciona.

Por qué ESLint y Prettier entran en conflicto

El solape es reducido. Las reglas de estilo de ESLint (comillas, punto y coma, indentación, comas finales) cubren el mismo terreno que Prettier, y los dos no siempre coinciden. Cuando no coinciden, aparece el ciclo del primer párrafo: Prettier reformatea una línea, ESLint marca el resultado, lo “arreglas” a mano, Prettier lo deja como estaba.

Así que deja de pedirle a ESLint que tome decisiones de formato. Déjalo hacer solo lo que Prettier no puede: detectar bugs y patrones incorrectos. eslint-config-prettier es una configuración que desactiva toda regla de ESLint que pueda chocar con Prettier o duplicar su salida. No añade nada. Solo desactiva.

Un segundo paquete, eslint-plugin-prettier, va en la dirección opuesta: ejecuta Prettier como regla de ESLint, así que cada diferencia de formato aparece como su propio error de ESLint. La documentación oficial de Prettier califica esta configuración como “generalmente no recomendada”. Es más lento, porque ESLint invoca a Prettier por cada archivo. También es más ruidoso: un solo reformateo puede convertirse en docenas de errores de ESLint en vez de un diff. En vez de eso, ejecuta Prettier por su cuenta.

Cómo configurar ESLint y Prettier con flat config

Desde ESLint 9, el archivo de configuración es eslint.config.js, un array plano de objetos de configuración que sustituye el antiguo formato .eslintrc. Si empiezas un proyecto desde cero:

1
npm init @eslint/config@latest

Eso te guía por unas preguntas y escribe un eslint.config.js. Después añade Prettier y la configuración que silencia las reglas en conflicto:

1
npm install --save-dev --save-exact prettier eslint-config-prettier

eslint-config-prettier incluye un punto de entrada para flat config. Impórtalo y colócalo al final del array. Las entradas posteriores sobrescriben a las anteriores, así que cualquier cosa colocada después puede reactivar una regla desactivada:

1
2
3
4
5
6
7
8
// eslint.config.js
import js from "@eslint/js";
import eslintConfigPrettier from "eslint-config-prettier/flat";

export default [
  js.configs.recommended,
  eslintConfigPrettier,
];

Añade un .prettierrc para las opciones propias de Prettier (la mayoría de los equipos casi no necesitan ninguna):

1
2
3
4
{
  "semi": true,
  "singleQuote": true
}

Y dos scripts en package.json para que las dos herramientas corran de forma independiente:

1
2
3
4
5
6
{
  "scripts": {
    "lint": "eslint .",
    "format": "prettier --write ."
  }
}

eslint-config-prettier también incluye una herramienta de línea de comandos que revisa tu configuración y lista cualquier regla que siga en conflicto con Prettier:

1
npx eslint-config-prettier eslint.config.js

El código de salida 0 significa que no hay conflictos. 2 significa que encontró algunos, y la salida nombra cada regla para que puedas eliminarla.

Usar eslint-config-prettier con typescript-eslint

Para TypeScript, typescript-eslint se conecta al mismo array de flat config y añade sus propias reglas semánticas sobre el ESLint básico — tseslint.configs.recommended prohíbe el any escrito directamente en tu código (no-explicit-any). Detectar un any que se filtró desde una llamada a una librería sin tipar es un paso aparte y más pesado: cambia a recommendedTypeChecked, que necesita parserOptions.project apuntando a tu tsconfig.json para que las reglas puedan pedirle tipos reales al compilador de TypeScript. La regla del orden no cambia: eslint-config-prettier siempre va al final.

1
2
3
4
5
6
7
8
9
import js from "@eslint/js";
import tseslint from "typescript-eslint";
import eslintConfigPrettier from "eslint-config-prettier/flat";

export default [
  js.configs.recommended,
  ...tseslint.configs.recommended,
  eslintConfigPrettier,
];

Ninguna de las dos herramientas comprueba si tus tipos están bien diseñados. Un generic que debería estar restringido con extends pasa ambos controles; eso es trabajo del compilador, y lo explicamos en Generics en TypeScript Explicados con Ejemplos.

Ejecutar ESLint y Prettier en el editor y en CI

En el editor, activa “format on save” para ejecutar Prettier, y deja que ESLint informe por separado, al guardar o en un hook de pre-commit. Una vez que existen los archivos de configuración anteriores, las extensiones del editor para ambas herramientas funcionan de forma independiente. No hace falta conectar una con la otra.

En CI, ejecuta las dos como pasos separados:

1
2
npx eslint .
npx prettier --check .

Un prettier --check fallido significa que alguien no ejecutó el formateador. Un eslint fallido significa que el código tiene un problema real. Separar los dos pasos mantiene esa diferencia legible en el log de la CI.

Errores comunes al configurar ESLint y Prettier

El más común es instalar eslint-plugin-prettier porque algún tutorial antiguo lo recomienda, y luego preguntarse por qué eslint . va lento y la lista de problemas es el triple de lo esperado. Quita el plugin, mantén eslint-config-prettier, ejecuta Prettier por su cuenta.

El segundo es el orden. Si una configuración compartida o un override específico del proyecto queda después de eslintConfigPrettier en el array, reactiva en silencio una regla que estaba desactivada. Esa misma herramienta de línea de comandos lo detecta antes de que llegue a una PR.

El tercero es el alcance. Esta pareja está pensada para JavaScript y TypeScript. Si empiezas un proyecto nuevo y prefieres una sola herramienta más rápida que haga ambos trabajos, mira Biome. Es un compromiso distinto, no un sustituto directo de la configuración anterior.

Qué hacer hoy en tu proyecto

Abre tu package.json. Si eslint-plugin-prettier está ahí, desinstálalo. Añade eslint-config-prettier como última entrada en eslint.config.js, ejecuta npx eslint-config-prettier eslint.config.js y corrige lo que señale. Después de eso, cada error de ESLint que veas será sobre algo que ESLint encontró en la estructura del código, no una línea que Prettier habría reformateado igual. Todavía no detecta todo: olvidar await dentro de un bucle o dejar una promise rejection sin gestionar es un error de lógica, no un error de lint, salvo que añadas un plugin para ello — consulta Async/Await y el Event Loop de JavaScript Explicados para ver cómo se ven esos errores.

Artículos relacionados