# SSR e hidratación

Contrato render del servidor, Declarative Shadow DOM, estado de hidratación y helpers React/Vue.

Consulta las versiones, cambios y requisitos en [Framework 4.0 y CLI 1.2](/es/docs/guides/releases/).


<span class="section-anchor" id="section-1"></span>

## Qué produce el servidor

`wu-framework/server` transforma el resultado de un renderer en el HTML de un contenedor WU. Por defecto encapsula contenido y estilos en Declarative Shadow DOM. El navegador puede materializar ese árbol antes de que cargue el JavaScript de la app.

El servidor recibe un módulo con `render(props)`, no el lifecycle de montaje del cliente. El resultado puede ser un string HTML o `{ html, styles?, state? }`. Quien genera el HTML debe escapar los datos no confiables antes de incorporarlos.

```js
import { renderApp, escapeHtml, renderStateScript } from 'wu-framework/server';

const result = await renderApp('greeting', {
  props: { name: 'Ada' },
  module: {
    render(props) {
      return {
        html: `<p>Hola, ${escapeHtml(props.name)}</p>`,
        styles: 'p { color: #f97316; }',
        state: props,
      };
    },
  },
});

const html = result.html + renderStateScript({ greeting: result.state });
```

`renderApp` también acepta un módulo ya importado, una promesa, un módulo `default` o una función que lo carga. El host decide qué módulos puede ejecutar en el servidor; no conviertas un input de usuario en un import arbitrario.

<span class="section-anchor" id="section-2"></span>

## Resultado y opciones

| Elemento | Contrato |
| --- | --- |
| `result.html` | Contenedor completo |
| `result.inner` | Interior declarativo o plano |
| `result.containerAttrs` | ID, `data-wu-app`, `data-wu-ssr`, clase opcional |
| `result.state` | Estado opcional devuelto por renderer |
| `containerId` | Por defecto `wu-app-<name>` |
| `shadowMode` | `open` por defecto; closed impide hidratación normal |
| `declarative` | `true` por defecto |
| `styles` | CSS extra del host para esa app |
| `nonce` | Nonce CSP para estilos de esa respuesta |

`renderApps` procesa varias apps en paralelo. Por defecto registra fallos individuales en `errors` y conserva las otras; `failFast: true` permite abortar. Un renderer que falla en `renderApp` produce un error con código `WU_SSR_RENDER_FAILED`.

<span class="section-anchor" id="section-3"></span>

## Hidratar sin borrar

En el cliente, el shell registra la app normalmente y monta en su contenedor SSR. El sandbox detecta `data-wu-ssr` y la raíz `data-wu-root`. Si existe `lifecycle.hydrate`, el core lo invoca con las props de servidor y la facade scoped. Sin ese slot llama `mount` y el HTML previo funciona como contenido inicial hasta que el renderer lo reemplace.

```js
wu.define('greeting', {
  mount(container) {
    container.textContent = 'Montaje cliente';
  },
  hydrate(container, { props }) {
    // Adoptar lo que ya existe. No limpiar container primero.
    const paragraph = container.querySelector('p');
    paragraph?.setAttribute('aria-label', `Saludo para ${props.name}`);
  },
  unmount(container) {
    container.replaceChildren();
  },
});
```

Para React, `hydrateRoot` debe recibir el mismo árbol/props que produjo el servidor. Para Vue, el adapter usa `createSSRApp` y `.mount` para adoptar el DOM existente. El estado publicado debe conservar esa correspondencia; cambiar datos entre render e hidratación causa mismatches.

<span class="section-anchor" id="section-4"></span>

## Helpers de renderer

El subpath server exporta `renderReact`, `renderPreact`, `renderVue`, `renderSvelte`, `renderSolid` y `renderHtml`. Los helpers devuelven un módulo conforme al contrato `render`.

```jsx
import * as React from 'react';
import { renderToString } from 'react-dom/server';
import { renderReact } from 'wu-framework/server';
import App from './App.jsx';

export default renderReact(App, {
  react: React,
  renderToString,
  styles: '.greeting { font-weight: 600; }',
});
```

Inyectar renderer y runtime evita resolver otra copia de React fuera del bundle del servidor. Vue admite inyección de `createSSRApp` y `renderToString`, además de `setup`. Los nombres de opciones server no son idénticos a `runtime: { React, ReactDOM }` del adapter cliente.

<span class="section-anchor" id="section-5"></span>

## Astro

```astro
---
import WuAppSSR from 'wu-framework/integrations/astro/WuAppSSR.astro';
import greetingServer from '../apps/greeting/server.js';
---
<WuAppSSR
  name="greeting"
  module={greetingServer}
  props={{ name: 'Ada' }}
  fallbackToClient={false}
/>
```

El shell aún debe inicializar el registro cliente correspondiente. El componente publica estado como JSON para hidratar; `hydrate` es `true` por defecto. `fallbackToClient` también es `true`: un fallo SSR deja el slot para un montaje cliente, salvo que lo desactives como en el ejemplo.

En una build estática de Astro el renderer corre al compilar; no convierte esa página en contenido personalizado por petición. Para datos dependientes del usuario necesitas una estrategia de servidor o carga cliente adecuada.

<span class="section-anchor" id="section-6"></span>

## Estado y cabeceras

`serializeState` y `renderStateScript` resuelven la serialización para el contexto HTML/script. `escapeHtml` escapa texto, no valida una aplicación HTML arbitraria. No inyectes secretos en estado de hidratación: todo lo publicado en esa página es visible para su lector.

`createNonce()` produce un nonce para una respuesta. `securityHeaders`, `applySecurityHeaders`, `wuSecurityMiddleware` y `withSecurityHeaders` ayudan a instalar CSP y otras cabeceras. Usa el mismo nonce de respuesta en estilos/scripts que deban ejecutarse y un nonce nuevo en la siguiente respuesta; el servidor controla esa coordinación.

<span class="section-anchor" id="section-7"></span>

## Referencia de implementación

Exports: `src/server/index.js`; render: `src/server/wu-server-render.js`; varios renders: `src/server/wu-server-render.js`; tipos: `src/server/index.d.ts`; adopción de DOM: `src/core/wu-sandbox.js`; slot hydrate: `src/core/wu-core.js`; Astro: `integrations/astro/WuAppSSR.astro`. Pruebas: `tests/unit/server-render.test.js`, `tests/unit/server-security-fuzz.test.js`, `tests/unit/security-headers.test.js`.

Para aislar cada petición y limitar trabajo, consulta `createServerRequest`, loaders privados, cancelación y concurrencia en la [referencia SSR](/es/docs/framework/api-server/). Los wrappers de cabeceras 4.0 rechazan un nonce fijo: usa una función por petición o la generación predeterminada.