Reglas de lint de React 19 en ESLint 10 cuando eslint-plugin-react está desactualizado
Por qué eslint-plugin-react deja de funcionar en ESLint 10, cómo una configuración centrada en Biome reduce las reglas de linting para React a 11, y cómo integrar la versión modificada en una configuración simplificada con Next.js.
La actualización de una base de código React a ESLint 10 a menudo se detiene por una sola dependencia: eslint-plugin-react. En el momento de escribir este texto, su última versión no es compatible con ESLint 10, y la corrección en el proyecto original aún está esperando ser integrada. Este artículo explica por qué ocurre este problema, muestra cómo un fork independiente, @ternaus/eslint-plugin-react, reduce el conjunto de reglas a las comprobaciones relevantes para React 19 una vez que Biome se encarga de la mayor parte del análisis, y explica cómo instalarlo en una configuración sencilla así como en Next.js.
Por qué las reglas de linting son más importantes cuando los agentes escriben código
Cuanto más trabajo un equipo asigna a los agentes de codificación, más expectativas relacionadas con su repositorio deben ser ejecutables. La revisión humana es un método costoso para detectar errores predecibles. Los ganchos de pre-commit, las pruebas, las verificaciones deterministas de convenciones y los commits pequeños que pueden ser revisados convierten esas expectativas en señales de éxito o fracaso con las que los agentes pueden actuar, y mantienen cada fallo lo suficientemente pequeño como para poder diagnosticarlo. El análisis de código es una de esas medidas de control, y por eso perder la capa de análisis de React durante una actualización de la cadena de herramientas es algo más que un simple inconveniente.
Qué se rompe con ESLint 10
Entre los cambios significativos en la versión ESLint 10 se encuentra la eliminación de métodos obsoletos desde hace tiempo del objeto contexto de las reglas. La versión más reciente publicada del plugin para React, eslint-plugin-react@7.37.5, indica que ESLint 9 es la versión compatible más alta, y algunas de sus reglas siguen llamando a esos métodos. Bajo ESLint 10 esto provoca un error como este:
TypeError: contextOrFilename.getFilename is not a function
El método en cuestión, context.getFilename(), fue reemplazado por la propiedad context.filename en la API moderna de reglas, por lo que el código del plugin debe modificarse; no existe ninguna bandera de configuración que lo restaure.
Upstream rastrea el problema en un ticket de GitHub abierto el 7 de febrero de 2026; una solución provisional llegó en forma de solicitud de fusión el 30 de julio. Ninguno de los dos había sido cerrado a finales de agosto de 2026. Verifique su estado antes de actuar: si para cuando lea esto Upstream ya ha lanzado soporte para ESLint 10, la opción más sencilla podría ser actualizar el plugin original.
El fork descrito aquí está orientado a una matriz de soporte específica:
- React 19 y versiones posteriores
- ESLint 10 y versiones posteriores, solo con configuración plana
- Biome 2.5.8 y versiones posteriores
- Node.js 22.13, 24 y 26
La configuración basada en Biome que asume este fork
La selección de reglas solo tiene sentido en relación con un entorno específico. Biome es el formateador y verificador principal, y abarca las comprobaciones generales de JavaScript, TypeScript, JSX, DOM y la mayoría de las características de React. ESLint permanece en la cadena de herramientas únicamente para lo que Biome no ofrece: plugins de framework y unos pocos requisitos específicos de React 19.
Los proyectos de referencia funcionan con:
- React 19 en Next.js 16, escrito en TypeScript
- Biome configurado con el preset
all - ESLint 10 utilizando una configuración plana
- Yarn 4 como gestor de paquetes
- Node.js 22, 24 y 26
Con ese arreglo no se desea contar con un segundo verificador que duplique funciones. Solo se necesitan las comprobaciones específicas de React que añadan información adicional después de que Biome haya ejecutado sus tareas.
De 102 reglas activas a 11
La bifurcación parte del repositorio original y mantiene su historial de Git, licencia MIT y atribución; se mantiene de forma independiente desde allí. En el commit en el que se bifurcó, el repositorio original exportó 104 módulos de reglas. La preconfiguración all activó 102 de ellos (los otros dos estaban obsoletos), y recommended enumeró 22 reglas, de las cuales react/no-unsafe se desactivó explícitamente, quedando 21 en vigor.
Portar todo habría mantenido el paquete grande sin darle un propósito claro. En lugar de eso, cada regla se clasificó según el tipo de decisión que impone:
- Si Biome ya informa del mismo diagnóstico, la regla se descarta.
- Si se trata de formato, nombre, estructura del archivo o política del equipo, pertenece a Biome o a la configuración propia de la aplicación.
.eslintrc, soluciones temporales para el analizador o APIs obsoletas de React, queda fuera del contrato de React 19.Exactamente 20 reglas pertenecieron al primer grupo, incluyendo jsx-key, no-danger, no-unknown-property y self-closing-comp. Un documento de correspondencia en la carpeta docs del fork relaciona cada una de ellas con su equivalente en Biome.
Las reglas que codifican estilos o políticas, como por ejemplo prefer-stateless-function, jsx-sort-props o function-component-definition, fueron eliminadas ya que no aportan información sobre la corrección en React 19. no-unused-prop-types y no-unused-state también desaparecieron porque una verificación del AST en un único archivo no puede responder de manera fiable a preguntas relacionadas con todo el proyecto; las reglas excesivamente detalladas hacen que los desarrolladores y los agentes ignoren los resultados de lint. Todo lo demás excluido era código de compatibilidad heredado que no formaba parte de la matriz de soporte indicada.
Solo cuatro identificadores de reglas provenientes del código fuente lograron mantenerse: no-deprecated, no-invalid-html-attribute, no-direct-mutation-state y jsx-no-constructed-context-values.
Nuevas reglas y una que se eliminó intencionalmente
Tres propuestas del código fuente que aún no se habían integrado eran relevantes para la transición a React 19:
- Componentes de marcador que devuelven
undefined(problema en la capa anterior #3020) - Prohibir el uso de
defaultPropsen componentes funcionales (problema #3911) - Preferir un inicializador perezoso para
useState(PR #3579)
Todos los tres se implementaron, y luego se eliminó nuevamente no-render-return-undefined. React 19 permite que un componente devuelva undefined, por lo que prohibirlo sería un estilo interno disfrazado de regla del framework. Los otros dos se integraron como no-function-default-props, que marca una API que React 19 ignora en componentes funcionales, y prefer-use-state-lazy-initialization, una advertencia sobre tareas evitables en cada renderizado, como pasar expensive() en lugar de () => expensive().
Cinco reglas adicionales se enfocan en comportamientos específicos de React 19, ya sean nuevos o reforzados respecto a versiones anteriores: no-prop-types, no-misspelled-lifecycle-methods, jsx-no-key-after-spread, controlled-form-requires-handler y no-implicit-ref-callback-return. La regla relativa a los callbacks de ref es un buen ejemplo de por qué esto es importante ahora: dado que React 19 permite que los callbacks de ref devuelvan una función de limpieza, una función flecha que devuelva implícitamente un valor desde dicho callback ya no es inofensiva.
Por lo tanto, la versión 8.0.0 incluye 11 reglas, todas clasificadas como recommended. Las nueve reglas de corrección se consideran errores; las dos reglas de rendimiento son advertencias. Un preset all duplicaría la categoría recommended o solo diferiría en gravedad, por lo que el paquete no ofrece esta opción.
Proyectos reales que detectaron problemas que las pruebas no pudieron identificar
A partir de 8.0.0-rc.3, las pruebas unitarias y las verificaciones de paquetes superaron con éxito los controles. Fue al instalar el plugin en aplicaciones reales cuando comenzaron a aparecer los fallos útiles.
La primera categoría afectada fueron los metadatos de atributos HTML. La regla no-invalid-html-attribute rechazaba atributos perfectamente válidos, como alt, accept, name, loading, form y value en los elementos <select>, <option> y <textarea>. Corregir este problema requirió tres rondas de trámite de incidencias y pull requests en el sistema de seguimiento del fork (#21/#22, #25/#26 y #29/#31). La solución definitiva consistió en tratar los atributos de contenido HTML de WHATWG y las propiedades del DOM de React como dos fuentes de información independientes, en lugar de asumir que una sola tabla de metadatos describía ambos.
El segundo problema provino de Next.js. eslint-config-next@16 genera una configuración plana, pero lee las reglas del campo de formato antiguo react.configs.recommended.rules. La versión modificada solo expuso react.configs.flat.recommended, por lo que la configuración falló antes incluso de que se analizara un solo archivo. Un cambio posterior (issue #24, PR #27) añadió ese campo únicamente para lectura, sin restablecer la compatibilidad con .eslintrc. Dado que Next.js importa el plugin por su nombre sin escopo, también se necesita una resolución mediante Yarn, como se muestra a continuación.
Esas integraciones generaron las versiones candidatas 4 a 6 y transformaron la estrategia de pruebas. Antes del lanzamiento final, el archivo tarball de npm se analiza con publint, se importa en pruebas escritas en ESM, CommonJS y TypeScript, y se ejecuta mediante la configuración de Next.js que el paquete afirma soportar, con pruebas continuas en Node.js 22.13, 24 y 26. Esta lección es aplicable a cualquier paquete de herramientas: pruebe el artefacto que publica, desde los consumidores que soporta, y no solo el árbol de fuentes.
El paquete resultante es nativo ESM, solo utiliza configuración plana, y mantiene el conocido espacio de nombres de reglas react/*.
Instalación y configuración
Los comandos utilizan Yarn 4. Primero, agregue Biome, ESLint 10 y el plugin como dependencias de desarrollo:
yarn add --dev @biomejs/biome@'>=2.5.8' eslint@^10 @ternaus/eslint-plugin-react@^8.0.0
Habilite el conjunto completo de reglas estables de Biome y su dominio React en biome.json, para que Biome cubra todo lo que la versión derivada dejó intencionalmente fuera:
{
"linter": {
"domains": {
"react": "all"
},
"rules": {
"preset": "all"
}
}
}
Luego agregue las reglas React restantes a eslint.config.js. La operación de expansión combina el registro de plugins y las reglas del preset en un objeto de configuración delimitado por el patrón files:
import react from '@ternaus/eslint-plugin-react';
export default [
{
files: ['**/*.{js,jsx,mjs,cjs,ts,tsx}'],
...react.configs.flat.recommended,
},
];
Si ese patrón incluye archivos .ts o .tsx, registre un analizador compatible con TypeScript en un objeto de configuración anterior; el preset no lo configura automáticamente.
Ejecute las dos herramientas simultáneamente, generalmente como pasos separados en CI o mediante un único script:
yarn biome check .
yarn eslint .
Los IDs de las reglas mantienen el prefijo react, por lo que las sobrescripciones hechas para el plugin original se aplican también a las reglas que aún existen:
{
rules: {
'react/no-deprecated': 'error',
'react/no-implicit-ref-callback-return': 'error',
},
}
Integración en Next.js
eslint-config-next importa el plugin como eslint-plugin-react. Con Yarn puedes redirigir ese nombre al fork a través de resolutions:
{
"devDependencies": {
"@ternaus/eslint-plugin-react": "8.0.0"
},
"resolutions": {
"eslint-plugin-react": "npm:@ternaus/eslint-plugin-react@8.0.0"
}
}
Mantén alineados los dos números de versión. La resolución impide que eslint-config-next incluya la versión original de ESLint 9 junto con la versión del fork para ESLint 10. Dado que Next.js registra el plugin bajo react, los IDs de reglas existentes react/* siguen funcionando.
Cuándo este fork no es la opción adecuada
- No incluye todas las reglas originales. Las configuraciones que dependen de
react/prop-types,react/display-nameoreact/jsx-sort-propsdeben comparar la lista de reglas soportadas en el repositorio del fork antes de hacer el cambio.
key.Puntos clave
- La caída de ESLint 10 se debe a la eliminación de las API de contexto de reglas, por lo que solo una versión del plugin puede solucionarlo.
- Una pila basada en Biome necesita muchas menos reglas de ESLint para React; clasificar las reglas según el tipo de decisión que imponen es un método reutilizable para reducir cualquier configuración redundante de validación.
- Las reglas que requieren evidencia de todo el proyecto generan ruido en un analizador por archivo y es mejor eliminarlas que tolerarlas.
- Herramientas de prueba publicadas tal como las ven los usuarios: el archivo comprimido, cada formato de módulo y configuraciones reales del framework como
eslint-config-next. - Con Next.js, un alias de Yarn
resolutionspermite que el fork sustituya al paquete sin ámbito sin cambiar los IDs de las reglas.
Las notas de origen y de lanzamiento se encuentran en el repositorio del fork.