Por que Next.js domina el desarrollo web en 2026
En mi experiencia trabajando con React durante los ultimos 5 anos, Next.js se ha consolidado como el framework por defecto para proyectos profesionales. No es solo una opinion personal: Vercel reporta que mas del 50% de las nuevas apps React se construyen con Next.js, y empresas como Netflix, TikTok y Notion lo usan en produccion.
Next.js resuelve los problemas que React por si solo no maneja: routing, renderizado del lado del servidor (SSR), generacion estatica (SSG), optimizacion de imagenes, y mucho mas. Con el App Router introducido en la version 13 y madurado en la 14, el framework dio un salto enorme en simplicidad y rendimiento.
Crear tu proyecto
# Crear nuevo proyecto Next.js 14
npx create-next-app@latest mi-app --typescript --tailwind --app --src-dir
# Navegar al proyecto
cd mi-app
# Iniciar servidor de desarrollo
npm run dev
# Abrir en http://localhost:3000
Estructura del App Router
Un error que cometi al principio fue confundir el App Router (carpeta app/) con el Pages Router (carpeta pages/). En 2026, el App Router es el estandar. Su estructura es basada en carpetas:
src/
app/
layout.tsx # Layout raiz (envuelve toda la app)
page.tsx # Pagina principal (/)
globals.css # Estilos globales
about/
page.tsx # /about
blog/
page.tsx # /blog
[slug]/
page.tsx # /blog/mi-post (ruta dinamica)
api/
hello/
route.ts # API endpoint: GET /api/hello
| Archivo | Proposito |
|---|---|
page.tsx | Define una ruta accesible por URL |
layout.tsx | Layout compartido (no se re-renderiza al navegar) |
loading.tsx | UI de carga (Suspense automatico) |
error.tsx | Manejo de errores por ruta |
not-found.tsx | Pagina 404 personalizada |
route.ts | API endpoint (reemplaza pages/api) |
Server Components vs Client Components
La innovacion mas importante del App Router es que todos los componentes son Server Components por defecto. Esto significa que se renderizan en el servidor, no envian JavaScript al navegador, y pueden acceder directamente a bases de datos y APIs.
// app/page.tsx - Server Component (por defecto)
// Puede hacer fetch directo sin useEffect ni useState
async function HomePage() {
const posts = await fetch("https://api.example.com/posts").then(r => r.json());
return (
<main className="max-w-4xl mx-auto p-8">
<h1 className="text-3xl font-bold mb-6">Mi Blog</h1>
{posts.map((post: any) => (
<article key={post.id} className="mb-4 p-4 border rounded">
<h2 className="text-xl font-semibold">{post.title}</h2>
<p className="text-gray-600">{post.body}</p>
</article>
))}
</main>
);
}
export default HomePage;
// components/SearchBar.tsx - Client Component
// Necesita "use client" porque usa useState y onChange
"use client";
import { useState } from "react";
export default function SearchBar() {
const [query, setQuery] = useState("");
return (
<input
type="text"
value={query} => setQuery(e.target.value)}
placeholder="Buscar..."
className="p-2 border rounded w-full"
/>
);
}
La regla de oro: usa Server Components para todo lo que puedas (data fetching, layouts, paginas). Solo usa "use client" cuando necesites interactividad (useState, useEffect, event handlers, APIs del navegador).
Rutas dinamicas
// app/blog/[slug]/page.tsx
interface Props {
params: { slug: string };
}
async function BlogPost({ params }: Props) {
const post = await fetch(
`https://api.example.com/posts/${params.slug}`
).then(r => r.json());
return (
<article className="max-w-3xl mx-auto p-8">
<h1 className="text-3xl font-bold">{post.title}</h1>
<p className="text-gray-500 mt-2">{post.date}</p>
<div className="mt-6 prose">{post.content}</div>
</article>
);
}
export default BlogPost;
API Routes con Route Handlers
// app/api/posts/route.ts
import { NextResponse } from "next/server";
// GET /api/posts
export async function GET(request: Request) {
const { searchParams } = new URL(request.url);
const page = searchParams.get("page") || "1";
// Aqui iria tu logica de base de datos
const posts = [
{ id: 1, title: "Post 1" },
{ id: 2, title: "Post 2" },
];
return NextResponse.json({ posts, page: Number(page) });
}
// POST /api/posts
export async function POST(request: Request) {
const body = await request.json();
if (!body.title) {
return NextResponse.json(
{ error: "Title is required" },
{ status: 400 }
);
}
// Crear post en la base de datos
const newPost = { id: Date.now(), ...body };
return NextResponse.json(newPost, { status: 201 });
}
Errores comunes y soluciones
Error 1: "You are attempting to export use client from a Server Component". Estas usando hooks de React (useState, useEffect) en un Server Component. Agrega "use client" al inicio del archivo o mueve la logica interactiva a un componente hijo con "use client".
Error 2: Hydration mismatch. El HTML del servidor no coincide con lo que React genera en el cliente. Causa comun: usar Date.now() o Math.random() directamente en el render. Solucion: usa useEffect para valores que varian entre servidor y cliente.
Error 3: "Dynamic server usage: force-dynamic". Next.js intenta generar paginas estaticamente. Si necesitas datos frescos en cada request, agrega export const dynamic = "force-dynamic" al inicio de tu page.tsx.
Error 4: Imports circulares entre Server y Client Components. Un Server Component puede importar un Client Component, pero no al reves. Estructura tu arbol de componentes de arriba (server) hacia abajo (client).