Volver al blog
25 de julio de 2025

Next.js: Lo Bueno, Lo Malo y el '¿Por Qué es Tan Complicado?'

Una opinión honesta sobre Next.js después de construir proyectos reales — qué funciona, qué no, y cuándo considerar alternativas

Next.js: Lo Bueno, Lo Malo y el '¿Por Qué es Tan Complicado?'

🤔 ¿Qué es Next.js Exactamente?

¿Es un framework? ¿Un meta-framework? ¿Un wrapper de React? ¿Un framework full-stack? Sí.

Next.js es React con esteroides — toma React (que maneja UI) y añade routing, server-side rendering, rutas de API, optimización de imágenes, y un montón de cosas que de otra manera tendrías que configurar tú mismo.

Versión simple: React hace fácil construir UIs. Next.js hace fácil enviar esas UIs a producción con buen rendimiento y SEO.

✅ Lo que Next.js Realmente Resuelve

1. Routing Sin el Dolor

React solo:

npm install react-router-dom
# Configurar rutas manualmente
# Set up lazy loading
# Manejar 404s
# Lidiar con rutas anidadas
# Llorar

Next.js:

# Crea /app/about/page.tsx
# Eso es todo. Tienes una ruta /about.

El routing basado en archivos es genial. La estructura de archivos ES la estructura de rutas. Sin configuración. Sin imports. Solo funciona.

Ejemplo real en Four-Points: Tengo 50+ páginas organizadas por módulo (parking, groups, blacklist, logbooks, etc.). Sin el routing basado en archivos habría sido un infierno mantener eso con React Router.

2. Server-Side Rendering (SSR) Gratis

Problema con React SPA:

  • Google rastrea tu sitio: ve <div id="root"></div> vacío
  • Usuario carga la página: mira pantalla en blanco mientras JS descarga
  • SEO: Muerto
  • Rendimiento: Terrible

Next.js:

// Esto corre en el servidor
export default async function Page() {
  const data = await getData()  // Fetch antes de enviar HTML
  return <div>{data}</div>  // HTML ya tiene contenido
}

El usuario recibe HTML renderizado instantáneamente. Los motores de búsqueda ven contenido real. Todos ganan.

Ejemplo real en mi portfolio: Los posts del blog se pre-renderizan. Google los indexa perfectamente. El usuario ve contenido al instante. Sin SSR, estarían viendo un loader mientras React carga y luego hace fetch del contenido.

3. Optimización de Imágenes Incluida

Etiqueta img regular:

<img src="/huge-photo.jpg" />
<!-- Carga imagen de 5MB -->
<!-- Usuario en conexión lenta: 😢 -->
<!-- Puntuación Lighthouse: 💀 -->

Image de Next.js:

<Image src="/huge-photo.jpg" width={800} height={600} />
// Automáticamente:
// - Redimensiona al tamaño de pantalla
// - Sirve WebP
// - Lazy loads
// - srcset responsivo
// Puntuación Lighthouse: 🎉

Ejemplo real en Four-Points: Las fotos de incidencias de mantenimiento se cargan al instante. En la tienda online, las imágenes de productos se optimizan automáticamente. En mi portfolio, las capturas de proyectos cargan rapidísimo. Lighthouse score 95+.

4. Rutas de API Sin Backend Separado

// app/api/users/route.ts
export async function GET() {
  const users = await db.users.findMany()
  return Response.json(users)
}

// Ahora tienes endpoint /api/users
// Sin setup de Express
// Sin servidor separado
// Solo funciona

Perfecto para proyectos pequeños. No necesitas backend separado hasta que escalas de verdad.

Ejemplo real en la app del tiempo: La ruta /api/weather hace fetch a la API externa y cachea los resultados. No necesité montar Express solo para eso.

5. Code Splitting Automático

React carga todo el JavaScript de golpe. Next.js solo carga lo que la página necesita. Ganancia enorme de rendimiento.

Ejemplo real en la tienda online: El código del checkout solo se carga en la página de checkout. La página de productos no carga ese JavaScript innecesario. Bundle size reducido en 40%.

🎯 Las 4 Estrategias de Renderizado (Lo Que REALMENTE Importa)

Aquí es donde la cosa se pone interesante. Next.js te da 4 formas de renderizar páginas, y elegir mal te jode el proyecto. Déjame explicarte cada una con ejemplos reales.

1. SSG (Static Site Generation) - Genera Una Vez, Sirve Para Siempre

Qué es: La página se genera en build time y se guarda como HTML estático.

Cuándo funciona de puta madre:

  • Mi portfolio: Todos los posts del blog, página about, proyectos. Se generan una vez, nunca cambian hasta el siguiente deploy. Rápido como un rayo.
  • Landing pages: Si no cambia, ¿para qué regenerar?
  • Documentación: Contenido estático que se actualiza con deploys.
// app/blog/[slug]/page.tsx
export async function generateStaticParams() {
  const posts = await getPosts()
  return posts.map(post => ({ slug: post.slug }))
}

export default async function BlogPost({ params }) {
  const post = await getPost(params.slug)
  return <Article post={post} />
}

// Se genera en build:
// /blog/post-1.html
// /blog/post-2.html
// etc.

Problema real que encontré: Si tu contenido cambia y no haces redeploy, los usuarios ven contenido viejo. En Four-Points, intenté usar SSG para la lista de grupos al principio. Error. Los grupos se crean/editan constantemente. Terminé con datos desactualizados hasta el siguiente deploy.

2. ISR (Incremental Static Regeneration) - Lo Mejor de SSG + SSR

Qué es: Página estática que se regenera automáticamente cada X segundos en background.

Cuándo es oro puro:

  • Datos que cambian poco pero necesitan actualizarse
  • No quieres hacer redeploy solo para actualizar contenido
  • Quieres velocidad de estático pero frescura de dinámico
// app/dashboard/blacklist/[id]/page.tsx
export const revalidate = 30  // Regenera cada 30 segundos

export default async function BlacklistDetailPage({ params }) {
  const entry = await getBlacklistById(params.id)
  return <BlacklistDetail entry={entry} />
}

Ejemplo real en Four-Points: Las páginas de detalle de la blacklist usan ISR con 30 segundos. La blacklist no cambia cada segundo, pero sí necesita actualizarse. Con ISR:

  • Primera carga: HTML estático (rápido)
  • Cada 30s: Se regenera en background
  • Usuario siempre ve contenido relativamente fresco
  • Sin golpear la DB en cada request

Problema que NO te cuentan: El primer usuario después de los 30 segundos sigue viendo el contenido viejo mientras se regenera en background. El SEGUNDO usuario ve el contenido nuevo. Esto puede ser confuso si esperas updates instantáneos.

3. SSR (Server-Side Rendering) - Fresh en Cada Request

Qué es: El servidor genera HTML nuevo en cada request.

Cuándo lo necesitas de verdad:

  • Dashboards con datos de usuario
  • Páginas personalizadas
  • Datos que cambian constantemente
  • SEO + frescura crítica
// app/dashboard/bo/page.tsx
export const dynamic = 'force-dynamic'  // Fuerza SSR

export default async function BackOfficePage() {
  // Esto corre EN CADA REQUEST
  const [stats, invoices, suppliers] = await Promise.all([
    getStats(),
    getInvoices(),
    getSuppliers()
  ])

  return <BackOfficeClient
    initialStats={stats}
    initialInvoices={invoices}
    initialSuppliers={suppliers}
  />
}

Ejemplos reales donde uso SSR:

  • Dashboard de Back Office: Stats, facturas pendientes, proveedores. Datos específicos de usuario que cambian todo el tiempo.
  • Dashboard de Parking: Ocupación en tiempo real, reservas activas.
  • Listado de Grupos: Se crean/editan constantemente.

El patrón que REALMENTE uso (SSR + Client + React Query):

// Server Component hace fetch inicial
export default async function GroupsPage() {
  let initialGroups = undefined

  try {
    const response = await getGroups()
    initialGroups = response.data
  } catch (error) {
    console.error('Error fetching groups:', error)
  }

  return <GroupsListClient initialGroups={initialGroups} />
}

// Client Component usa React Query
'use client'

export function GroupsListClient({ initialGroups }) {
  const { data: groups = [] } = useQuery({
    queryKey: ['groups', filters],
    queryFn: () => groupsApi.getAll(filters),
    initialData: initialGroups,  // 🔥 Usa datos del servidor
    staleTime: 5 * 60 * 1000,
  })

  return <GroupsList groups={groups} />
}

Por qué este patrón es oro:

  1. Usuario recibe HTML con datos (rápido, SEO-friendly)
  2. React Query toma control después (cache, refetch, mutaciones)
  3. No hay flash de "Cargando..." en primera carga
  4. Updates posteriores son instantáneos (React Query)

Problema real: El servidor tiene que esperar todos los fetches antes de enviar HTML. Si uno de tus endpoints es lento, TODO se retrasa. Tuve que optimizar queries SQL que tardaban 2+ segundos. Con SSG/ISR no importa tanto (sucede en background), con SSR jodes la UX.

4. CSR (Client-Side Rendering) - Todo en el Navegador

Qué es: HTML vacío + JavaScript. El navegador hace todo el trabajo.

Cuándo es la única opción:

  • Interactividad pesada
  • No necesitas SEO
  • Datos dependen 100% de acciones de usuario
'use client'

export default function DashboardHome() {
  const [entries, setEntries] = useState([])
  const [loading, setLoading] = useState(true)

  useEffect(() => {
    async function load() {
      const data = await getLogbooks()
      setEntries(data)
      setLoading(false)
    }
    load()
  }, [])

  if (loading) return <Skeleton />
  return <LogbooksList entries={entries} />
}

Ejemplos reales donde CSR tiene sentido:

  • Dashboard Home de Four-Points: Actividad reciente, logbooks del periodo. No necesita SEO, es área privada.
  • Páginas de admin internas: Herramientas donde SEO no importa.
  • App del tiempo: Datos basados en geolocalización del usuario.

Problema que nadie menciona: Ese if (loading) es un loading real que el usuario VE. Con SSR, no hay loading en primera carga. La diferencia en UX es brutal. Usa CSR solo cuando de verdad no puedas usar el patrón híbrido.

Cuadro Comparativo Real

SSG (Static Site Generation)

  • Velocidad: Instantáneo
  • Frescura: Stale hasta deploy
  • SEO: Sí
  • Dificultad: Fácil
  • Uso: Portfolio (posts, about)

ISR (Incremental Static Regeneration)

  • Velocidad: Instantáneo
  • Frescura: Fresh cada X segundos
  • SEO: Sí
  • Dificultad: Fácil
  • Uso: Four-Points (blacklist detail)

SSR (Server-Side Rendering)

  • Velocidad: Rápido
  • Frescura: Siempre fresh
  • SEO: Sí
  • Dificultad: Media
  • Uso: Four-Points (dashboards, grupos, parking)

CSR (Client-Side Rendering)

  • Velocidad: Lento (loading...)
  • Frescura: Siempre fresh
  • SEO: No
  • Dificultad: Fácil
  • Uso: Four-Points (home, admin tools)

La Verdad: Usa El Patrón Híbrido

En proyectos reales, casi siempre termino usando SSR + Client Components + React Query:

  1. Server Component hace fetch inicial (SSR)
  2. Pasa datos como initialData a Client Component
  3. Client Component usa React Query con ese initialData
  4. Usuario ve contenido instantáneamente (no hay loading)
  5. React Query maneja updates, cache, refetch
// ✅ PATRÓN GANADOR
// Server Component
export default async function Page() {
  const data = await fetchData()  // SSR
  return <ClientComponent initialData={data} />
}

// Client Component
'use client'
export function ClientComponent({ initialData }) {
  const { data } = useQuery({
    queryKey: ['data'],
    queryFn: fetchData,
    initialData,  // 🔥 Sin loading inicial
    staleTime: 5 * 60 * 1000,
  })

  // Mutaciones, refetch, interactividad...
}

Esto es lo que uso en el 80% de Four-Points. Combina lo mejor de SSR (velocidad, SEO) con lo mejor de CSR (interactividad, React Query).

💀 Donde Next.js Duele (Versión Honesta con Experiencia Real)

Seré honesto — Next.js no es perfecto. A veces es frustrante. Esto es lo que sucks:

1. El Caos de Versiones

Next.js 12 → Diferente de Next.js 13 → COMPLETAMENTE diferente de Next.js 14.

Pages Router vs App Router es confuso:

  • Tutoriales viejos usan Pages Router
  • Docs nuevos empujan App Router
  • Funcionan diferente
  • No puedes mezclarlos limpiamente
  • Respuestas de Stack Overflow están desactualizadas

Tuve que reaprender routing dos veces. La documentación de versiones antiguas todavía rankea alto en Google. Pierdes horas debuggeando hasta que te das cuenta de que la solución es para Next.js 12 y tú estás en 14.

Historia real: Pasé 3 horas debuggeando por qué getServerSideProps no funcionaba en App Router. Spoiler: No existe en App Router. Usas async Server Components. Los tutoriales no lo dejan claro.

2. Los Componentes de Servidor Son Confusos

// Este es un Componente de Servidor (default en App Router)
async function Page() {
  const data = await getData()  // Funciona

  const [state, setState] = useState()  // ❌ ERROR
  useEffect(() => {})  // ❌ ERROR
  onClick={() => {}}  // ❌ ERROR
}

// ¿Quieres features de cliente? Añade esto:
'use client'

// Ahora funciona pero pierde beneficios de servidor
// ¿Qué componentes deberían ser de servidor?
// ¿Cuáles de cliente?
// Nadie sabe. Adivina y reza.

El mental model es difícil. Tienes que pensar constantemente "¿esto corre en servidor o cliente?" Los mezclas mal y aparecen bugs raros.

Bug real que tuve: Un componente de servidor importaba un componente de cliente que importaba otro componente de servidor. Next.js no sabía qué hacer. Error críptico de "You're importing a Server Component into a Client Component". Tardé 2 horas en encontrar la cadena de imports.

3. Hydration Errors (El Infierno)

Error: Hydration failed because the initial UI does not match what was rendered on the server.

Este error es mi némesis. Aparece cuando:

  • Renderizas algo diferente en servidor vs cliente
  • Usas Date.now() o cualquier cosa no determinista
  • Tienes un espacio en blanco extra en algún lado
  • La Luna está en cuarto creciente

Casos reales donde me pasó:

  1. Timestamp formateado diferente en servidor (UTC) vs cliente (local time)
  2. Componente que checkeaba window.innerWidth para mostrar/ocultar elementos
  3. ID generado con Math.random() en servidor vs cliente

Solución: Usa useEffect para cualquier cosa que dependa del navegador. O llora un rato primero.

4. El Cache Agresivo (WTF is Happening?)

Next.js cachea TODO. A veces demasiado:

// ¿Por qué mis datos no se actualizan?
const data = await getData()

// Ah, está cacheado
const data = await getData({ cache: 'no-store' })

// ¿O debería usar revalidate?
const data = await getData({ next: { revalidate: 60 } })

// ¿O es problema del Router Cache?
// ¿O del Full Route Cache?
// ¿O del Data Cache?
// HAY 4 TIPOS DE CACHE DIFERENTES

Historia de terror real: En Four-Points, creaba un grupo, volvía a la lista, y no aparecía el grupo nuevo. Estaba cacheado. Tuve que:

  1. Agregar cache: 'no-store' al fetch
  2. Usar revalidatePath() después de mutaciones
  3. Configurar staleTime en React Query
  4. Sacrificar un pollo a los dioses del cache

El cache es genial para rendimiento, terrible para saber QUÉ COÑO ESTÁ PASANDO.

5. Over-Engineering para Proyectos Simples

¿Construyendo una landing page simple? Next.js puede ser overkill:

  • Se requiere servidor Node.js (no puedes solo abrir index.html)
  • Necesita paso de build
  • Variables de entorno
  • Configuración
  • Complejidad de despliegue

Para un sitio estático de 3 páginas, esto es absurdo.

Experiencia real: La app del tiempo es básicamente una página. Podría haber sido HTML + vanilla JS. Pero usé Next.js porque quería API route para cachear llamadas a la API del clima. ¿Valió la pena? Probablemente no.

6. El Problema del Tamaño del Bundle

Next.js incluye React, que son 40KB+ gzipped. Para proyectos pequeños, eso es overhead enorme.

App del tiempo en vanilla JS: 5KB
Misma app en Next.js: 85KB+

¿Vale el DX 80KB extra? A veces sí. A veces no.

En mi portfolio: 90KB gzipped. ¿Lo necesito? Probablemente no. ¿Me importa? Tampoco. Carga en 0.5s de todas formas.

7. Lock-in de Despliegue (Sort Of)

Next.js funciona mejor en Vercel (la empresa que hace Next.js). ¡Sorpresa!

Desplegar en Vercel: Un click. Perfecto.

Desplegar en cualquier otro lugar:

  • Necesitas servidor Node.js
  • Configurar build correctamente
  • Manejar variables de entorno
  • Set up caching
  • Lidiar con adaptadores serverless

No es imposible, pero Vercel lo hace tan fácil que te sientes atrapado.

Experiencia real: Four-Points está en Vercel (frontend) + Render (backend). El frontend fue deploy de un click. El backend en Render requirió configuración manual, variables de entorno, SSL, etc. La diferencia es brutal.

8. Mensajes de Error del Infierno

Error: useContext is not a function
Error: Hydration failed
Error: Expected server HTML to contain matching <div>
Error: Cannot read property 'useState' of null

Estos errores no te dicen NADA útil. ¿Los googleas? 47 posibles causas diferentes. Suerte debuggeando.

Mi favorito: Error: invariant expected app router to be mounted. ¿Qué significa? Nadie lo sabe. ¿Cómo lo arreglé? Borré .next, node_modules, y recé.

🎾 Ganancias en el Mundo Real en Mis Proyectos

Four-Points (Sistema de Gestión Hotelera)

Lo que funciona de puta madre:

  • SSR + React Query para dashboards: Datos frescos, primera carga rápida, UX fluida
  • ISR para blacklist: Velocidad de estático, frescura sin golpear la DB cada segundo
  • Rutas de API: Endpoints simples para CRUD sin necesitar Express
  • File routing: 50+ páginas organizadas limpiamente
  • Image optimization: Fotos de incidencias de mantenimiento cargan al instante

Lo que dolió:

  • Cache agresivo en mutaciones (crear grupo → volver → no aparece)
  • Hydration errors con timestamps
  • Server vs Client Components confuso al principio
  • Build time alto (1-2 minutos con 50+ páginas)

Portfolio (stackbp.es)

Lo que funciona perfecto:

  • SSG para posts: Generación estática, velocidad insana (0.4s load time)
  • MDX: Blog posts con componentes React incrustados
  • Image optimization: Capturas de proyectos optimizadas automáticamente
  • SEO: Google indexa todo perfectamente, meta tags fáciles

Sin problemas: Proyecto simple, no necesita nada fancy. SSG + MDX es todo lo que necesito.

Tienda Online (E-commerce)

Lo que funciona bien:

  • ISR para productos: Páginas rápidas, se actualizan cada minuto
  • SSG para categorías: Estáticas, rápidas
  • Dynamic imports: Checkout solo carga en página de checkout (-40% bundle)
  • Image optimization: Productos con muchas fotos cargan instantáneamente

Problema: Al principio usé SSG para productos. Cuando actualizaba precio, no se veía hasta redeploy. Cambié a ISR con revalidate: 60 y problema resuelto.

App del Tiempo

Lo que funciona:

  • SSG para shell: Página estática, carga instantánea
  • API route: /api/weather cachea llamadas a API externa (ahorra requests)
  • CSR para datos: Basados en geolocalización, tiene sentido

¿Valió la pena Next.js?: Honestamente, no. Podría haber sido HTML + fetch. Pero ya tenía Next.js configurado, así que meh.

🔄 Cuándo Usar Next.js (y Cuándo No)

✅ Usa Next.js Cuando:

1. SEO Importa

  • Sitios de marketing
  • E-commerce (✅ mi tienda online)
  • Blogs (✅ mi portfolio)
  • Landing pages de SaaS

2. Necesitas SSR o ISR

  • Dashboard con datos de usuario (✅ Four-Points)
  • Contenido dinámico que debe cargar rápido
  • Páginas personalizadas

3. Construyendo App Full-Stack Pequeña/Mediana

  • Rutas de API para backend (✅ app del tiempo, Four-Points)
  • Integración de base de datos
  • Flujos de autenticación (✅ Four-Points)

4. Sitio con Muchas Imágenes

  • Portfolio (✅ el mío)
  • E-commerce con productos (✅ mi tienda)
  • Galerías de fotos

5. Quieres Buen DX y Velocidad de Desarrollo

  • Soporte TypeScript es excelente
  • Routing basado en archivos es intuitivo (✅ salvó mi vida en Four-Points)
  • Hot reload es rápido
  • Herramientas de desarrollo son sólidas

❌ No Uses Next.js Cuando:

1. Sitio Estático Simple

  • Blog personal con 5 posts
  • Landing page de empresa simple
  • Sitio de documentación simple

→ Usa Astro en cambio: Más rápido, más simple, mejor para contenido estático puro

2. SPA Altamente Interactiva Sin SEO

  • Dashboard admin interno sin necesidades SEO
  • Herramientas internas
  • Apps con gestión de estado compleja

→ Usa Vite + React: Más simple, dev server más rápido, sin overhead de SSR

Mi experiencia: Four-Points NECESITA SEO? No, es app interna. Pero SSR para primera carga rápida SÍ vale la pena. Si fuera solo CSR, cada dashboard tendría loading de 2-3 segundos.

3. App Móvil

  • React Native existe
  • Next.js en móvil es raro

→ Usa React Native o Expo

4. Mucho Tiempo Real / WebSockets Heavy

  • Aplicaciones de chat
  • Herramientas colaborativas
  • Apps heavy en WebSockets

→ Usa React raw + Socket.io, o considera otros frameworks

Next.js puede manejar WebSockets pero no es su fuerte.

5. Tamaño de Bundle Crítico

  • Widgets embebidos
  • Scripts de terceros
  • Máximo rendimiento necesario
  • App que debe funcionar offline

→ Usa Preact, Svelte, o vanilla JS

6. Proyecto de Aprendizaje / Primer Proyecto

No empieces con Next.js. Aprende React primero. Next.js añade complejidad (Server Components, cache, ISR, etc.) que te confundirá.

Aprende en orden:

  1. JavaScript puro
  2. React
  3. React + Vite (para entender bundling)
  4. LUEGO Next.js

🆚 Alternativas y Cuándo Son Mejores

Astro

Cuándo: Enfoque en contenido estático, blog, docs

Por qué: Más rápido, shippea zero JS por defecto, más fácil de aprender

Contras: Menos capacidades dinámicas

Mi take: Si mi portfolio fuera SOLO blog, usaría Astro. Pero tengo componentes React interactivos, así que Next.js vale la pena.

Remix

Cuándo: Fetching de datos complejo, rutas anidadas, forms pesados

Por qué: Mejor manejo de forms, mental model más simple que App Router de Next.js

Contras: Ecosistema más pequeño, menos adopción

No lo he usado, pero suena prometedor para apps form-heavy.

Vite + React

Cuándo: SPA sin necesidades SSR

Por qué: Más simple, dev server más rápido, sin complejidad de servidor

Contras: No tiene SSR ni routing built-in

Mi experiencia: Usé Vite para un proyecto interno. Dev server VUELA. Si no necesitas SSR, es mejor opción que Next.js.

SvelteKit

Cuándo: Quieres bundles más pequeños, sintaxis más limpia

Por qué: Menos JavaScript shipped, reactividad más simple

Contras: Comunidad más pequeña que React

Nunca lo he usado, pero la gente jura que es mejor que Next.js. Algún día lo probaré.

Nuxt (Vue)

Cuándo: Prefieres Vue sobre React

Por qué: Similar a Next.js pero para ecosistema Vue

Contras: Ecosistema Vue más pequeño que React

No uso Vue, pero si te gusta, Nuxt es el "Next.js de Vue".

💡 Lecciones de Construir 4 Proyectos Reales con Next.js

1. Empieza Simple, Añade Complejidad Cuando se Necesita

No uses Server Components en todas partes porque son "lo nuevo". No uses ISR porque suena cool.

Mi regla:

  • Página no cambia casi nunca? → SSG
  • Cambia poco? → ISR
  • Cambia mucho y necesitas SEO? → SSR + React Query
  • No necesitas SEO? → CSR

En Four-Points, empecé haciendo TODO con SSR. Build time era 5 minutos. Cambié a SSG para páginas contenedoras y build bajó a 1 minuto.

2. El Patrón SSR + Client + React Query es Oro

Después de 50+ páginas en Four-Points, este es el patrón que siempre funciona:

// Server Component (page.tsx)
export default async function Page() {
  const data = await fetchData()
  return <ClientComponent initialData={data} />
}

// Client Component
'use client'
export function ClientComponent({ initialData }) {
  const { data } = useQuery({
    queryKey: ['data'],
    queryFn: fetchData,
    initialData,  // 🔥 Key del patrón
    staleTime: 5 * 60 * 1000,
  })
  // ...resto del componente con toda la interactividad
}

Gets:

  • ✅ Primera carga rápida (SSR)
  • ✅ SEO (contenido en HTML)
  • ✅ Sin flash de loading
  • ✅ Cache inteligente (React Query)
  • ✅ Refetch automático
  • ✅ Mutaciones optimistas

3. revalidatePath() es Tu Amigo en Mutaciones

Después de crear/editar/eliminar, SIEMPRE revalida:

'use server'
export async function createGroup(data: GroupData) {
  await db.group.create({ data })
  revalidatePath('/dashboard/groups')  // 🔥 CRUCIAL
  return { success: true }
}

Sin esto, el cache te jode. El usuario crea algo y no lo ve. Aprende de mi dolor.

4. Entiende los Trade-offs

SSR es genial para SEO, malo para complejidad. Generación estática es rápida, mala para datos dinámicos. ISR es el término medio.

Tabla de decisión que uso:

¿Los datos cambian?
├─ No → SSG
├─ Cada pocos minutos/horas → ISR
├─ Constantemente + necesitas SEO → SSR
└─ Constantemente + no necesitas SEO → CSR

5. Los Docs de Next.js Son Buenos (Pero Confusos al Principio)

Los docs son completos pero asumen que entiendes el modelo mental. No es obvio al principio.

Mi consejo: Lee los docs de App Router de inicio a fin UNA VEZ antes de empezar. No vayas aprendiendo mientras construyes. Terminarás con un Frankenstein de Pages Router + App Router.

6. El Despliegue de Vercel es una Droga

Una vez que experimentas despliegue de un click, no puedes volver. Git push → Live en 30 segundos.

Pero: Si tu backend está en otro lado (Four-Points backend en Render), terminas con complejidad de todas formas. CORS, variables de entorno en dos lugares, etc.

7. No Pelees Contra el Framework

Si Next.js hace algo difícil, quizás estás usando la herramienta equivocada.

Ejemplos:

  • ¿Quieres WebSockets persistentes? Next.js no es ideal.
  • ¿Todo tu sitio es estático? Astro es mejor.
  • ¿App interna sin SEO? Vite + React es más simple.

No lo forces. Elige la herramienta correcta.

8. TypeScript + Next.js es el Combo Perfecto

El soporte de TypeScript en Next.js es excelente. params, searchParams, y todo está tipado.

type PageProps = {
  params: Promise<{ id: string }>
  searchParams: Promise<{ tab?: string }>
}

export default async function Page({ params, searchParams }: PageProps) {
  const { id } = await params
  const { tab } = await searchParams
  // Todo tipado, VSCode autocompleta
}

Sin TypeScript en Next.js, te perderías. Con él, es developer experience top tier.

📊 Mi Opinión Personal Después de 4 Proyectos

Four-Points (50+ páginas, producción)

Veredicto: ✅ Next.js fue la elección correcta

Por qué:

  • SSR para dashboards con datos de usuario
  • ISR para blacklist (velocidad + frescura)
  • Routing basado en archivos salvó mi vida
  • Image optimization para fotos de mantenimiento
  • Rutas de API para endpoints simples

Pero: Build time alto, cache confuso, hydration errors me hicieron llorar.

Lo haría de nuevo?: Sí, sin duda.

Portfolio (stackbp.es)

Veredicto: ✅ Next.js es perfecto para esto

Por qué:

  • SSG para posts → Velocidad insana
  • MDX → Blog con componentes React
  • Image optimization → Fotos profesionales
  • Deploy de un click → Actualizar blog es trivial

Contras: Ninguno. Caso de uso ideal para Next.js.

Lo haría de nuevo?: 100%. Podría usar Astro, pero Next.js funciona perfecto.

Tienda Online

Veredicto: ✅ Buena elección

Por qué:

  • ISR para productos → Rápido + actualizado
  • SEO crítico → SSR/ISR ayudan
  • Image optimization → Productos con fotos

Contras: Al principio usé SSG mal, tuve que refactorear.

Lo haría de nuevo?: Sí, pero empezaría con ISR desde día 1.

App del Tiempo

Veredicto: 🤷 Overkill, pero funciona

Por qué funcionó: API route para cachear llamadas externas.

Por qué es overkill: Podría ser HTML + vanilla JS.

Lo haría de nuevo?: Probablemente no. Usaría HTML + Tailwind + fetch.

🎯 La Verdad Honesta

Next.js es increíble cuando necesitas lo que ofrece:

  • ✅ SEO
  • ✅ SSR/ISR
  • ✅ Optimización de imágenes
  • ✅ Routing basado en archivos
  • ✅ Capacidad full-stack (pequeña escala)

Pero también es:

  • ❌ Complejo (Server vs Client Components)
  • ❌ A veces over-engineered (para proyectos simples)
  • ❌ Caos de versiones (Pages vs App Router)
  • ❌ Cache confuso (4 tipos de cache, WTF)
  • ❌ Optimizado para Vercel (lock-in suave)
  • ❌ Curva de aprendizaje empinada

Úsalo cuando resuelva tus problemas. No lo uses porque está de moda.

🚀 Veredicto Final (Después de 4 Proyectos Reales)

Next.js es como una navaja suiza:

  • Tiene herramientas para todo
  • Complicado cuando necesitas algo simple
  • Perfecto cuando necesitas las herramientas específicas que ofrece

Mi proceso de selección de framework (actualizado con experiencia):

1. ¿Es contenido 100% estático? → Astro
2. ¿Blog con componentes React? → Next.js (SSG)
3. ¿E-commerce? → Next.js (ISR para productos)
4. ¿Dashboard con datos de usuario? → Next.js (SSR + React Query)
5. ¿SPA interna sin SEO? → Vite + React
6. ¿App del tiempo / widget? → Vanilla JS (o Next.js si ya lo tienes configurado)
7. ¿Proyecto grande con 50+ páginas? → Next.js (routing basado en archivos es oro)
8. ¿No estás seguro? → Next.js es una apuesta segura

La regla de oro: El mejor framework es el con el que realmente envías.

Un proyecto terminado en Next.js gana a un proyecto perfecto en Astro que nunca se lanza.

No uses porque este de moda. Elige basado en necesidades del proyecto. Pero tampoco le des muchas vueltas — Next.js es suficientemente bueno para el 80% de proyectos.

Mi recomendación real:

  • Primer proyecto? Empieza con Vite + React. Aprende React primero.
  • Segundo proyecto con SEO? Next.js. Empieza simple (SSG).
  • Proyecto serio en producción? Next.js. Usa patrón SSR + React Query.
  • ¿Ya tienes Next.js configurado? Úsalo. No vale la pena cambiar.

Sigue enviando. Elige sabiamente. Pero sobre todo, ENVÍA. 🚀


P.D.: Después de construir Four-Points con 50+ páginas, sistema de autenticación, roles, módulos de parking, grupos, blacklist, logbooks, cashier, y más... Next.js aguantó todo. ¿Fue perfecto? No. ¿Funcionó? Sí. ¿Lo recomiendo? Para proyectos serios, absolutamente.

P.P.D.: El cache de Next.js me hizo llorar más de una vez. Pero revalidatePath() es tu amigo. Úsalo.

P.P.P.D.: Si estás leyendo esto en 2026 y Next.js 15/16 cambió todo de nuevo... lo siento. Bienvenido al caos de versiones. Es parte de la experiencia. 😅

Volver al blog
Next.js: Lo Bueno, Lo Malo y el '¿Por Qué es Tan Complicado?' | bpstack