Introducción a Next.js: qué es y por qué los devs lo prefieren sobre React

D
DanisCh
(Actualizado: ) • 12 min de lectura
Introducción a Next.js: qué es y por qué los devs lo prefieren sobre React
JavaScript Programación

Si llevas un tiempo en el mundo del desarrollo frontend, seguro has escuchado hablar de Next.js. En los últimos años se ha convertido en el framework más popular para construir aplicaciones web con React, y en muchos equipos ha reemplazado al uso de React puro como punto de partida de cualquier proyecto nuevo.

Pero ¿qué tiene Next.js que no tiene React solo? ¿Por qué tantos desarrolladores lo prefieren? En esta guía lo vas a entender desde cero, con ejemplos concretos y sin rodeos.

¿Qué es Next.js?

Next.js es un framework de React creado por Vercel. Esto significa que está construido sobre React: todo lo que sabes de React (componentes, hooks, props, estado) funciona exactamente igual en Next.js. Lo que Next.js añade es una capa de infraestructura que resuelve los problemas que React por sí solo no resuelve.

React es una librería de interfaz de usuario. Te da las herramientas para construir componentes y gestionar el estado, pero no te dice nada sobre cómo organizar las rutas, cómo hacer el renderizado en el servidor, cómo optimizar las imágenes o cómo estructurar un proyecto de mediana o gran escala. Next.js toma todas esas decisiones por ti, de forma opinada pero flexible.

Una analogía útil: React es el motor de un coche. Next.js es el coche completo, con la carrocería, el sistema de navegación, los frenos y el maletero. Puedes construir un coche a partir de un motor, pero la mayoría de las veces tiene más sentido empezar con uno ya ensamblado.

El problema principal de React puro: el renderizado en el cliente

Para entender por qué existe Next.js, hay que entender el problema que resuelve. Cuando creas una aplicación con React puro (usando Create React App o Vite, por ejemplo), obtienes una SPA (Single Page Application): una sola página HTML casi vacía que descarga un archivo JavaScript enorme, lo ejecuta en el navegador y construye toda la interfaz desde cero en el cliente.

Este modelo tiene tres problemas serios:

Problema 1: SEO deficiente. Los motores de búsqueda reciben una página HTML vacía. Aunque Google ha mejorado su capacidad para ejecutar JavaScript, el rastreo de SPAs sigue siendo menos fiable y más lento que el de páginas con HTML ya renderizado. Para blogs, tiendas, portafolios o cualquier contenido que necesite posicionarse en buscadores, una SPA pura es una desventaja importante.

Problema 2: Tiempo hasta el primer contenido visible (FCP) lento. El usuario ve una pantalla en blanco mientras el navegador descarga el JavaScript, lo analiza, lo ejecuta y finalmente renderiza la interfaz. En conexiones lentas o dispositivos de gama baja, esto puede tardar varios segundos.

Problema 3: Sin sistema de rutas nativo. React no incluye un router. Tienes que instalar React Router, configurarlo manualmente y mantenerlo. No es difícil, pero es una decisión extra y una dependencia más que gestionar.

Next.js resuelve los tres problemas de forma integrada.

Las ventajas clave de Next.js

Sistema de rutas basado en archivos

En Next.js no configuras rutas en ningún archivo de JavaScript. Las rutas se definen por la estructura de carpetas de tu proyecto. Cada archivo dentro de la carpeta app/ (o pages/ en versiones anteriores) se convierte automáticamente en una ruta.

app/
├── page.tsx              →  /
├── about/
│   └── page.tsx          →  /about
├── blog/
│   ├── page.tsx          →  /blog
│   └── [slug]/
│       └── page.tsx      →  /blog/cualquier-slug
└── dashboard/
    ├── layout.tsx        →  layout compartido para /dashboard/*
    ├── page.tsx          →  /dashboard
    └── settings/
        └── page.tsx      →  /dashboard/settings

Los corchetes en el nombre de una carpeta ([slug]) indican un segmento dinámico: esa parte de la URL puede ser cualquier valor, y Next.js lo pasa al componente como parámetro. No necesitas instalar nada, no necesitas configurar nada. La estructura de carpetas es la configuración.

Múltiples estrategias de renderizado

Esta es la ventaja más poderosa de Next.js. Puedes elegir, página a página, cómo se renderiza cada parte de tu aplicación:

Server-Side Rendering (SSR): el HTML se genera en el servidor en cada petición. Ideal para páginas con datos que cambian frecuentemente y que necesitan SEO, como un feed de noticias o los resultados de una búsqueda.

Static Site Generation (SSG): el HTML se genera una vez en tiempo de construcción (build time) y se sirve como un archivo estático. Extremadamente rápido. Ideal para páginas que no cambian frecuentemente, como un blog o una página de marketing.

Incremental Static Regeneration (ISR): una mezcla de los dos anteriores. Genera la página estáticamente pero la regenera en segundo plano cada cierto tiempo. Ideal para contenido que cambia, pero no en cada petición, como una tienda online con miles de productos.

Client-Side Rendering (CSR): el comportamiento tradicional de React. Puedes seguir usándolo cuando lo necesites, por ejemplo para dashboards privados donde el SEO no importa.

En React puro solo tienes CSR. En Next.js eliges la estrategia más adecuada para cada página.

Server Components y Client Components

Desde Next.js 13 con el App Router, existe una distinción fundamental entre Server Components y Client Components.

Los Server Components se ejecutan exclusivamente en el servidor. Pueden acceder directamente a bases de datos, sistemas de archivos o APIs privadas sin exponer nada al cliente. No añaden nada al bundle de JavaScript que descarga el navegador. Son el tipo de componente por defecto en el App Router.

Los Client Components se ejecutan en el navegador e incluyen interactividad: eventos, hooks de estado, efectos. Para declararlos, añades 'use client' al inicio del archivo.

// app/productos/page.tsx
// Server Component por defecto: accede a la base de datos directamente

async function obtenerProductos() {
  // Esto se ejecuta en el servidor, nunca llega al navegador
  const res = await fetch("https://api.tienda.com/productos", {
    next: { revalidate: 3600 }  // ISR: regenerar cada hora
  });
  return res.json();
}

export default async function PaginaProductos() {
  const productos = await obtenerProductos();

  return (
    <main>
      <h1>Nuestros productos</h1>
      <ul>
        {productos.map(p => (
          <li key={p.id}>{p.nombre} — ${p.precio}</li>
        ))}
      </ul>
    </main>
  );
}
// components/BotonCarrito.tsx
// Client Component: necesita interactividad
'use client';

import { useState } from 'react';

export default function BotonCarrito({ productoId }: { productoId: number }) {
  const [agregado, setAgregado] = useState(false);

  return (
    <button onClick={() => setAgregado(true)}>
      {agregado ? '✓ En el carrito' : 'Agregar al carrito'}
    </button>
  );
}

El patrón recomendado es mantener la mayor cantidad posible de componentes como Server Components (para reducir el JavaScript en el cliente) y usar Client Components solo donde hay interactividad real.

Optimización de imágenes automática

Next.js incluye un componente <Image> que optimiza las imágenes automáticamente: las convierte a formatos modernos como WebP o AVIF, las redimensiona según el dispositivo, aplica lazy loading por defecto y evita el layout shift (el salto visual cuando carga una imagen). Todo sin configuración adicional.

import Image from 'next/image';

export default function Avatar() {
  return (
    <Image
      src="/fotos/perfil.jpg"
      alt="Foto de perfil de Ana"
      width={200}
      height={200}
      priority   // carga esta imagen primero (para imágenes above the fold)
    />
  );
}

 

Con una etiqueta <img> normal tendrías que hacer todo esto manualmente o con librerías adicionales.

Optimización de fuentes

El módulo next/font descarga las fuentes de Google Fonts en tiempo de construcción y las sirve desde tu propio dominio, eliminando las peticiones externas a Google y evitando el parpadeo de texto mientras la fuente carga.

// app/layout.tsx
import { Inter } from 'next/font/google';

const inter = Inter({ subsets: ['latin'] });

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="es" className={inter.className}>
      <body>{children}</body>
    </html>
  );
}

 

Metadatos y SEO integrados

Next.js tiene una API nativa para gestionar los metadatos de cada página: título, descripción, Open Graph, Twitter Cards y más. Puedes exportar un objeto metadata estático o una función generateMetadata dinámica desde cualquier página.

// app/blog/[slug]/page.tsx
import { Metadata } from 'next';

// Metadatos dinámicos basados en el contenido de la página
export async function generateMetadata({ params }: { params: { slug: string } }): Promise<Metadata> {
  const articulo = await obtenerArticulo(params.slug);

  return {
    title:       articulo.titulo,
    description: articulo.resumen,
    openGraph: {
      title:  articulo.titulo,
      images: [articulo.imagenPortada],
    },
  };
}

export default async function PaginaArticulo({ params }: { params: { slug: string } }) {
  const articulo = await obtenerArticulo(params.slug);
  return <article>{/* contenido */}</article>;
}

 

API Routes: backend incluido

Next.js te permite crear endpoints de API dentro del mismo proyecto, en la carpeta app/api/. Esto es especialmente útil para proyectos pequeños y medianos donde no quieres mantener un servidor separado.

// app/api/contacto/route.ts
import { NextRequest, NextResponse } from 'next/server';

export async function POST(request: NextRequest) {
  const datos = await request.json();
  const { nombre, email, mensaje } = datos;

  if (!nombre || !email || !mensaje) {
    return NextResponse.json(
      { error: 'Todos los campos son obligatorios.' },
      { status: 400 }
    );
  }

  // Aquí enviarías el email, guardarías en la base de datos, etc.
  console.log('Nuevo mensaje de contacto:', datos);

  return NextResponse.json({ ok: true }, { status: 200 });
}

 

Middleware

Puedes ejecutar código antes de que se procese cualquier petición usando middleware. Es ideal para proteger rutas, redirigir usuarios según su autenticación, aplicar lógica de internacionalización o hacer A/B testing.

// middleware.ts (en la raíz del proyecto)
import { NextRequest, NextResponse } from 'next/server';

export function middleware(request: NextRequest) {
  const token = request.cookies.get('auth-token');

  // Redirigir al login si no hay token y se accede al dashboard
  if (!token && request.nextUrl.pathname.startsWith('/dashboard')) {
    return NextResponse.redirect(new URL('/login', request.url));
  }

  return NextResponse.next();
}

export const config = {
  matcher: ['/dashboard/:path*'],
};

 

Next.js vs React puro: comparativa directa

CaracterísticaReact puroNext.js
Sistema de rutasManual (React Router)Basado en archivos, integrado
Renderizado en servidorNo (solo CSR)SSR, SSG, ISR y CSR
SEODifícil sin configuración extraNativo y sencillo
Optimización de imágenesManual o con libreríasAutomática con <Image>
Optimización de fuentesManualAutomática con next/font
API backendNo incluidaAPI Routes integradas
Acceso a base de datosSolo desde el clienteDesde Server Components
Configuración inicialMínimaMínima (más funcionalidades)
Curva de aprendizajeBajaMedia (requiere entender SSR)
Ideal paraApps privadas, dashboardsCasi cualquier proyecto web

Crear tu primer proyecto con Next.js

Empezar un proyecto con Next.js es tan simple como ejecutar un comando. Necesitas tener Node.js instalado.

npx create-next-app@latest mi-proyecto

El asistente te pregunta varias opciones. Para un proyecto nuevo en 2026, la configuración recomendada es:

✔ Would you like to use TypeScript?          → Yes
✔ Would you like to use ESLint?              → Yes
✔ Would you like to use Tailwind CSS?        → Yes (o No, según prefieras)
✔ Would you like to use the src/ directory?  → No
✔ Would you like to use App Router?          → Yes
✔ Would you like to customize the import alias? → No

Después del asistente, la estructura de tu proyecto queda así:

mi-proyecto/
├── app/
│   ├── layout.tsx       ← layout raíz (envuelve todas las páginas)
│   ├── page.tsx         ← página de inicio  /
│   └── globals.css
├── public/              ← archivos estáticos (imágenes, fuentes, etc.)
├── next.config.js       ← configuración de Next.js
├── package.json
└── tsconfig.json

Para arrancar el servidor de desarrollo:

cd mi-proyecto
npm run dev

Tu aplicación estará disponible en http://localhost:3000.

Un ejemplo completo: página con datos del servidor

Para terminar, un ejemplo que muestra la diferencia más importante respecto a React puro: obtener datos directamente en un Server Component sin useEffect, sin estado de carga y sin que nada de eso llegue al navegador.

// app/page.tsx
// Nota: no tiene 'use client', es un Server Component

interface Post {
  id:    number;
  title: string;
  body:  string;
}

async function obtenerPosts(): Promise<Post[]> {
  const res = await fetch('https://jsonplaceholder.typicode.com/posts?_limit=5', {
    next: { revalidate: 60 }   // regenerar cada 60 segundos (ISR)
  });

  if (!res.ok) throw new Error('Error al obtener los posts');
  return res.json();
}

export default async function Inicio() {
  const posts = await obtenerPosts();

  return (
    <main style={{ maxWidth: '700px', margin: '2rem auto', padding: '0 1rem' }}>
      <h1>Últimas entradas</h1>
      {posts.map(post => (
        <article key={post.id} style={{ marginBottom: '2rem' }}>
          <h2>{post.title}</h2>
          <p>{post.body}</p>
        </article>
      ))}
    </main>
  );
}

 

En React puro, este mismo componente requeriría un useState para guardar los posts, un useState para el estado de carga, un useState para el error, un useEffect para hacer el fetch, y toda esa lógica llegaría al navegador como JavaScript. En Next.js, el componente es una función async que obtiene los datos directamente y devuelve HTML. El navegador recibe el HTML ya construido.

¿Cuándo NO usar Next.js?

Next.js no es la respuesta correcta para todo. Hay casos donde React puro (con Vite, por ejemplo) es una mejor elección:

  • Dashboards o aplicaciones privadas donde el SEO no importa y todos los usuarios están autenticados. El SSR no aporta valor y añade complejidad.
  • Prototipos muy rápidos donde necesitas validar una idea en horas y no quieres pensar en Server vs Client Components.
  • Aplicaciones que ya usan otro backend (Django, Rails, Laravel) y solo necesitan un frontend sin lógica de servidor en JavaScript.
  • Proyectos donde el equipo no conoce el modelo de Next.js y el tiempo de aprendizaje no está justificado.

Resumen

Next.js no reemplaza React: lo extiende. Si ya sabes React, aprender Next.js es añadir capas sobre algo que ya conoces, no empezar desde cero.

La razón por la que los desarrolladores lo prefieren para proyectos reales es sencilla: resuelve de forma integrada y bien pensada los problemas que todo proyecto web termina encontrando. Rutas, SEO, rendimiento, acceso a datos en el servidor, optimización de recursos. En lugar de ensamblar cinco librerías distintas y configurarlas para que funcionen juntas, Next.js te da todo eso listo desde el primer comando.

Si estás empezando un proyecto nuevo con React, la pregunta ya no es si usar Next.js, sino si hay alguna razón de peso para no usarlo.

Etiquetas: Next.js React.js

¿Te ha gustado esta entrada?

Compártela con tus compañeros para que también sigan aprendiendo.

Comunidad y Comentarios

0 COMENTARIOS

No hay comentarios todavía. Sé el primero en compartir tu opinión.

Escribe tu opinión
Respondiendo a