{post.title}
{post.excerpt}
Next.js 14 introduce el App Router, una nueva forma de construir aplicaciones con React Server Components, streaming y más.
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.ts// 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}
))}
);
}'use client'; // Directiva para Client Component
import { useState } from 'react';
export default function Counter() {
const [count, setCount] = useState(0);
return (
);
}// 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}
);
}// 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();
}// 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
}));
}// 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... }>
// app/blog/error.tsx
'use client';
export default function Error({ error, reset }) {
return (
Algo salió mal!
{error.message}
);
}// 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 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]
}
};
}'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 (
);
}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.
Asistente Virtual
Descubre mi experiencia, proyectos y habilidades