Volver al blog

React 20 Llego: Server Actions Estable y Partial Prerendering Cambian Todo

Hola HaWkers, React 20 finalmente esta aqui y no es solo otra actualizacion incremental. Esta version representa el mayor cambio arquitectural desde los Hooks. Server Actions ahora son estables, Partial Prerendering elimina la pantalla blanca, y el React Compiler hace memoizacion automatica.

Si pensabas que ya sabias React, preparate para reaprender algunas cosas. Vamos a explorar todo lo que cambio.

Server Actions: La Estrella de React 20

Despues de meses en Canary, Server Actions finalmente graduaron a estable.

Que Son Server Actions

Server Actions permiten ejecutar codigo en el servidor directamente desde componentes React, sin crear API routes separadas.

// Antes: Necesitabas API route + fetch en el cliente
// pages/api/submit.js
export default async function handler(req, res) {
  const data = JSON.parse(req.body);
  await saveToDatabase(data);
  res.json({ success: true });
}

// component.jsx
async function handleSubmit(formData) {
  const res = await fetch('/api/submit', {
    method: 'POST',
    body: JSON.stringify(formData)
  });
  return res.json();
}
// Despues: Server Action directo en el componente
// actions.js
'use server'

export async function submitForm(formData) {
  const name = formData.get('name');
  const email = formData.get('email');

  await saveToDatabase({ name, email });

  return { success: true };
}

// component.jsx
import { submitForm } from './actions';

function ContactForm() {
  return (
    <form action={submitForm}>
      <input name="name" required />
      <input name="email" type="email" required />
      <button type="submit">Enviar</button>
    </form>
  );
}

Beneficios Practicos

1. Menos Boilerplate:

// API routes eliminadas
// Sin fetch() manual
// Sin gestion de loading states manual
// TypeScript end-to-end

2. Progressive Enhancement:

// El formulario funciona incluso sin JavaScript!
<form action={serverAction}>
  {/* Si JS falla, el form aun envia */}
</form>

3. Integracion Con useActionState:

'use client'
import { useActionState } from 'react';
import { submitForm } from './actions';

function Form() {
  const [state, formAction, isPending] = useActionState(
    submitForm,
    { message: '' }
  );

  return (
    <form action={formAction}>
      <input name="email" disabled={isPending} />
      <button disabled={isPending}>
        {isPending ? 'Enviando...' : 'Enviar'}
      </button>
      {state.message && <p>{state.message}</p>}
    </form>
  );
}

Partial Prerendering: Fin de la Pantalla Blanca

PPR (Partial Prerendering) resuelve uno de los problemas mas antiguos de React: el tiempo de carga inicial.

El Problema Antiguo

Usuario hace clic en el enlace

Pantalla blanca (cargando JS)

Pantalla blanca (hidratando)

Contenido aparece (finalmente!)

Tiempo total: 2-5 segundos en conexiones lentas

Como Funciona PPR

Usuario hace clic en el enlace

Contenido estatico aparece INMEDIATAMENTE

Partes dinamicas hacen streaming mientras cargan

Pagina totalmente interactiva

Tiempo para primer contenido: < 100ms

Implementacion Practica

// page.jsx - Partial Prerendering en accion
import { Suspense } from 'react';

export default function ProductPage({ params }) {
  return (
    <main>
      {/* Parte estatica - renderizada en el build */}
      <Header />
      <ProductDetails id={params.id} />

      {/* Parte dinamica - streaming */}
      <Suspense fallback={<ReviewsSkeleton />}>
        <Reviews productId={params.id} />
      </Suspense>

      <Suspense fallback={<RecommendationsSkeleton />}>
        <Recommendations userId={getCurrentUser()} />
      </Suspense>

      {/* Estatico nuevamente */}
      <Footer />
    </main>
  );
}

Metricas de Rendimiento

Antes de PPR:

TTFB: 800ms
FCP: 2.1s
LCP: 3.2s
TTI: 4.5s

Despues de PPR:

TTFB: 50ms
FCP: 150ms
LCP: 800ms
TTI: 1.2s

Empresas que adoptaron PPR reportan mejoras masivas en Core Web Vitals.

React Compiler: Memoizacion Automatica

Recuerdas toda esa complejidad con useMemo, useCallback y React.memo? El React Compiler se encarga de eso automaticamente.

Antes: Memoizacion Manual

// El infierno de la memoizacion manual
const MemoizedList = React.memo(function List({ items, onItemClick }) {
  const sortedItems = useMemo(
    () => items.sort((a, b) => a.name.localeCompare(b.name)),
    [items]
  );

  const handleClick = useCallback(
    (id) => {
      onItemClick(id);
    },
    [onItemClick]
  );

  return (
    <ul>
      {sortedItems.map(item => (
        <ListItem
          key={item.id}
          item={item}
          onClick={handleClick}
        />
      ))}
    </ul>
  );
});

const ListItem = React.memo(function ListItem({ item, onClick }) {
  return (
    <li onClick={() => onClick(item.id)}>
      {item.name}
    </li>
  );
});

Despues: Codigo Limpio

// React Compiler se encarga de la optimizacion
function List({ items, onItemClick }) {
  const sortedItems = items.sort((a, b) =>
    a.name.localeCompare(b.name)
  );

  return (
    <ul>
      {sortedItems.map(item => (
        <ListItem
          key={item.id}
          item={item}
          onClick={() => onItemClick(item.id)}
        />
      ))}
    </ul>
  );
}

function ListItem({ item, onClick }) {
  return (
    <li onClick={onClick}>
      {item.name}
    </li>
  );
}

// Mismo rendimiento, codigo 60% menor

Activando el Compiler

// babel.config.js
module.exports = {
  plugins: [
    ['babel-plugin-react-compiler', {
      // Opciones
    }]
  ]
};

// O en Next.js 15+
// next.config.js
module.exports = {
  experimental: {
    reactCompiler: true
  }
};

Asset Loading API

Nueva API para control fino sobre la carga de recursos.

Priorizacion Inteligente

import { preload, prefetch, preinit } from 'react-dom';

function App() {
  // Preload critico - carga inmediatamente
  preload('/critical-image.jpg', { as: 'image' });

  // Prefetch - carga cuando este idle
  prefetch('/next-page-data.json', { as: 'fetch' });

  // Preinit - inicializa script
  preinit('/analytics.js', { as: 'script' });

  return <MainContent />;
}

Carga Condicional

function ProductGallery({ products }) {
  return (
    <div>
      {products.map((product, index) => {
        // Primeras 4 imagenes: prioridad alta
        if (index < 4) {
          preload(product.image, {
            as: 'image',
            fetchPriority: 'high'
          });
        }

        return <ProductCard key={product.id} product={product} />;
      })}
    </div>
  );
}

Hooks Mejorados

React 20 trae mejoras significativas en los hooks existentes.

useDeferredValue Expandido

function SearchResults({ query }) {
  // Defer actualizaciones no urgentes
  const deferredQuery = useDeferredValue(query);
  const isStale = query !== deferredQuery;

  return (
    <div style={{ opacity: isStale ? 0.7 : 1 }}>
      <Results query={deferredQuery} />
    </div>
  );
}

useOptimistic Para UI Optimista

function TodoList({ todos, addTodo }) {
  const [optimisticTodos, addOptimisticTodo] = useOptimistic(
    todos,
    (state, newTodo) => [...state, { ...newTodo, pending: true }]
  );

  async function handleAdd(formData) {
    const newTodo = { text: formData.get('text') };

    // La UI actualiza inmediatamente
    addOptimisticTodo(newTodo);

    // El servidor procesa en background
    await addTodo(newTodo);
  }

  return (
    <form action={handleAdd}>
      <input name="text" />
      <ul>
        {optimisticTodos.map(todo => (
          <li
            key={todo.id}
            style={{ opacity: todo.pending ? 0.5 : 1 }}
          >
            {todo.text}
          </li>
        ))}
      </ul>
    </form>
  );
}

Migracion de React 19 a 20

La migracion es relativamente suave, pero hay puntos de atencion.

Breaking Changes

// 1. forwardRef ya no es necesario
// Antes
const Input = forwardRef((props, ref) => (
  <input ref={ref} {...props} />
));

// Despues
function Input({ ref, ...props }) {
  return <input ref={ref} {...props} />;
}

// 2. Context como provider directo
// Antes
<ThemeContext.Provider value={theme}>

// Despues
<ThemeContext value={theme}>

Checklist de Migracion

# 1. Actualiza dependencias
npm install react@20 react-dom@20

# 2. Actualiza tipos TypeScript
npm install @types/react@20

# 3. Ejecuta codemods
npx @react-codemod/update-react-imports

# 4. Verifica deprecations
npx react-upgrade-check

React Server Components Maduros

RSC salieron de experimental a estandar de produccion.

Arquitectura Recomendada

app/
├── layout.jsx          # Server Component
├── page.jsx            # Server Component
├── components/
│   ├── Header.jsx      # Server Component
│   ├── Footer.jsx      # Server Component
│   └── client/
│       ├── Form.jsx    # 'use client'
│       └── Modal.jsx   # 'use client'
└── actions/
    └── submit.js       # 'use server'

Regla de Oro

// Por defecto: Server Components
// - No necesitan directiva
// - Pueden acceder a base de datos directamente
// - No agregan JS al bundle del cliente

// Client Components: solo cuando sea necesario
// - Interactividad (onClick, onChange)
// - Hooks de navegador (useState, useEffect)
// - APIs del navegador (localStorage, etc)

'use client' // Solo cuando realmente lo necesites

Rendimiento: Numeros Reales

Comparativas de proyectos migrados a React 20.

Bundle Size

React 19: 142 KB (gzipped)
React 20: 128 KB (gzipped)

Reduccion: 10%

Core Web Vitals (Promedio)

Metrica    | React 19 | React 20 | Mejora
-----------|----------|----------|----------
LCP        | 2.1s     | 0.9s     | -57%
FID        | 95ms     | 42ms     | -56%
CLS        | 0.12     | 0.05     | -58%
INP        | 180ms    | 75ms     | -58%

Uso de Memoria

Aplicacion promedio:
React 19: 45MB heap
React 20: 32MB heap

Reduccion: 29%

Lo Que Viene

React 20 es solo el comienzo. El roadmap indica:

React 21 (Preview)

- Activity API (sustituye StrictMode)
- Soporte de animaciones integrado
- Mejoras en Suspense
- Offscreen rendering

Ecosistema

- Next.js 16 con PPR por defecto
- Remix adopcion total de RSC
- React Native con nueva arquitectura

Conclusion

React 20 no es solo una actualizacion, es una reimaginacion de como construimos aplicaciones web. Server Actions eliminan boilerplate, PPR acaba con pantallas blancas, y el Compiler elimina la complejidad de la optimizacion manual.

Para desarrolladores, significa codigo mas simple y rendimiento mejor. Para usuarios, significa aplicaciones mas rapidas y responsivas.

Si aun no empezaste a migrar, ahora es el momento. El futuro de React es server-first, streaming-enabled y automaticamente optimizado.

Si quieres seguir acompañando las novedades del ecosistema, te recomiendo darle una mirada a otro articulo: TypeScript 7 Nativo en Go: 10x Mas Rapido donde exploramos otro gran cambio que esta transformando el desarrollo frontend.

Vamos con todo! 🦅

Comentarios (0)

Este artículo aún no tiene comentarios 😢. ¡Sé el primero! 🚀🦅

Añadir comentarios