Frontend React
Reglas para cada frontend React. Este archivo contiene lo que siempre se aplica; lea la referencia correspondiente antes de trabajar en ese tema.
| Tarea | Leer |
|---|---|
| Carpetas, capas, slices, API pública, importaciones, archivos barrel | references/structure.md |
| Inmersiones profundas en FSD: árbol de decisiones, importaciones cruzadas, entidades, activos, ubicación de auth/API, conexión del framework, migración | references/fsd/ (índice al final de structure.md) |
| Nomenclatura, tamaño de archivo, props, sistema de diseño, layouts, constantes | references/code-style.md |
| Componentes, hooks, efectos, memoización, transiciones, listas largas, hidratación | references/hooks-and-rendering.md |
| Cascadas, tamaño del bundle, división de código, precarga, coste de re-renderizado | references/performance.md |
| Datos del servidor, consultas, mutaciones, loaders, parámetros de búsqueda, Zustand | references/data-and-state.md |
| Formularios, validación, errores de formulario | references/forms.md |
| Semántica, teclado, foco, contraste, regiones en vivo, pruebas de accesibilidad | references/accessibility.md (implementación React) + el skill frontend-accessibility (reglas completas, auditorías) |
| Pruebas unitarias, de componentes y E2E, cobertura, mocking de API | references/testing.md |
| Historias, comprobaciones de accesibilidad de Storybook, regresión visual | references/storybook.md |
| XSS, renderizado de HTML/Markdown/salida LLM, auth, CSRF, CSP, variables de entorno, cierre de sesión | references/security.md |
| Versión de Node, gestor de paquetes, pre-commit + gitleaks, auditoría de dependencias | references/tooling.md |
Traducciones, cambio de idioma, <html lang> | references/i18n.md |
| Configuración de un nuevo proyecto | references/setup.md |
| Imagen de contenedor para el frontend | references/docker.md + el skill docker-kubernetes |
Stack
- React 19 (≥ 19.2 para
useEffectEventy<Activity>) · TypeScript estricto - Renderizado: Vite SPA o modo framework de React Router (SSR + hidratación). Las reglas marcadas como Solo modo framework de React Router se aplican solo allí. Sin Next.js, sin React Server Components, sin SWR.
- Enrutamiento: TanStack Router o React Router — fijo por proyecto, detectarlo del código, nunca mezclar.
- Estado del servidor: TanStack Query · Estado del cliente: Zustand
- Formularios: React Hook Form + Zod o TanStack Form + Zod — fijo por proyecto
- Validación: Zod en cada límite (respuestas de API, formularios, parámetros de búsqueda, env, almacenamiento,
postMessage) - Herramientas: Biome (lint + formato), Steiger (reglas FSD), Vitest + Testing Library, Playwright, Storybook
- Sin React Compiler → la memoización es manual, pero solo después de medir.
Estructura — Feature-Sliced Design
Capas app → pages → widgets → features → entities → shared; las importaciones solo hacia abajo; sin importaciones entre slices de la misma capa; cada slice se importa solo a través de su index.ts con exportaciones con nombre explícitas (sin export *). shared/ui y shared/lib tienen un índice por componente/biblioteca. Comience con app, pages, shared; sin carpetas de capa vacías. El código se queda en la página hasta que se cumpla la regla de extracción: se usa en varios lugares ahora mismo, una razón para cambiar independientemente de cualquier consumidor, una responsabilidad enfocada; las tres, la duplicación es aceptable hasta entonces. Se desaconseja widgets: solo para un bloque compuesto reutilizado en varias páginas que no puede vivir en una página (los widgets existentes siguen siendo válidos). Detalles: references/structure.md, libro de reglas upstream references/fsd/rules.md.
- Un componente por archivo, un hook por archivo, ~150 líneas por componente; componentes prefijados con el nombre de su slice (
references/code-style.md). - Sistema de diseño primero: use los componentes propios de
shared/ui/; nunca importeshared/ui/primitives/(salida de CLI de la biblioteca de UI, nunca editada) fuera deshared/ui/. Sin nuevo CSS antes de comprobar qué pueden hacer los componentes, props y tokens del sistema de diseño.
Componentes y hooks — siempre
- Nunca defina un componente dentro de otro componente — se vuelve a montar en cada renderizado.
- Derive, no sincronice: los valores calculables a partir de props/estado se calculan durante el renderizado. Sin
useEffectque copie props o estado en estado. Reinicie conkey. - Las acciones del usuario se ejecutan en manejadores de eventos, no en efectos desencadenados por banderas de estado.
- Los efectos solo sincronizan con sistemas externos, con dependencias primitivas estrechas, un concern por efecto y una limpieza. Nunca silencie la regla exhaustive-deps.
setState(prev => …)cuando el siguiente valor depende del anterior;useState(() => expensive())para valores iniciales costosos.- Nunca mute props o estado:
toSorted(),toReversed(),toSpliced(),with(), spread. {count > 0 ? <X /> : null}— nunca&&con un número a la izquierda.- Sin
fetchenuseEffect; los datos del servidor provienen de TanStack Query o un route loader. memo/useMemo/useCallbacksolo después de perfilar o cuando se requiera estabilidad referencial.- Los errores nunca se tragan: sin
catchvacío, sin.catch(() => {}). Cada ruta tiene un error boundary; cada consulta tiene UI de carga, error y vacío.
Datos y estado — siempre
- Función de solicitud → hook → componente. HTTP solo a través del cliente en
shared/api/; nunca importe un cliente HTTP en hooks o componentes. - Los datos del servidor viven solo en TanStack Query, a través de una fábrica de claves de consulta con
queryOptions; las respuestas se analizan con Zod en la función de solicitud. - El acceso a recursos simples, CRUD y fábricas de claves de consulta viven en
shared/api(o la página/funcionalidad que los usa exclusivamente);entities/<x>/apisolo para solicitudes con reglas de dominio o mapeo de esa entidad. - Las mutaciones invalidan (o actualizan) cada consulta afectada.
- El estado de la URL (filtros, ordenación, paginación, pestañas) vive en parámetros de búsqueda tipados y validados con Zod — nunca analizados a mano desde
window.location. - Zustand solo para estado compartido del cliente; siempre con un selector; las stores persistidas usan
version,migrateypartialize, y no contienen datos sensibles. - Cargue los datos de la ruta en el route loader (
queryClient.query({ ...options, staleTime: 'static' }), anteriormenteensureQueryData) y en paralelo, no en componentes anidados uno tras otro.
Accesibilidad — incorporada, nunca añadida después
Objetivo: WCAG 2.2 AA. Parte de la definición de hecho para cada componente y página. El skill frontend-accessibility contiene las reglas completas y el procedimiento de auditoría — cárguelo para cada cambio de UI.
- HTML semántico primero:
<button>para acciones,<a>/<Link>para navegación,<label>para cada campo, landmarks, un<h1>y encabezados ordenados. ARIA solo cuando ningún elemento nativo encaje. - Teclado: todo accesible y operable con el teclado; sin
tabIndexpositivo; sin trampas de teclado. - Foco visible en cada elemento interactivo (
:focus-visible, contraste ≥ 3:1). - Gestión del foco: al cambiar de ruta, establezca el título de la página y mueva el foco al
<h1>o<main>; los diálogos atrapan el foco, se cierran conEscapey devuelven el foco al disparador. - Nombres: los botones de icono tienen
aria-label; las imágenes tienenalt(alt=""si son decorativas). - Contraste 4.5:1 para texto, 3:1 para componentes de UI; el color nunca es la única señal.
- Movimiento reducido respetado; regiones en vivo (
role="status"/role="alert") para resultados asíncronos y errores. - Las pruebas consultan por rol y nombre accesible; las historias pasan el addon de accesibilidad; las páginas críticas pasan axe en Playwright.
Pruebas — escritas con la funcionalidad, no después
- Pruebas unitarias y de componentes con Vitest + Testing Library para cada funcionalidad, en el mismo cambio. Pruebe el comportamiento a través de la UI (roles, etiquetas,
user-event), no detalles de implementación. - Cobertura ≥ 80 % líneas y ≥ 80 % ramas, aplicada por umbrales de Vitest en CI.
- Playwright E2E para cada flujo de usuario crítico (inicio de sesión, flujos de negocio principales, pago/checkout, permisos): localizadores basados en roles, sin esperas fijas, estado de autenticación reutilizado, escaneo axe en páginas clave.
- Historia de Storybook para cada componente de UI compartido/reutilizable, cubriendo todos los estados (predeterminado, carga, vacío, error, deshabilitado, contenido largo). Viewport de escritorio por defecto; viewports móviles solo cuando el proyecto requiera diseños responsivos.
- Regresión visual mediante Storybook + capturas de pantalla de Playwright; pruebas de snapshot solo con moderación y de forma dirigida.
Seguridad — siempre
- Sin
dangerouslySetInnerHTMLsin DOMPurify; fuera de React usetextContent, nuncainnerHTML. La salida del modelo y Markdown nunca se renderizan como HTML sin procesar. - Sin tokens ni datos sensibles en
localStorage/sessionStorage/IndexedDB. La autenticación es una cookie de sesiónHttpOnlyestablecida por el backend; para OIDC prefiera un backend-for-frontend. - Las solicitudes que cambian el estado envían el encabezado CSRF;
credentials: 'include'solo a la propia API. - Las variables
VITE_*son públicas — nunca contengan secretos. - Los destinos de
?redirect=son solo rutas relativas del mismo origen (redirección abierta). - El cierre de sesión limpia la sesión del servidor, la caché de TanStack Query y todas las stores persistidas.
- CSP como encabezado HTTP con
object-src 'none',base-uri 'none',frame-ancestors. - Los manejadores de
postMessageverificanevent.origin; nunca fusionen objetos no confiables (contaminación de prototipo). - El frontend nunca aplica autorización — el backend lo hace [ASVS 8.3.1].
TypeScript y Biome
strictactivado (el valor predeterminado desde TypeScript 7 — manténgalo explícito); tambiénnoUncheckedIndexedAccess,verbatimModuleSyntax,moduleResolution: "bundler",libconES2023+DOM. TypeScript 7 por defecto ponetypesa[]— enumere"vite/client"explícitamente;baseUrlha desaparecido, usepaths("@/*": ["./src/*"]).- Sin
any(useunknowny restrinja), sin aserciones no nulas para silenciar errores, sin@ts-ignore(@ts-expect-errorcon una razón si es inevitable). Sin números o cadenas mágicas: constantes con nombre enshared/config/o en elconfig/modeldel slice. - Los tipos de datos externos provienen de esquemas Zod (
z.infer/z.output), no de interfaces escritas a mano que puedan desviarse. - Biome: reglas recomendadas más
noNestedComponentDefinitions,noReExportAllynoConsole(no en el conjunto recomendado);noBarrelFilepermanece desactivado para los archivosindex.tsde slice (references/structure.md). biome ci,tsc --noEmit,steiger ./srcyknippasan localmente y en CI. No desactive reglas en línea sin un comentario que explique por qué.- Comentarios en inglés; los TODOs usan el marcador TODO del proyecto.
Higiene del repositorio — siempre
- Versión de Node fijada:
.nvmrc(LTS),enginesenpackage.jsonconengine-strict,packageManagercon la versión exacta de npm/pnpm; archivo de bloqueo confirmado. - pre-commit instalado para commit y push: gitleaks en cambios staged y en todos los commits no enviados, Biome;
tsc, pruebas relacionadas de Vitest y la auditoría de dependencias al hacer push. Nunca lo evite con--no-verify. - Auditoría de dependencias en CI en cada MR y de forma programada.
Detalles: references/tooling.md.
Docker y despliegue
Las imágenes de contenedor, .dockerignore, Compose y Kubernetes están cubiertos por el skill docker-kubernetes; el flujo de trabajo y la lista de verificación del frontend están en references/docker.md. Cada imagen tiene un .dockerignore. Datos específicos del frontend a respetar allí:
- Los valores
VITE_*están integrados en el bundle en tiempo de compilación. Una imagen para varios entornos necesita configuración en tiempo de ejecución: sirva unconfig.json(oconfig.js) que se escriba al iniciar el contenedor a partir de variables de entorno y se obtenga/valide al iniciar la aplicación — sigue siendo público, sin secretos. - Un Vite SPA son archivos estáticos: sírvalos desde un servidor web con un fallback a
index.htmlpara rutas del lado del cliente, caché larga para activos con hash,no-cacheparaindex.html, y los encabezados de seguridad dereferences/security.md. - El modo framework de React Router (SSR) necesita un servidor Node en tiempo de ejecución (
react-router-serveo un servidor personalizado) — no es un despliegue estático.
Después de escribir
Para una revisión de seguridad del resultado, sugiera el skill appsec-review.
Fuentes
Combinado de las mejores prácticas de React de Vercel Engineering (github.com/vercel-labs/agent-skills, react-best-practices, Licencia MIT) — adaptado de Next.js a Vite/React Router con TanStack Query; los ids de reglas se mantienen para trazabilidad — y el OWASP Secure Agent Playbook (CC BY 4.0; OWASP ASVS 5.0). La estructura sigue Feature-Sliced Design (feature-sliced.design, fsd.how); references/fsd/ está adaptado del skill oficial de agente FSD (github.com/feature-sliced/skills, Licencia MIT). La accesibilidad tiene como objetivo WCAG 2.2.