Stack de Producción Next.js
Valores predeterminados bien pensados para construir y mantener aplicaciones Next.js. Al generar un nuevo proyecto o añadir a uno existente, sigue estas convenciones a menos que el código base existente del usuario haga algo claramente diferente: prioriza los patrones existentes sobre este documento cuando entren en conflicto, y anota brevemente la desviación.
0. Stack de un vistazo
| Capa | Recomendado (preferido, no estricto) | Opciones a ofrecer si se solicita un cambio |
|---|---|---|
| Framework | Next.js (App Router), TypeScript, Server Components + Server Actions | ninguno necesario, esto es el núcleo |
| Gestor de paquetes | pnpm | npm, yarn, bun |
| UI | Tailwind CSS, shadcn/ui, react-icons, Motion (motion.dev) | Radix UI o Mantine, lucide-react, CSS Modules |
| Formularios | React Hook Form + Zod | TanStack Form, Valibot en lugar de Zod |
| Base de datos | PostgreSQL + Drizzle ORM | Prisma, Kysely; MySQL o SQLite para apps pequeñas |
| Autenticación | Better Auth, RBAC | Auth.js, Clerk, Supabase Auth |
| Multimedia | Cloudinary | UploadThing, S3 + CDN, Vercel Blob |
| Testing | Vitest (unit/integración), Playwright (e2e) | Jest, Cypress |
| Monitoreo | Sentry, logging estructurado | Datadog, Highlight, Axiom + pino |
| Lint/Formato | ESLint, Prettier, TypeScript strict | Biome |
| CI/CD | GitHub + GitHub Actions, Vercel, despliegues de vista previa | GitLab CI, Netlify, Cloudflare Pages |
Nunca cambies silenciosamente un paquete de esta tabla por una alternativa (ej. Prisma en lugar de Drizzle, next-auth en lugar de Better Auth). Si el proyecto del usuario ya usa una opción diferente, continúa usándola y señala la discrepancia en lugar de mezclar dos soluciones para la misma tarea.
Estas herramientas son Recomendadas: el usuario las prefiere, pero no son estrictas.
- Donde una sección posterior nombre una herramienta específica (pnpm, Drizzle, Better Auth, Cloudinary, Sentry, etc.), léela como el valor predeterminado recomendado bajo esta regla.
- Antes de cambiar, reemplazar u omitir cualquier herramienta de la tabla, pregunta primero al usuario. Ofrece opciones: mantener la recomendada, o una alternativa de la última columna (u otra mejor práctica), con una línea de compensación para cada una y tu propia recomendación.
- Verifica el estado actual (mantenimiento, cambios importantes) antes de recomendar una alternativa. No te bases solo en la memoria.
- Nunca cambies una herramienta sin una respuesta. Hasta que el usuario responda, mantén el valor predeterminado recomendado.
1. Configuración del proyecto
- Genera con
pnpm create next-app@latest— TypeScript, App Router, Tailwind, ESLin todo habilitado, rechaza el directoriosrc/para queapp/permanezca en la raíz del proyecto (solo proyectos nuevos). - Proyecto existente que ya use
src/: manténsrc/—src/app/,src/components/,src/lib/,src/actions/. Usaapp/solo cuando el proyecto no tenga un directoriosrc/. Sigue el diseño existente y los alias de importación (@/*→src/*); nunca muevas archivos fuera desrc/como parte de una tarea no relacionada. - Usa pnpm para cada comando de instalación/ejecución (
pnpm add,pnpm dlx,pnpm dev). Nunca mezcles archivos de bloqueo de npm/yarn. - Inicializa shadcn/ui mediante
pnpm dlx shadcn@latest init, luego añade componentes bajo demanda (pnpm dlx shadcn@latest add button) en lugar de preinstalar todo el conjunto. - Forma de carpeta base (sin
src/—app/está en la raíz del proyecto):
# proyectos nuevos (sin src/)
app/ # rutas, layouts, límites error/loading/not-found
components/ # UI compartida (ui/ = primitivas shadcn)
lib/ # db, auth, validaciones, utils, rate-limit, logger
actions/ # server actions agrupadas por dominio
hooks/
types/
drizzle/ # migraciones
Proyectos existentes con src/: misma forma bajo src/ (src/app/, src/components/, src/lib/, src/actions/) — los nombres de carpeta permanecen idénticos, solo cambia el prefijo.
2. TypeScript y calidad de código
strict: trueen tsconfig, sinanysin un comentario que lo justifique, sin@ts-ignoresin un problema/razón vinculado.- ESLint (next/core-web-vitals + typescript-eslint) y Prettier configurados y pasando antes de que el código se considere terminado; ejecútalos, no solo lo asumas.
- Prefiere tipos de retorno explícitos en funciones exportadas y Server Actions.
- Mantén los Server Components como predeterminados; añade
"use client"solo donde se requiera interactividad, APIs del navegador o hooks, y empuja el límite lo más bajo (lo más hoja) posible. - Una responsabilidad por archivo/módulo; coloca las server actions de una ruta en
actions/o en un archivo_actions.tsjunto a la ruta, no en línea en archivos de página enormes. - Convenciones de arquitectura: mantén un lugar para cada tipo de lógica — acceso a datos en
lib/db, mutaciones enactions/, UI compartida encomponents/, ayudantes transversales enlib/. Sigue la nomenclatura existente del proyecto (carpeta de archivos, nomenclatura de componentes) en lugar de introducir una nueva convención a mitad de camino. - Revisión de código: trata cada cambio no trivial como del tamaño de un PR — un diff claro, lint/typecheck/tests pasando antes de solicitar revisión, y una descripción de qué cambió y por qué. Al revisar (humano o asistido por IA), verifica la falta de comprobaciones de autenticación, errores no manejados y desviación de convenciones, no solo si se ejecuta.
3. Renderizado, datos y rendimiento
- Renderizado estático vs dinámico: predeterminado a estático; opta por dinámico (
export const dynamic = 'force-dynamic',cookies(),headers()) solo cuando la ruta genuinamente necesite datos por solicitud. - Obtención de datos: obtén datos directamente en Server Components; usa Server Actions para mutaciones, no fetch del lado del cliente a una ruta API a menos que el consumidor sea externo.
- Streaming y Suspense: envuelve secciones lentas/transmisibles en
<Suspense>con un fallback de esqueleto real (no un spinner vacío) para que el resto de la página pueda renderizarse y transmitirse de inmediato — esto también es lo que proporcionaloading.tsxa nivel de ruta. - Almacenamiento en caché y revalidación: usa
use cache/unstable_cache(según la versión de Next) para lecturas costosas; invalida precisamente conrevalidatePath/revalidateTagdespués de mutaciones en lugar de recurrir aforce-dynamicpara "solucionar" la falta de actualización. - Precarga: precarga los enlaces que los usuarios probablemente sigan —
<Link>hace esto automáticamente; no lo deshabilites (prefetch={false}) sin una razón específica. - Carga diferida y división de código: carga diferida de componentes del lado del cliente que están debajo del pliegue o que se usan con poca frecuencia con
next/dynamic; evita grandes paquetes de cliente de bibliotecas que solo se necesitan en una página. - Optimización de imágenes y fuentes: usa siempre
next/imagepara imágenes rasterizadas (width/heightexplícitos ofill+ contenedor dimensionado) ynext/fontpara fuentes — nunca<img>sin procesar ni un<link>a Google Fonts. - Core Web Vitals: trata LCP, CLS, INP como una restricción real — sin contenido tardío que cause desplazamiento de diseño, sin medios sin dimensionar, sin scripts síncronos bloqueantes.
4. UI
- Tailwind para estilos; componentes shadcn/ui como primitivas base, personalizadas a través de los tokens de tema del proyecto (variables CSS) en lugar de anulaciones en línea dispersas en archivos.
- react-icons para iconografía (elige un conjunto de iconos por proyecto, ej.
react-icons/luo/hi2, y mantén la consistencia). - Motion (motion.dev, antes Framer Motion) para animaciones; mantén los componentes animados del lado del cliente y pequeños, y respeta
prefers-reduced-motion. - Diseña con capacidad de respuesta de forma predeterminada (puntos de interrupción de Tailwind mobile-first); no envíes un diseño solo para escritorio.
5. Formularios y validación
- Define un esquema Zod por formulario/forma de datos; reutiliza el mismo esquema para la validación del cliente (a través de
@hookform/resolvers/zod) y la validación del lado del servidor dentro de la Server Action — nunca confíes en la validación solo del cliente. - React Hook Form (
useForm) maneja el estado del cliente; al enviar, llama a una Server Action, muestra los errores de campo del valor de retorno estructurado de la acción (no excepciones lanzadas que terminan en un error genérico). - Valida
process.envcon un esquema Zod al inicio (unlib/env.ts) para que la configuración faltante/incorrecta falle rápidamente en lugar de en el primer uso.
6. Base de datos (PostgreSQL + Drizzle)
- El esquema reside en
lib/db/schema.ts(o dividido por dominio), las migraciones se generan mediantedrizzle-kit generatey se aplican mediantedrizzle-kit migrate— nunca edites manualmente los archivos de migración generados. - Usa una conexión agrupada (ej. a través del pool del controlador o un proveedor como Neon/Supabase pooling) en contextos serverless/edge; no abras un nuevo cliente por solicitud sin agrupación.
- Optimización de consultas y prevención de N+1: usa consultas relacionales de Drizzle (
db.query.x.findMany({ with: {...} })) o joins explícitos en lugar de bucles y consultas por fila; selecciona solo las columnas que necesitas en lugar de recuperaciones de filas completas al estiloselect *; pagina/limita cualquier consulta que pueda devolver un conjunto de resultados ilimitado. - Indexa las columnas utilizadas en
WHERE/ORDER BY/joins; agrégalas en el esquema, no como una ocurrencia tardía. - Mantén una historia de copia de seguridad/restauración documentada para bases de datos de producción (instantáneas del proveedor administrado como mínimo) — menciona esto si la configuración del usuario no tiene ninguna.
7. Autenticación y seguridad
- Better Auth para autenticación (sesiones, proveedores OAuth, email/contraseña según sea necesario); nunca implementes manualmente hash de contraseñas o tokens de sesión.
- RBAC: modela roles/permisos explícitamente (una columna
rolemás un pequeño helper de verificación de permisos), y aplica la autorización en la Server Action / capa de acceso a datos, no solo ocultando la UI — un botón oculto no es control de acceso. - Limita la tasa de puntos finales sensibles (auth, envíos de formularios, cualquier cosa costosa) — ej. a través de Upstash Redis o un limitador de token-bucket/sliding-window equivalente.
- Establece encabezados de seguridad (a través de
next.config.jsheaders o middleware):Content-Security-Policy,X-Frame-Options,Referrer-Policy,Permissions-Policy,Strict-Transport-Security. - Cookies:
httpOnly,secure,sameSite: 'lax'(o'strict'cuando sea factible) en todas las cookies de autenticación/sesión. - Valida cada entrada externa (datos de formulario, parámetros de consulta, payloads de webhook) con Zod en el límite antes de que toque la lógica empresarial o la base de datos.
- Nunca registres secretos, tokens o cuerpos de solicitud completos que contengan credenciales.
8. SEO
- Usa la API de Metadatos (
generateMetadata/ exportación demetadataestática) por ruta — título, descripción, Open Graph, tarjeta de Twitter. - Proporciona
app/sitemap.tsyapp/robots.tsgenerados a partir de datos de ruta reales, no una lista desactualizada codificada. - Establece URLs canónicas a través de
alternates.canonicalde metadatos, especialmente en cualquier ruta accesible a través de múltiples variantes de cadena de consulta. - Añade datos estructurados JSON-LD para tipos de contenido que se beneficien (artículos, productos, migas de pan, organización) usando una etiqueta script con
type="application/ld+json".
9. Accesibilidad
- HTML semántico primero (
button,nav,main,header,label) — recurre a ARIA solo para llenar un vacío real que el HTML semántico no puede cubrir. - Todo accesible y operable solo con teclado; estados de enfoque visibles (no elimines los contornos de
:focus-visiblesin reemplazarlos). - Cada imagen tiene un
altsignificativo (alt=""vacío solo para imágenes genuinamente decorativas). - Cada entrada de formulario tiene una etiqueta
<label>real asociada (no solo un placeholder). - Verifica el contraste de color contra WCAG AA como mínimo para texto y elementos interactivos.
10. Manejo de errores y estados de carga
Cada segmento de ruta que pueda fallar o tomar tiempo debe tener:
error.tsx— límite de error a nivel de segmento con una acción de reintento.global-error.tsx— fallback de nivel superior para errores queerror.tsxno puede capturar.not-found.tsx— para 404s, incluyendo llamadasnotFound()desde búsquedas de datos.loading.tsx— fallback de Suspense; hazlo un esqueleto real que coincida con el diseño final, no un spinner vacío.
11. Testing
- Vitest para pruebas unitarias (funciones puras, validación de esquemas, lógica de utilidades) y pruebas de componentes (React Testing Library) — colocalas como
*.test.ts(x)o bajo__tests__/. - Playwright para flujos de extremo a extremo: autenticación, recorridos críticos del usuario, envíos de formularios. Ejecuta contra un despliegue de vista previa o una compilación local, no contra producción.
- Las nuevas Server Actions y esquemas de validación deben enviarse con al menos una prueba básica; no dejes que la lógica empresarial crítica no se pruebe por defecto.
12. Monitoreo y logging
- Sentry para seguimiento de errores tanto en cliente como en servidor (archivo de instrumentación +
sentry.client.config/sentry.server.config), con mapas de origen cargados en el despliegue. - Logging estructurado (logs JSON con id de solicitud, ruta, id de usuario cuando estén disponibles) en lugar de
console.logad hoc. - Rastrea las Core Web Vitals en producción (Vercel Analytics o Sentry Performance) y configura monitoreo de disponibilidad en la URL desplegada.
13. DevOps
- Git con commits convencionales y con alcance; rama
mainprotegida. - CI de GitHub Actions en cada PR: instalar → lint → typecheck → pruebas unitarias → compilar (añade e2e para cambios más grandes).
- Despliega en Vercel; cada PR obtiene un despliegue de vista previa antes de fusionar.
- Variables de entorno gestionadas por entorno en Vercel, validadas al inicio mediante el esquema Zod de entorno de la §5.
14. Multimedia
- Cloudinary para multimedia subida por el usuario/dinámica: sube a través de parámetros de subida firmados y emitidos por el servidor (nunca expongas el secreto de API del lado del cliente), y sirve a través de las URLs de transformación de Cloudinary en lugar de reimplementar el redimensionamiento.
15. Convenciones de trabajo para Claude
Cuando se te pida añadir una característica en un proyecto que use este stack:
- Verifica el esquema/acciones/componentes existentes antes de crear nuevos — extiende, no dupliques.
- Nuevos campos de BD → actualiza
schema.ts, genera una migración, no edites SQL manualmente. - Nuevo formulario → esquema Zod → formulario RHF → Server Action con revalidación del lado del servidor y una verificación de autorización.
- Nueva ruta → decide estática vs dinámica deliberadamente; añade
loading.tsx/error.tsxsi faltan; añade metadatos. - Después de una mutación, revalida la ruta/etiqueta específica afectada — no revalides toda la aplicación de forma general.
- Ejecuta lint/typecheck mentalmente contra el modo estricto antes de presentar código; señala cualquier
any, entrada no verificada o verificación de autorización faltante en lugar de enviarlo silenciosamente. - Antes de introducir, reemplazar u omitir cualquier herramienta de la sección 0, pregunta al usuario con opciones (consulta la regla "Recomendado, no estricto" en la sección 0).
16. Archivo de reglas complementarias
AGENTS.md— la regla de flujo de trabajo del proyecto (Flujo Principal, Flujo de Características, aprobación ga