# SSR and hydration

Server render contract, Declarative Shadow DOM, hydration state, and React/Vue helpers.

See versions, changes and requirements in [Framework 4.0 and CLI 1.2](/docs/guides/releases/).


<span id="qué-produce-el-servidor"></span>

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

## What the server produces

`wu-framework/server` transforms a renderer's result into WU container HTML. By default, it encapsulates content and styles in Declarative Shadow DOM. The browser can materialize that tree before the app's JavaScript loads.

The server receives a module with `render(props)`, not the client's mounting lifecycle. The result can be an HTML string or `{ html, styles?, state? }`. Whoever generates the HTML must escape untrusted data before including it.

```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` also accepts an already imported module, a promise, a `default` module, or a function that loads it. The host decides which modules may run on the server; do not turn user input into an arbitrary import.

<span id="resultado-y-opciones"></span>

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

## Result and options

| Element | Contract |
| --- | --- |
| `result.html` | Complete container |
| `result.inner` | Declarative or flat interior |
| `result.containerAttrs` | ID, `data-wu-app`, `data-wu-ssr`, optional class |
| `result.state` | Optional state returned by the renderer |
| `containerId` | Defaults to `wu-app-<name>` |
| `shadowMode` | Defaults to `open`; closed prevents normal hydration |
| `declarative` | Defaults to `true` |
| `styles` | Extra host CSS for that app |
| `nonce` | CSP nonce for that response's styles |

`renderApps` processes multiple apps in parallel. By default, it records individual failures in `errors` and preserves the others; `failFast: true` allows aborting. A renderer failing in `renderApp` produces an error with code `WU_SSR_RENDER_FAILED`.

<span id="hidratar-sin-borrar"></span>

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

## Hydrate without erasing

On the client, the shell registers the app normally and mounts it in its SSR container. The sandbox detects `data-wu-ssr` and the `data-wu-root` root. If `lifecycle.hydrate` exists, the core invokes it with server props and the scoped facade. Without that slot, it calls `mount`, and the existing HTML serves as initial content until the renderer replaces it.

```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();
  },
});
```

For React, `hydrateRoot` must receive the same tree/props the server produced. For Vue, the adapter uses `createSSRApp` and `.mount` to adopt the existing DOM. Published state must preserve that correspondence; changing data between rendering and hydration causes mismatches.

<span id="helpers-de-renderer"></span>

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

## Renderer helpers

The server subpath exports `renderReact`, `renderPreact`, `renderVue`, `renderSvelte`, `renderSolid`, and `renderHtml`. Helpers return a module conforming to the `render` contract.

```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; }',
});
```

Injecting the renderer and runtime avoids resolving another React copy outside the server bundle. Vue supports injection of `createSSRApp` and `renderToString`, plus `setup`. Server option names are not identical to the client adapter's `runtime: { React, ReactDOM }`.

<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}
/>
```

The shell must still initialize the corresponding client registration. The component publishes state as JSON for hydration; `hydrate` defaults to `true`. `fallbackToClient` also defaults to `true`: an SSR failure leaves the slot for client mounting unless you disable it as in the example.

In a static Astro build, the renderer runs at build time; it does not turn that page into per-request personalized content. User-dependent data requires an appropriate server or client-loading strategy.

<span id="estado-y-cabeceras"></span>

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

## State and headers

`serializeState` and `renderStateScript` handle serialization for the HTML/script context. `escapeHtml` escapes text; it does not validate an arbitrary HTML application. Do not inject secrets into hydration state: everything published on that page is visible to its reader.

`createNonce()` produces a nonce for one response. `securityHeaders`, `applySecurityHeaders`, `wuSecurityMiddleware`, and `withSecurityHeaders` help install CSP and other headers. Use the same response nonce in styles/scripts that must run and a new nonce for the next response; the server controls that coordination.

<span id="referencia-de-implementación"></span>

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

## Implementation references

Exports: `src/server/index.js`; rendering: `src/server/wu-server-render.js`; multiple renders: `src/server/wu-server-render.js`; types: `src/server/index.d.ts`; DOM adoption: `src/core/wu-sandbox.js`; hydrate slot: `src/core/wu-core.js`; Astro: `integrations/astro/WuAppSSR.astro`. Tests: `tests/unit/server-render.test.js`, `tests/unit/server-security-fuzz.test.js`, `tests/unit/security-headers.test.js`.

To isolate each request and bound work, see `createServerRequest`, private loaders, cancellation and concurrency in the [SSR reference](/docs/framework/api-server/). The 4.0 header wrappers reject a fixed nonce: use a per-request function or the default generation.