Clase 36 - Next JS
🤔 - ¿Qué es Next.js?
Next.js es un framework de React de código abierto desarrollado por Vercel que proporciona capacidades de renderizado del lado del servidor (SSR), generación de sitios estáticos (SSG), y muchas otras características avanzadas. A diferencia de React puro, Next.js ofrece una solución completa para aplicaciones web de producción con optimizaciones automáticas y un sistema de routing basado en archivos.
💫 - Ventajas Principales
- Renderizado Híbrido: Soporte para SSG, SSR, ISR y CSR según las necesidades de cada página.
- Routing Automático: Sistema de routing basado en la estructura de archivos.
- Optimización Automática: Code splitting, lazy loading, optimización de imágenes.
- TypeScript: Soporte nativo y configuración automática.
- Performance: Web Vitals optimizados, pre-fetching inteligente.
- Developer Experience: Hot reloading, error reporting mejorado.
- Deploy Simple: Integración nativa con Vercel y otras plataformas.
🏗️ - Modos de Renderizado en Next.js
Antes de profundizar en Next.js, es crucial entender los diferentes modos de renderizado disponibles, ya que esta comprensión guiará nuestras decisiones arquitectónicas.
🔄 - Client-Side Rendering (CSR)
Definición: El contenido se renderiza completamente en el navegador usando JavaScript.
"use client"
import { useState, useEffect } from 'react';
interface User { id: number; name: string; email: string;}
export default function Profile() { const [user, setUser] = useState<User | null>(null); const [loading, setLoading] = useState<boolean>(true);
useEffect(() => { // Los datos se cargan después del montaje del componente fetch('/api/user') .then(res => res.json()) .then((data: User) => { setUser(data); setLoading(false); }); }, []);
if (loading) return <div>Cargando...</div>;
return ( <div> <h1>Perfil de {user?.name}</h1> <p>Email: {user?.email}</p> </div> );}🖥️ - Server-Side Rendering (SSR)
Definición: El HTML se genera en el servidor para cada request, enviándose completamente renderizado al cliente.
interface Post { id: number title: string body: string}
interface Comment { id: number name: string body: string}
export default async function Page () { const id = 10
let post: Post | null = null let comments: Comment[] = [] let errorMsg = ""
try { const data = await fetch(`https://jsonplaceholder.typicode.com/posts/${id}`, { cache: "no-store" }) if (!data.ok) throw new Error("No se pudo cargar el post") post = await data.json()
const commentsData = await fetch(`https://jsonplaceholder.typicode.com/posts/${id}/comments`, { cache: "no-store" }) if (!commentsData.ok) throw new Error("No se pudo cargar los comentarios") comments = await commentsData.json() } catch (error: any) { errorMsg = error?.message || "Error desconocido" }
if (errorMsg) { return <div className="p-6 text-red-600">Error: {errorMsg}</div> }
return ( <article className="p-6"> <h1 className="text-2xl font-bold">{post?.title}</h1> <p className="mt-2">{post?.body}</p>
<section className="mt-6"> <h3 className="text-xl font-semibold"> Comentarios ({comments.length}) </h3> <div className="mt-2 space-y-2"> {comments.map((comment) => ( <div key={comment.id} className="border-b pb-2"> <strong>{comment.name}</strong>: {comment.body} </div> ))} </div> </section> </article> )}📄 - Static Site Generation (SSG)
Definición: Las páginas se generan como HTML estático en tiempo de build, sirviendo archivos pre-renderizados.
type Post = { id: number title: string body: string}
async function getPosts () { const res = await fetch('https://jsonplaceholder.typicode.com/posts') return res.json()}
export default async function Page () { const posts = await getPosts()
return posts.map((post: Post) => ( <article key={post.id}> <h1>{post.title}</h1> <p>{post.body}</p> </article> ))}🔄 - Incremental Static Regeneration (ISR)
Definición: Combina los beneficios de SSG con la capacidad de regenerar páginas estáticas bajo demanda.
type Post = { id: number title: string body: string}
// NEXT invalidará la caché de la página cada 60 segundosexport const revalidate = 60
export async function generateStaticParams () { const posts = await getPosts() return posts.map((post: Post) => ({ id: post.id.toString() }))}
async function getPosts () { const res = await fetch('https://jsonplaceholder.typicode.com/posts') return res.json()}
export default async function Page ({ params }: { params: Promise<{ id: string }> }) { const { id } = await params
const response = await fetch(`https://jsonplaceholder.typicode.com/posts/${id}`)
const post: Post = await response.json()
return ( <article> <h1>{post.title}</h1> <p>{post.body}</p> </article> )}⚡ - React Server Components (RSC)
Definición: Componentes que se ejecutan en el servidor, reduciendo el bundle de JavaScript enviado al cliente.
import { Suspense } from 'react'
interface UserData { id: number name: string username: string email: string address: { street: string suite: string city: string zipcode: string geo: { lat: string lng: string } } phone: string website: string company: { name: string catchPhrase: string bs: string }}
// Este componente se ejecuta en el servidorasync function ServerData () { // Fetch directo en el componente, sin useEffect const userData: UserData = await fetch('https://jsonplaceholder.typicode.com/users/1', { next: { revalidate: 300 } // Cache por 5 minutos }).then(r => r.json())
return <UserStats data={userData} />}
export default function Dashboard () { return ( <div> <h1>Dashboard</h1> <Suspense fallback={<div>Cargando estadísticas...</div>}> <ServerData /> </Suspense> <Suspense fallback={<div>Cargando actividad...</div>}> <RecentActivity /> </Suspense> </div> )}
// -- Componentes -------------------------------------------------------------
function UserStats ({ data }: { data: UserData }) { return ( <div> <h2>Información del usuario</h2> <p><strong>Nombre:</strong> {data.name}</p> <p><strong>Usuario:</strong> {data.username}</p> <p><strong>Email:</strong> {data.email}</p> <p><strong>Teléfono:</strong> {data.phone}</p> <p><strong>Ciudad:</strong> {data.address.city}</p> <p><strong>Compañía:</strong> {data.company.name}</p> </div> )}
function RecentActivity () { return ( <div> <h2>Actividad reciente</h2> <p>Esta información se cargará en segundo plano</p> </div> )}🗺️ - Sistema de Routing
Next.js incluye dos sistemas de routing: Pages Router (legacy) y App Router (recomendado).
📁 - App Router (app/)
El App Router utiliza la estructura de carpetas para definir rutas:
Directorioapp/
- page.ts
Directorioabout/
- page.ts
Directorioblog/
- page.ts
Directorio[slug]/
- page.ts
Directorioproducts/
- page.ts
Directorio[id]/
- page.ts
Directorioreviews/
- page.ts
Directoriodashboard/
- layout.ts
- page.ts
Directoriosettings/
- page.ts
Directorioanalytics/
- page.ts