Optimización de rendimiento con React Server Components: obtención de datos y caché en la práctica

Si el TTFB de tus páginas RSC sigue entre 300 y 500 ms, probablemente solo estés aprovechando alrededor del 30 % del potencial de rendimiento. Datos reales muestran que una arquitectura de streaming bien configurada puede bajar el TTFB a 45 ms: no es magia, es el resultado de activar de verdad el renderizado en streaming de React Server Components.
El año pasado ayudé a un equipo de e-commerce a optimizar su página de detalle de producto y caí en la misma trampa. Usaban Next.js App Router, pero el TTFB se mantenía estable en unos 380 ms. Tras investigar, descubrimos que los componentes anidados obtenían datos por separado, formando una cascada típica: la info del producto esperaba a las reseñas, las reseñas al precio y el precio a la verificación de stock. Nueve segundos de pantalla en blanco.
Este artículo trata de cómo resolver eso. Compararé 4 enfoques para la cascada, explicaré cuándo usar cada una de las 5 APIs de caché y te daré plantillas de configuración listas para copiar. De 450 ms a 45 ms de TTFB: la diferencia puede estar solo en dónde colocas unos cuantos límites de Suspense.
El problema de cascada: el mayor enemigo del rendimiento RSC
Imagina un escenario real. Abres la ficha de un producto en una tienda online: primero aparece el nombre, tres segundos después el precio y cinco segundos más tarde los comentarios. ¿Experiencia de usuario? Un desastre.
Eso es la cascada. Componentes anidados obtienen datos por separado y se ejecutan en serie, no en paralelo. En React Server Components, la obtención de datos bloquea el renderizado de forma síncrona por defecto: cualquier petición con await detiene el render a menos que uses Suspense.
Dos formas de cascada
Primera: cascada interna en el servidor. En la misma página, el componente padre obtiene datos antes de renderizar los hijos, y cada hijo vuelve a pedir los suyos. Código típico:
// Ejemplo de cascada — código problemático
async function ProductPage({ id: string }) {
// Primera petición: 1 s
const product = await db.getProduct(id);
// Estas peticiones empiezan solo tras renderizar los hijos
return (
<div>
<ProductDetails product={product} />
<ProductPrice id={id} /> {/* await getPrice(id) interno, 3 s */}
<ProductReviews id={id} /> {/* await getReviews(id) interno, 5 s */}
</div>
);
}
// ProductPrice.tsx
async function ProductPrice({ id }) {
const price = await getPrice(id); // Se ejecuta tras el render del padre
return <span>{price}</span>;
}
¿Tiempo total? Nueve segundos. El usuario mira una pantalla vacía durante nueve segundos.
Segunda: cascada cliente-servidor. Un componente cliente pide al servidor y el servidor consulta la base de datos. Es más difícil de ver; React DevTools Profiler la detecta. Una variante del problema N+1.
Cómo identificar la cascada
Abre React DevTools Profiler y graba una carga de página. Si en la línea de tiempo las peticiones forman escalones — cada una espera a la anterior — tienes cascada.
Otra forma más directa: en la pestaña Network del navegador, mira cuándo se lanzan las peticiones. Si están repartidas en el tiempo en lugar de dispararse juntas, casi seguro que ahí está el problema.
Muchos desarrolladores creen que usar RSC ya da rendimiento automático. No es así. Según el informe de SitePoint de 2026, la mayoría de equipos solo aprovecha alrededor del 30 % del potencial RSC. La causa habitual: no tratar la cascada.
Cuatro soluciones comparadas: de lo bruto a lo elegante
Hay cuatro enfoques habituales para la cascada. De simple a complejo, de bruto a elegante.
Solución 1: obtención paralela con Promise.all
La idea más directa: lanzar todas las peticiones a la vez y esperar con Promise.all.
// Solución 1: Promise.all en paralelo
async function ProductPage({ id: string }) {
// Lanzar todas las peticiones a la vez
const [product, price, reviews] = await Promise.all([
getProduct(id), // 1 s
getPrice(id), // 3 s
getReviews(id), // 5 s
]);
return (
<div>
<ProductDetails product={product} />
<ProductPriceDisplay price={price} />
<ProductReviewsList reviews={reviews} />
</div>
);
}
¿Tiempo total? Cinco segundos. Lo marca la petición más lenta.
Ventajas: simple, pocos cambios.
Desventajas: el usuario sigue esperando la petición más lenta para ver algo. Además hay acoplamiento de datos: el padre debe saber qué necesita cada hijo, lo que rompe la independencia de componentes.
Solución 2: aislamiento con límites Suspense
Envuelve las partes que dependen de datos con Suspense para mostrar primero lo crítico.
// Solución 2: límites Suspense
async function ProductPage({ id: string }) {
const product = await getProduct(id); // Datos clave primero
return (
<div>
<ProductDetails product={product} /> {/* Visible a 1 s */}
{/* Partes no críticas con Suspense */}
<Suspense fallback={<PriceSkeleton />}>
<ProductPrice id={id} />
</Suspense>
<Suspense fallback={<ReviewsSkeleton />}>
<ProductReviews id={id} />
</Suspense>
</div>
);
}
Experiencia: a 1 s ves el producto, a 3 s el precio y a 5 s las reseñas.
Ventajas: contenido clave primero, mejor percepción.
Desventajas: las peticiones siguen iniciándose en serie. Las de ProductPrice y ProductReviews arrancan tras renderizar el padre, no en paralelo real.
Solución 3: pasar Promise como props
El padre inicia todas las peticiones y pasa cada Promise como prop; el hijo hace await.
// Solución 3: patrón de paso de Promise
async function ProductPage({ id: string }) {
// Lanzar todo sin await
const productPromise = getProduct(id);
const pricePromise = getPrice(id);
const reviewsPromise = getReviews(id);
// Solo await de lo crítico
const product = await productPromise;
return (
<div>
<ProductDetails product={product} />
<Suspense fallback={<PriceSkeleton />}>
<ProductPrice pricePromise={pricePromise} />
</Suspense>
<Suspense fallback={<ReviewsSkeleton />}>
<ProductReviews reviewsPromise={reviewsPromise} />
</Suspense>
</div>
);
}
// ProductPrice.tsx — recibe Promise
async function ProductPrice({ pricePromise }) {
const price = await pricePromise; // Reutiliza la Promise del padre
return <span>{price}</span>;
}
Las tres peticiones arrancan a la vez en el padre. Datos clave a 1 s, precio a 3 s, reseñas a 5 s.
Ventajas: peticiones en paralelo, contenido clave primero, datos desacoplados (el hijo solo recibe Promise).
Desventajas: hay que cambiar la interfaz del componente: de id a Promise.
Solución 4: React cache() + preload (recomendada)
React 19 introduce cache(). Con el patrón preload es la solución más elegante.
// Solución 4: React cache() + preload
import { cache } from 'react';
// Envolver la obtención de datos con cache
const getComments = cache(async (postId: string) => {
return db.getComments(postId);
});
// Exportar preload con propósito explícito
export const preloadComments = (id: string) => {
void getComments(id); // Sin await: inicia pero no bloquea
};
// Componente padre
async function PostPage({ postId: string }) {
preloadComments(postId); // Precarga comentarios
const post = await getPost(postId); // Solo await de lo crítico
return (
<div>
<PostContent post={post} />
<Suspense fallback={<CommentsSkeleton />}>
<Comments postId={postId} /> {/* Sigue usando id; cache reutiliza */}
</Suspense>
</div>
);
}
// Comments.tsx — interfaz sin cambios
async function Comments({ postId }) {
const comments = await getComments(postId); // Reutiliza la Promise del preload
return <CommentList comments={comments} />;
}
Principio: cache() memoiza automáticamente en el mismo ciclo de render. preload dispara la petición sin esperar; el hijo reutiliza la misma Promise al hacer await.
Ventajas:
- La interfaz del componente no cambia (sigue recibiendo id)
- Peticiones memoizadas automáticamente, sin acoplamiento de datos
- Si eliminas el hijo, el preload queda como código muerto fácil de detectar
Desventajas: hay que entender cache() y vigilar acoplamiento oculto (al borrar Comments, quitar también preload).
Comparativa de las cuatro soluciones
| Solución | Tiempo total | Contenido clave visible | Acoplamiento | Coste de cambio |
|---|---|---|---|---|
| Secuencial | 9s | 9s | Ninguno | Ninguno |
| Promise.all | 5s | 5s | Sí | Bajo |
| Suspense | 5s | 1s | No | Bajo |
| Paso de Promise | 5s | 1s | Desacoplado | Medio |
| cache() + preload | 1s | 1s | No | Medio |
La elección depende del equipo. Migración rápida: solución 2; proyecto nuevo: solución 4.
Arquitectura de streaming: el secreto del TTFB a 45 ms
El SSR clásico espera todos los datos, renderiza el HTML completo y lo envía de una vez. El TTFB (Time to First Byte) es tiempo de datos más tiempo de render.
Cifras concretas: consulta a BD 400 ms, render 50 ms, TTFB ~450 ms. Casi medio segundo de pantalla en blanco.
Cómo RSC Streaming cambia el flujo
El streaming no reduce tanto como cambia el orden en que llega el contenido. La parte estática sale al instante; la dinámica se completa en flujo.
// Ejemplo de arquitectura streaming
export default async function Dashboard() {
return (
<Layout> {/* Shell estático, sin Suspense */}
<Nav /> {/* Render inmediato */}
<Sidebar /> {/* Render inmediato */}
<Suspense fallback={<ChartSkeleton />}>
<DynamicChart /> {/* Datos dinámicos, streaming */}
</Suspense>
<Suspense fallback={<TableSkeleton />}>
<DataTable /> {/* Datos dinámicos, streaming */}
</Suspense>
</Layout>
);
}
Desglose del flujo:
- T=0 ms: el shell estático (Layout, Nav, Sidebar) sale desde caché CDN al borde
- T=30-50 ms: el navegador renderiza el shell y muestra skeletons
- T=200 ms: DynamicChart termina; el contenido de ese Suspense se envía en streaming
- T=400 ms: DataTable termina; su contenido se envía en streaming
¿TTFB? Unos 45 ms: el momento en que sale el shell estático.
Papel de PPR (Partial Prerendering)
PPR llegó en Next.js 15; Next.js 16 lo activará por defecto. Pre-renderiza lo estático en CDN y mantiene streaming en lo dinámico.
Configuración:
// next.config.js — Next.js 15
module.exports = {
experimental: {
ppr: true, // Activar PPR
},
};
// next.config.js — Next.js 16 (preview)
module.exports = {
experimental: {
ppr: 'incremental', // Activación gradual
cacheComponents: true, // Nuevo modelo de caché
},
};
Con PPR, el shell estático (navegación, layout, skeletons) se pre-renderiza en CDN. Al visitar, el CDN devuelve HTML estático al instante y el servidor completa lo dinámico en streaming.
Principios de diseño de límites Suspense
Regla clave: si olvidas marcar bloques streamables con Suspense, React trata toda la app como un bloque gigante.
Buenas prácticas:
- Estático sin Suspense: navegación, Layout, skeletons sin datos
- Dinámico con Suspense: componentes que dependen de BD/API
// Ejemplo correcto
export default async function Page() {
return (
<>
<Header /> {/* Estático, sin envolver */}
<main>
<Suspense fallback={<HeroSkeleton />}>
<HeroSection /> {/* Dinámico, envuelto */}
</Suspense>
<Suspense fallback={<ContentSkeleton />}>
<MainContent /> {/* Dinámico, envuelto */}
</Suspense>
</main>
<Footer /> {/* Estático, sin envolver */}
</>
);
}
Ejemplo incorrecto (bloquea toda la página):
// Incorrecto — sin Suspense
export default async function Page() {
const data = await fetchDashboard(); // await bloquea toda la página
return (
<>
<Header />
<Dashboard data={data} />
<Footer />
</>
);
}
Sin límites Suspense, toda la página es un solo bloque streamable. El TTFB sigue en 450 ms.
Comparativa de rendimiento
| Modo de render | TTFB | LCP | Notas |
|---|---|---|---|
| SSR clásico | ~450ms | ~500ms | Espera todos los datos |
| RSC (sin Suspense) | ~450ms | ~500ms | Equivalente al SSR clásico |
| RSC Streaming | ~45ms | ~200ms | Shell estático al instante |
| RSC + PPR | ~30ms | ~150ms | Shell estático en CDN |
Fuente: informe SitePoint 2026; cifras reales pueden variar según datos y CDN.
Guía de las cinco APIs de caché
Next.js y React ofrecen cinco mecanismos de caché. Elegir bien multiplica resultados; elegir mal puede duplicar peticiones.
1. fetch cache (la más usada)
Las peticiones fetch en Server Components se memoizan solas. En el mismo ciclo de render, misma URL y parámetros = una sola petición.
// Ejemplo fetch cache
async function ProductCard({ id }) {
// Caché automática; misma URL no repite petición
const res = await fetch(`https://api.example.com/products/${id}`, {
cache: 'force-cache', // Forzar caché (por defecto)
next: {
revalidate: 3600, // Revalidar cada hora
tags: ['products'], // Etiqueta para revalidateTag
},
});
return <Card data={res.json()} />;
}
async function ProductList() {
// Reutiliza la caché de arriba
const res = await fetch('https://api.example.com/products', {
next: { tags: ['products'] },
});
return <List data={res.json()} />;
}
Opciones:
cache: 'force-cache': priorizar caché (por defecto)cache: 'no-store': petición nueva cada veznext.revalidate: revalidación programada (segundos)next.tags: etiquetas para refresco manual con revalidateTag
2. React cache() (nuevo en React 19)
Cachea resultados de funciones. Ideal para consultas a BD y obtención personalizada.
import { cache } from 'react';
// Envolver consulta a BD
export const getUser = cache(async (id: string) => {
const user = await db.query('SELECT * FROM users WHERE id = ?', [id]);
return user;
});
// Varios componentes; memoización automática
async function UserProfile({ id }) {
const user = await getUser(id);
return <Profile user={user} />;
}
async function UserStats({ id }) {
const user = await getUser(id); // Reutiliza el resultado anterior
return <Stats user={user} />;
}
Nota: cache() solo vale en el mismo ciclo de render. Entre peticiones HTTP usa unstable_cache.
3. unstable_cache (Next.js 14-15)
Caché persistente entre peticiones. Para cálculos costosos y datos compartidos entre páginas.
import { unstable_cache } from 'next/cache';
// Envolver función con caché persistente
export const getPopularProducts = unstable_cache(
async () => {
const products = await db.getPopularProducts();
return products;
},
['popular-products'], // Clave de caché
{
revalidate: 3600, // Revalidar cada hora
tags: ['products', 'popular'], // Varias etiquetas
}
);
// Uso
async function HomePage() {
const products = await getPopularProducts();
return <ProductGrid products={products} />;
}
Refresco manual:
import { revalidateTag } from 'next/cache';
// En Server Action o API Route
async function updateProduct() {
await db.updateProduct();
await revalidateTag('products'); // Refresca caché con etiqueta products
}
4. use cache (nuevo en Next.js 16)
Directiva de caché a nivel de componente o función. Con ‘use cache’ al inicio, la salida se cachea sola.
// 'use cache' a nivel de función
'use cache';
export async function getRecommendations(userId: string) {
return db.getRecommendations(userId);
}
// 'use cache' a nivel de componente
'use cache';
export async function CachedFooter() {
const links = await getFooterLinks();
return <Footer links={links} />;
}
Casos de uso: componentes muy visitados, contenido estático. Experimental; soporte formal en Next.js 16.
5. revalidatePath / revalidateTag
Formas de invalidar caché manualmente.
import { revalidatePath, revalidateTag } from 'next/cache';
// Por ruta
await revalidatePath('/products'); // Toda la caché de esa ruta
await revalidatePath('/products/[id]', 'page'); // Página concreta
// Por etiqueta
await revalidateTag('products'); // Toda la caché con etiqueta products
Recomendación:
- Control fino: revalidateTag (recomendado)
- Refresco masivo: revalidatePath
Comparativa de APIs de caché
| API | Alcance | Persistente | Caso de uso | Versión |
|---|---|---|---|---|
| fetch cache | Una petición | Configurable | Peticiones API | Next.js 13+ |
| React cache() | Un ciclo de render | No | BD, funciones custom | React 19 |
| unstable_cache | Entre peticiones | Sí | Cálculo costoso, datos compartidos | Next.js 14-15 |
| use cache | Función/componente | Sí | Componentes frecuentes | Next.js 16 |
| Cache Components | Salida de componente | Sí | Con PPR | Next.js 16 |
Plantillas de configuración y errores habituales
Pasemos a configuración copiable.
next.config.js completo
// next.config.mjs
/** @type {import('next').NextConfig} */
const nextConfig = {
experimental: {
// Next.js 15: activar PPR
ppr: true,
// Next.js 16: nuevo modelo de caché
// cacheComponents: true, // Activar en versión estable
},
// Rendimiento
images: {
formats: ['image/avif', 'image/webp'],
},
// Optimización de salida
output: 'standalone', // Para despliegue Docker
};
export default nextConfig;
Convención para funciones preload
preload puede crear acoplamiento oculto. Al borrar un hijo profundo, el preload puede quedar huérfano.
Buena práctica: comentar encima del preload su propósito y el componente asociado.
// comments.ts
import { cache } from 'react';
const getComments = cache(async (postId: string) => {
return db.getComments(postId);
});
/**
* preloadComments: precarga comentarios para el componente Comments
* Nota: al eliminar Comments, eliminar también esta función preload
*/
export const preloadComments = (id: string) => {
void getComments(id);
};
export async function Comments({ postId }) {
const comments = await getComments(postId);
return <CommentList comments={comments} />;
}
Errores frecuentes
Error 1: olvidar límites Suspense
Síntoma: TTFB sigue en 450 ms, sin efecto streaming.
Causa: sin Suspense, React trata toda la página como un bloque.
Solución: envolver componentes con datos.
// Antes
async function Page() {
const data = await getData(); // Bloquea toda la página
return <Dashboard data={data} />;
}
// Después
async function Page() {
return (
<Suspense fallback={<DashboardSkeleton />}>
<Dashboard />
</Suspense>
);
}
Error 2: preload sin uso
Síntoma: se lanza la petición pero no se usa; desperdicio.
Causa: borraste el hijo pero dejaste preload.
Solución: borrar preload al borrar el componente, o documentar la relación.
Error 3: conflicto de tags de caché
Síntoma: revalidateTag refresca de más.
Causa: datos distintos comparten la misma etiqueta.
Solución: etiquetas distintas por dominio de negocio.
// Incorrecto
await fetch(url, { next: { tags: ['data'] } }); // Todo con tag 'data'
await revalidateTag('data'); // Refresca toda la caché
// Correcto
await fetch(productsUrl, { next: { tags: ['products'] } });
await fetch(usersUrl, { next: { tags: ['users'] } });
await revalidateTag('products'); // Solo products
Error 4: mezclar fetch cache y React cache
Síntoma: la misma data dispara dos peticiones.
Causa: fetch con ‘no-store’ impide reutilizar React cache().
Solución: fetch con force-cache o por defecto para que React cache pueda reutilizar.
// Incorrecto
const data1 = await fetch(url, { cache: 'no-store' }); // Sin caché
const data2 = await getData(); // React cache no puede reutilizar
// Correcto
const data1 = await fetch(url); // force-cache por defecto
const data2 = await getData(); // Puede reutilizar
Herramientas de depuración
- React DevTools Profiler: graba el render y detecta cascadas
- Análisis Next.js:
next build --experimental-debugpara el build - Chrome DevTools: Network para tiempos de petición; Performance para render
Métricas clave:
- TTFB: tiempo al primer byte, objetivo < 100 ms
- LCP: Largest Contentful Paint, objetivo < 2,5 s
- CLS: Cumulative Layout Shift, objetivo < 0,1
Recomendaciones de migración
De SSR clásico a RSC en streaming:
- Identificar cascadas: Profiler y peticiones secuenciales
- Añadir Suspense: envolver lo que depende de datos; priorizar ruta crítica
- Añadir preload: cache() + preload en componentes profundos
- Configurar caché: tags en fetch para invalidación precisa
Migra por fases, no reescribas todo de golpe. Empieza por las páginas más lentas y mide antes de extender.
Resumen
En esencia: detectar cascada, elegir solución, configurar streaming.
La cascada se reconoce fácil: componentes anidados con await secuencial y peticiones en escalera. Cuatro soluciones según contexto: migración rápida con Suspense; proyecto nuevo con React cache() + preload.
La clave del streaming está en dónde pones Suspense. Estático (nav, Layout) sin envolver; dinámico (datos) siempre envuelto. Sin ese límite, la página sigue bloqueada.
El beneficio es medible: TTFB de 450 ms a 45 ms, diez veces. A veces basta con unos Suspense y un preload.
Abre tu proyecto Next.js y busca componentes anidados que piden datos por separado. Si el TTFB supera 300 ms, prueba a envolver lo crítico con Suspense. Tus usuarios lo notarán al instante.
Referencias
- Data Fetching Patterns and Best Practices — Documentación oficial de Next.js
- React Server Components Streaming Performance Guide 2026 — SitePoint, 2026-02
- Avoiding Server Component Waterfall Fetching with React 19 cache() — Aurora Scharff, 2025-02
- Avoiding Waterfalls in React Server Components — Akhila Ariyachandra, 2026-01
- Functions: revalidatePath — Documentación oficial de Next.js
- Functions: revalidateTag — Documentación oficial de Next.js
- Partial Prerendering (PPR) — Documentación oficial de Next.js
- React v19 Release — Blog oficial de React
Flujo de optimización de React Server Components
Pasos completos desde identificar cascadas hasta configurar arquitectura streaming
⏱️ Estimated time: 60 min
- 1
Step 1: Identificar cascada
Graba la carga con React DevTools Profiler:
• Abre Chrome DevTools, pestaña Profiler
• Graba, recarga la página y espera a que termine
• Revisa la distribución de peticiones en la línea de tiempo
• Distribución en escalones = cascada
• Comprueba si el TTFB supera 300 ms - 2
Step 2: Elegir solución
Según tu equipo:
• Migración rápida: solución 2 (Suspense)
• Proyecto nuevo: solución 4 (React cache() + preload)
• Si toleras acoplamiento: solución 1 (Promise.all)
• Sin cambiar interfaz: solución 4 (recomendada) - 3
Step 3: Añadir límites Suspense
Envuelve componentes con datos:
• Estático sin envolver (nav, Layout)
• Dinámico siempre envuelto
• Fallback skeleton adecuado
• Prioriza la ruta crítica - 4
Step 4: Configurar React cache() + preload
Usa la API cache() de React 19:
• Envuelve funciones de obtención con cache
• Exporta preload sin await
• Comenta la relación componente-preload
• Borra preload al eliminar el componente - 5
Step 5: Configurar estrategia de caché
Elige la API adecuada:
• fetch cache: peticiones API (más común)
• React cache(): consultas a BD
• unstable_cache: compartir entre peticiones
• Añade tags para invalidación precisa - 6
Step 6: Medir el beneficio
Valida la optimización:
• TTFB objetivo: < 100 ms
• LCP objetivo: < 2,5 s
• CLS objetivo: < 0,1
• Compara métricas antes y después
FAQ
¿Qué es la cascada en React Server Components?
¿Cómo elegir entre las cuatro soluciones de cascada?
• Promise.all: simple y directo; acoplamiento de datos
• Suspense: contenido clave primero; buena para migración rápida
• Paso de Promise: paralelo total; requiere cambiar props
• React cache() + preload: más elegante; recomendado en proyectos nuevos
¿Dónde colocar los límites Suspense?
¿En qué se diferencian las cinco APIs de caché?
• fetch cache: peticiones API, memoización automática (Next.js 13+)
• React cache(): consultas BD, un ciclo de render (React 19)
• unstable_cache: persistente entre peticiones, cálculo costoso (Next.js 14-15)
• use cache: caché de función/componente (Next.js 16)
• revalidatePath/Tag: invalidación manual
¿Cómo medir la optimización RSC?
¿Qué es PPR (Partial Prerendering)?
¿Errores habituales en configuración de caché?
• Olvidar Suspense: página bloqueada, TTFB sin mejora
• preload huérfano: componente borrado, petición desperdiciada
• Tags conflictivos: revalidateTag invalida de más
• Mezclar fetch cache y React cache: no-store impide reutilizar
15 min de lectura · Publicado el: 13 may 2026 · Actualizado el: 21 ago 2026
Guía completa de Next.js
Si llegaste desde búsqueda, lo más rápido es ir al artículo anterior o siguiente de esta misma serie.
Anterior
Next.js App Router + shadcn/ui: guía para mezclar componentes de servidor y cliente
Guía detallada para mezclar correctamente Server Components y Client Components en Next.js App Router: integración con shadcn/ui, diseño del flujo de datos, corrección de errores habituales y optimización de rendimiento
Parte 46 de 51
Siguiente
Tutorial de NextAuth.js: guía completa de inicio de sesión con Credentials y gestión de sesiones
¿NextAuth.js te parece demasiado complejo? Este artículo se centra en el inicio de sesión con Credentials, la elección entre JWT y Session, ejemplos de código completos y errores habituales para que los desarrolladores de Next.js monten su sistema de autenticación rápido.
Parte 48 de 51



Comentarios
Inicia sesión con GitHub para dejar un comentario