Reglas ESLint esenciales
Configura las reglas ESLint más impactantes para React, hooks, TypeScript, importaciones y accesibilidad.
Receta
Tarjeta de referencia rápida — lista para copiar y pegar.
// Niveles de severidad
"off" // 0 — desactiva la regla
"warn" // 1 — advertencia amarilla, no falla CI
"error" // 2 — error rojo, falla CI y bloquea compilaciones
// Patrón común: anular una regla
{
rules: {
"rule-name": "error",
"rule-name": ["error", { option: "value" }],
},
}Cuándo usarlo: Cuando los preajustes predeterminados son demasiado flexibles o estrictos y necesitas afinar reglas específicas.
Ejemplo funcional
// eslint.config.mjs
import { FlatCompat } from "@eslint/eslintrc";
import { dirname } from "path";
import { fileURLToPath } from "url";
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const compat = new FlatCompat({ baseDirectory: __dirname });
const eslintConfig = [
...compat.extends("next/core-web-vitals", "next/typescript"),
{
rules: {
// --- Reglas React ---
"react/jsx-no-target-blank": "error",
"react/no-unescaped-entities": "off",
"react/self-closing-comp": "warn",
"react/jsx-curly-brace-presence": [
"warn",
{ props: "never", children: "never" },
],
// --- Reglas hooks ---
"react-hooks/rules-of-hooks": "error",
"react-hooks/exhaustive-deps": "warn",
// --- Reglas TypeScript ---
"@typescript-eslint/no-unused-vars": [
"error",
{
argsIgnorePattern: "^_",
varsIgnorePattern: "^_",
caughtErrorsIgnorePattern: "^_",
},
],
"@typescript-eslint/no-explicit-any": "warn",
"@typescript-eslint/consistent-type-imports": [
"error",
{ prefer: "type-imports" },
],
// --- Reglas de importación ---
"import/order": [
"warn",
{
groups: [
"builtin",
"external",
"internal",
["parent", "sibling"],
"index",
"type",
],
"newlines-between": "always",
alphabetize: { order: "asc", caseInsensitive: true },
},
],
"import/no-duplicates": "error",
// --- Reglas de accesibilidad ---
"jsx-a11y/alt-text": "error",
"jsx-a11y/anchor-is-valid": "warn",
},
},
];
export default eslintConfig;Lo que esto demuestra:
- Reglas organizadas por categoría: React, hooks, TypeScript, importaciones, accesibilidad
- Combinación de severidades
error(debe corregirse) ywarn(debería corregirse) - Ignorancia basada en patrones de variables sin usar con prefijo
_ - Ordenamiento de importaciones con separación de grupos y alfabetización
Inmersión profunda
Cómo funciona
- Cada regla tiene un nivel de severidad:
"off","warn"o"error" - Las reglas pueden aceptar opciones como el segundo elemento de un array:
["error", { option: true }] - El preajuste
next/core-web-vitalsya habilita muchas reglas — las anulas en tu propia configuración - Las reglas de plugins tienen prefijo del nombre del plugin (p. ej.,
react/,@typescript-eslint/)
Variaciones
Reglas React que vale la pena conocer:
| Regla | Qué captura |
|---|---|
react/jsx-no-target-blank | Falta rel="noreferrer" en enlaces con target="_blank" |
react/no-unescaped-entities | ' o " sin escapar en texto JSX |
react/self-closing-comp | <div></div> en lugar de <div /> para elementos vacíos |
react/jsx-curly-brace-presence | {"string"} innecesario en lugar de string en JSX |
react/no-array-index-key | Usar índice de array como prop key |
Reglas hooks:
| Regla | Qué captura |
|---|---|
react-hooks/rules-of-hooks | Hooks llamados condicionalmente o en bucles |
react-hooks/exhaustive-deps | Dependencias faltantes en useEffect, useMemo, useCallback |
Reglas TypeScript que vale la pena habilitar:
| Regla | Qué captura |
|---|---|
@typescript-eslint/no-unused-vars | Variables declaradas pero sin usar |
@typescript-eslint/no-explicit-any | Usar tipo any |
@typescript-eslint/consistent-type-imports | Falta la palabra clave type en importaciones solo-tipo |
@typescript-eslint/no-non-null-assertion | Usar afirmación ! de no-null |
@typescript-eslint/prefer-nullish-coalescing | Usar OR lógico en lugar de ?? |
Notas de TypeScript
// consistent-type-imports fuerza esto:
import type { User } from "@/types"; // importación solo-tipo
import { fetchUser } from "@/lib/api"; // importación de valor
// En lugar de mezclarlos:
import { User, fetchUser } from "@/lib/api"; // ❌ error de lintTrampas
Cosas que te morderán. Cada trampa incluye qué sale mal, por qué sucede y la solución.
-
Falsos positivos de exhaustive-deps — Esta regla a veces marca referencias estables como
dispatcho refs. Solución: Usa// eslint-disable-next-line react-hooks/exhaustive-depssolo cuando estés seguro de que la dependencia es estable. Nunca la desactives globalmente. -
Conflictos de no-unused-vars con TypeScript — La regla base de ESLint
no-unused-varsy@typescript-eslint/no-unused-varspueden entrar en conflicto. Solución: Desactiva la regla base y usa solo la versión de TypeScript:"no-unused-vars": "off". -
import/order sin auto-corrección — La regla reporta violaciones pero
--fixsolo funciona para reordenar, no para agregar saltos de línea entre grupos retroactivamente. Solución: Ejecutaeslint --fixy agrega manualmente saltos de línea en el primer paso. -
La severidad importa para CI — Usar
"warn"significa que CI pasa incluso con violaciones. Si quieres forzar una regla, usa"error". Solución: Reserva"warn"para reglas hacia las que te estás migrando, usa"error"para reglas forzadas.
Alternativas
Otras formas de resolver el mismo problema — y cuándo es mejor cada una.
| Alternativa | Úsalo cuando | No lo uses cuando |
|---|---|---|
Valores predeterminados de next/core-web-vitals | Deseas valores predeterminados sensatos sin personalización | Necesitas reglas más estrictas o específicas del proyecto |
| Reglas de lint de Biome | Deseas linting más rápido con reglas integradas | Necesitas el rango completo de plugins de ESLint |
Compilador TypeScript (tsc --noEmit) | Deseas verificaciones de nivel de tipo que ESLint no puede hacer | Necesitas enforcement de estilo de código o patrones |
Preguntas frecuentes
¿Cuál es la diferencia entre los niveles de severidad "off", "warn" y "error"?
"off"(0) desactiva la regla completamente."warn"(1) muestra una advertencia amarilla pero no falla CI ni bloquea compilaciones."error"(2) muestra un error rojo, falla CI y bloquea compilaciones.
¿Por qué debería usar @typescript-eslint/no-unused-vars en lugar de la base no-unused-vars?
- La regla base de ESLint
no-unused-varsno entiende la sintaxis de TypeScript (interfaces, alias de tipo, enums). - Las dos reglas entran en conflicto cuando ambas están habilitadas.
- Desactiva la regla base y usa solo la versión de TypeScript:
"no-unused-vars": "off",
"@typescript-eslint/no-unused-vars": ["error", { argsIgnorePattern: "^_" }],¿Qué fuerza consistent-type-imports?
// Forzado (correcto):
import type { User } from "@/types";
import { fetchUser } from "@/lib/api";
// Rechazado (error de lint):
import { User, fetchUser } from "@/lib/api";Separa las importaciones solo-tipo de las importaciones de valor para que los bundlers puedan hacer tree-shake de tipos.
¿Cómo organiza las importaciones la regla import/order?
- Agrupa las importaciones en categorías: builtin, external, internal, parent/sibling, index, type.
- Fuerza saltos de línea en blanco entre grupos con
"newlines-between": "always". - Alfabetiza las importaciones dentro de cada grupo.
Trampa: ¿Por qué import/order no se auto-corrige completamente en la primera ejecución?
eslint --fixpuede reordenar importaciones dentro de grupos.- No siempre agrega saltos de línea en blanco faltantes entre grupos retroactivamente.
- Ejecuta
eslint --fixuna vez, luego agrega manualmente saltos de línea en el primer paso.
¿Cuándo debería usar "warn" versus "error" para una regla?
- Usa
"warn"para reglas hacia las que te estás migrando o que son de carácter consultivo. - Usa
"error"para reglas que deseas forzar estrictamente en CI. - Recuerda:
"warn"no fallará CI, por lo que las violaciones se acumulan silenciosamente si olvidas promocionar a"error".
¿Qué captura react-hooks/exhaustive-deps?
- Dependencias faltantes en arrays de dependencias de
useEffect,useMemoyuseCallback. - Agregar todas las dependencias evita bugs de closure obsoleto.
- Configúralo a
"warn"porque puede producir falsos positivos con referencias estables comodispatch.
Trampa: ¿Puedo desactivar exhaustive-deps globalmente de forma segura?
- No. Desactivarla globalmente oculta bugs reales donde los efectos usan valores obsoletos.
- En su lugar, suprimela por línea con
// eslint-disable-next-line react-hooks/exhaustive-depssolo cuando estés seguro de que la dependencia es estable.
¿Contra qué protege react/jsx-no-target-blank?
- Los enlaces con
target="_blank"sinrel="noreferrer"exponen tu página a ataqueswindow.opener. - La regla fuerza agregar
rel="noreferrer"a todos los enlaces externos.
¿Cómo paso opciones a una regla?
// Solo severidad:
"rule-name": "error"
// Severidad con opciones:
"rule-name": ["error", { option: "value" }]El segundo elemento del array es el objeto de opciones de la regla.
¿Qué reglas específicas de TypeScript debería habilitar más allá de las predeterminadas?
| Regla | Propósito |
|---|---|
@typescript-eslint/no-explicit-any | Marca el uso de any |
@typescript-eslint/no-non-null-assertion | Marca afirmaciones ! |
@typescript-eslint/prefer-nullish-coalescing | Prefiere ?? sobre || |
@typescript-eslint/consistent-type-imports | Fuerza import type |
¿Cómo anulo una regla establecida por next/core-web-vitals?
- Agrega un objeto
rulesdespués del arrayextendsen tu configuración plana. - Tus reglas anulan los valores predeterminados del preajuste.
const eslintConfig = [
...compat.extends("next/core-web-vitals"),
{ rules: { "react/no-unescaped-entities": "off" } },
];Relacionado
- Configuración de ESLint para Next.js — configuración inicial de ESLint
- Plugins de ESLint — instala y configura plugins
- Modo estricto de TypeScript — complementa ESLint con opciones estrictas del compilador
- Integración ESLint + Prettier — evita conflictos de formateo