Portafolio profesional
Next.js 14: Guía Completa del App Router
El Nuevo App Router de Next.js Next.js 14 introduce el App Router, una nueva forma de construir aplicaciones con React Server Components, streaming y más. Estru
Next.js 14: Guía Completa del App Router
21 de diciembre de 2025
Osman Jimenez
Next.js React Desarrollo Web
El Nuevo App Router de Next.js
Next.js 14 introduce el App Router, una nueva forma de construir aplicaciones con React Server Components, streaming y más.
Estructura de Carpetas
app/
├── layout.tsx # Layout raíz
├── page.tsx # Página principal
├── loading.tsx # UI de carga
├── error.tsx # UI de error
├── not-found.tsx # 404
├── blog/
│ ├── layout.tsx
│ ├── page.tsx
│ └── [slug]/
│ └── page.tsx
└── api/
└── users/
└── route.tsServer Components por Defecto
// app/page.tsx - Server Component
export default async function HomePage() {
const posts = await fetch('https://api.example.com/posts')
.then(res => res.json());
return (
Blog Posts
{posts.map(post => (
{post.title}
{post.excerpt}
))}
);
}Client Components
'use client'; // Directiva para Client Component
import { useState } from 'react';
export default function Counter() {
const [count, setCount] = useState(0);
return (
);
}Layouts Anidados
// app/layout.tsx - Root Layout
export default function RootLayout({ children }) {
return (
Header Global
{children}
);
}
// app/blog/layout.tsx - Blog Layout
export default function BlogLayout({ children }) {
return (
{children}
);
}Data Fetching
// Fetch con cache
async function getPosts() {
const res = await fetch('https://api.example.com/posts', {
cache: 'force-cache' // Default
});
return res.json();
}
// Revalidar cada 60 segundos
async function getPosts() {
const res = await fetch('https://api.example.com/posts', {
next: { revalidate: 60 }
});
return res.json();
}
// Sin cache
async function getPosts() {
const res = await fetch('https://api.example.com/posts', {
cache: 'no-store'
});
return res.json();
}Rutas Dinámicas
// app/blog/[slug]/page.tsx
export default async function BlogPost({ params }) {
const post = await getPost(params.slug);
return (
{post.title}
);
}
// Generar rutas estáticas
export async function generateStaticParams() {
const posts = await getPosts();
return posts.map((post) => ({
slug: post.slug
}));
}Loading y Streaming
// app/blog/loading.tsx
export default function Loading() {
return Cargando posts...;
}
// Streaming con Suspense
import { Suspense } from 'react';
export default function Page() {
return (
Mi Blog
Cargando posts... }>
Error Handling
// app/blog/error.tsx
'use client';
export default function Error({ error, reset }) {
return (
Algo salió mal!
{error.message}
);
}API Routes
// app/api/users/route.ts
import { NextResponse } from 'next/server';
export async function GET(request: Request) {
const users = await getUsers();
return NextResponse.json(users);
}
export async function POST(request: Request) {
const body = await request.json();
const user = await createUser(body);
return NextResponse.json(user, { status: 201 });
}
// app/api/users/[id]/route.ts
export async function GET(
request: Request,
{ params }: { params: { id: string } }
) {
const user = await getUser(params.id);
return NextResponse.json(user);
}Metadata para SEO
// Metadata estática
export const metadata = {
title: 'Mi Blog',
description: 'Un blog increíble',
openGraph: {
title: 'Mi Blog',
description: 'Un blog increíble',
images: ['/og-image.jpg']
}
};
// Metadata dinámica
export async function generateMetadata({ params }) {
const post = await getPost(params.slug);
return {
title: post.title,
description: post.excerpt,
openGraph: {
title: post.title,
images: [post.image]
}
};
}Server Actions
'use server';
export async function createPost(formData: FormData) {
const title = formData.get('title');
const content = formData.get('content');
await db.post.create({
data: { title, content }
});
revalidatePath('/blog');
redirect('/blog');
}
// Uso en Client Component
'use client';
import { createPost } from './actions';
export default function NewPostForm() {
return (
);
}Mejores Prácticas
- Server Components por defecto: Usa Client Components solo cuando necesites interactividad
- Fetch en Server Components: Mejor performance y SEO
- Streaming: Usa Suspense para mejor UX
- Metadata dinámica: Para mejor SEO
- Server Actions: Para mutations sin API routes
Conclusión
El App Router de Next.js 14 es un cambio de paradigma que aprovecha React Server Components para mejor performance y DX. Vale la pena la migración para proyectos nuevos.