prerender renderiza uma árvore React em uma string HTML estática usando um Web Stream.

const {prelude} = await prerender(reactNode, options?)

Note

Esta API depende de Web Streams. Para Node.js, use prerenderToNodeStream em vez disso.


Referência

prerender(reactNode, options?)

Chame prerender para renderizar seu app em HTML estático.

import { prerender } from 'react-dom/static';

async function handler(request) {
const {prelude} = await prerender(<App />, {
bootstrapScripts: ['/main.js']
});
return new Response(prelude, {
headers: { 'content-type': 'text/html' },
});
}

No cliente, chame hydrateRoot para tornar o HTML gerado pelo servidor interativo.

Veja mais exemplos abaixo.

Parâmetros

  • reactNode: Um nó React que você quer renderizar em HTML. Por exemplo, um nó JSX como <App />. Espera-se que ele represente o documento inteiro, então o componente App deve renderizar a tag <html>.

  • opcional options: Um objeto com opções de geração estática.

    • opcional bootstrapScriptContent: Se especificado, esta string será colocada em uma tag <script> inline.
    • opcional bootstrapScripts: Uma array de URLs de string para as tags <script> a serem emitidas na página. Use isso para incluir o <script> que chama hydrateRoot. Omita-o se você não quiser executar o React no cliente.
    • opcional bootstrapModules: Como bootstrapScripts, mas emite <script type="module"> em vez disso.
    • opcional identifierPrefix: Um prefixo de string que o React usa para IDs gerados por useId. Útil para evitar conflitos ao usar múltiplos roots na mesma página. Deve ser o mesmo prefixo do que aquele passado para hydrateRoot.
    • opcional namespaceURI: Uma string com a raiz URI do namespace para o fluxo. O padrão é HTML comum. Passe 'http://www.w3.org/2000/svg' para SVG ou 'http://www.w3.org/1998/Math/MathML' para MathML.
    • opcional onError: Um retorno de chamada que é disparado sempre que há um erro de servidor, seja ele recuperável ou não.. Por padrão, isso chama apenas console.error. Se você substituí-lo para registrar relatórios de falhas,, certifique-se de ainda chamar console.error. Você também pode usá-lo para ajustar o código de status antes da emissão do shell.
    • opcional progressiveChunkSize: O número de bytes em um bloco. Saiba mais sobre a heurística padrão.
    • opcional signal: Um sinal de aborto que permite abortar a renderização do servidor e renderizar o restante no cliente.

Retorna

prerender retorna uma Promise:

  • Se a renderização for bem-sucedida, a Promise resolverá para um objeto contendo:
    • prelude: um Web Stream de HTML. Você pode usar este stream para enviar uma resposta em chunks, ou você pode ler todo o stream em uma string.
  • Se a renderização falhar, a Promise será rejeitada. Use isso para gerar um shell de fallback.

Note

Quando devo usar prerender?

A API estática prerender é usada para geração estática do lado do servidor (SSG). Diferente de renderToString, prerender aguarda todo o carregamento dos dados antes de resolver. Isso o torna adequado para gerar HTML estático para uma página inteira, incluindo dados que precisam ser buscados usando Suspense. Para transmitir conteúdo conforme ele carrega, use uma API de renderização do lado do servidor (SSR) por streaming como renderToReadableStream.


Uso

Renderizando uma árvore React em um stream de HTML estático

Chame prerender para renderizar sua árvore React em HTML estático em um Readable Web Stream::

import { prerender } from 'react-dom/static';

async function handler(request) {
const {prelude} = await prerender(<App />, {
bootstrapScripts: ['/main.js']
});
return new Response(prelude, {
headers: { 'content-type': 'text/html' },
});
}

Junto com o componente root, você precisa fornecer uma lista de caminhos de <script> de bootstrap. Seu componente root deve retornar todo o documento incluindo a tag <html> root.

Por exemplo, pode se parecer com isso:

export default function App() {
return (
<html>
<head>
<meta charSet="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="stylesheet" href="/styles.css"></link>
<title>My app</title>
</head>
<body>
<Router />
</body>
</html>
);
}

O React vai injetar o doctype e suas tags de <script> de bootstrap no stream HTML resultante:

<!DOCTYPE html>
<html>
<!-- ... HTML from your components ... -->
</html>
<script src="/main.js" async=""></script>

No cliente, seu script de bootstrap deve hidratar todo o document com uma chamada para hydrateRoot:

import { hydrateRoot } from 'react-dom/client';
import App from './App.js';

hydrateRoot(document, <App />);

Isso vai anexar event listeners ao HTML estático gerado pelo servidor e torná-lo interativo.

Deep Dive

Lendo os caminhos de assets CSS e JS da saída da build

As URLs dos assets finais (como arquivos JavaScript e CSS) são frequentemente hasheadas após a build. Por exemplo, em vez de styles.css você pode acabar com styles.123456.css. Hashear nomes de arquivos de assets estáticos garante que cada build distinto do mesmo asset terá um nome de arquivo diferente. Isso é útil porque permite que você habilite o cache de longo prazo com segurança para ativos estáticos: um arquivo com um determinado nome nunca mudaria o conteúdo.

No entanto, se você não souber as URLs dos assets até depois da build, não haverá como colocá-los no código-fonte. Por exemplo, codificar "/styles.css" em JSX como antes não funcionaria. Para mantê-los fora do seu código-fonte, seu componente root pode ler os nomes reais dos arquivos de um mapa passado como uma prop:

export default function App({ assetMap }) {
return (
<html>
<head>
<title>My app</title>
<link rel="stylesheet" href={assetMap['styles.css']}></link>
</head>
...
</html>
);
}

No servidor, renderize <App assetMap={assetMap} /> e passe o seu assetMap com as URLs dos assets:

// Você precisa obter este JSON de sua ferramenta de build, por exemplo, ler da saída da build.
const assetMap = {
'styles.css': '/styles.123456.css',
'main.js': '/main.123456.js'
};

async function handler(request) {
const {prelude} = await prerender(<App assetMap={assetMap} />, {
bootstrapScripts: [assetMap['/main.js']]
});
return new Response(prelude, {
headers: { 'content-type': 'text/html' },
});
}

Como seu servidor agora está renderizando <App assetMap={assetMap} />, você precisa renderizá-lo com assetMap no cliente também para evitar erros de hidratação. Você pode serializar e passar assetMap para o cliente assim:

// Você precisa obter este JSON de sua ferramenta de build.
const assetMap = {
'styles.css': '/styles.123456.css',
'main.js': '/main.123456.js'
};

async function handler(request) {
const {prelude} = await prerender(<App assetMap={assetMap} />, {
// Cuidado: é seguro stringificar() isto porque estes dados não são gerados pelo usuário.
bootstrapScriptContent: `window.assetMap = ${JSON.stringify(assetMap)};`,
bootstrapScripts: [assetMap['/main.js']],
});
return new Response(prelude, {
headers: { 'content-type': 'text/html' },
});
}

No exemplo acima, a opção bootstrapScriptContent adiciona uma tag <script> inline extra que define a variável global window.assetMap no cliente. Isso permite que o código do cliente leia o mesmo assetMap:

import { hydrateRoot } from 'react-dom/client';
import App from './App.js';

hydrateRoot(document, <App assetMap={window.assetMap} />);

Tanto o cliente quanto o servidor renderizam App com a mesma prop assetMap, então não há erros de hidratação.


Renderizando uma árvore React em uma string de HTML estático

Chame prerender para renderizar seu app em uma string HTML estática:

import { prerender } from 'react-dom/static';

async function renderToString() {
const {prelude} = await prerender(<App />, {
bootstrapScripts: ['/main.js']
});

const reader = prelude.getReader();
let content = '';
while (true) {
const {done, value} = await reader.read();
if (done) {
return content;
}
content += Buffer.from(value).toString('utf8');
}
}

Isso irá produzir a saída HTML inicial não interativa de seus componentes React. No cliente, você precisará chamar hydrateRoot para hidratar aquele HTML gerado pelo servidor e torná-lo interativo.


Esperando todos os dados carregarem

prerender espera todos os dados carregarem antes de finalizar a geração do HTML estático e resolver. Por exemplo, considere uma página de perfil que mostra uma capa, uma barra lateral com amigos e fotos e uma lista de posts:

function ProfilePage() {
return (
<ProfileLayout>
<ProfileCover />
<Sidebar>
<Friends />
<Photos />
</Sidebar>
<Suspense fallback={<PostsGlimmer />}>
<Posts />
</Suspense>
</ProfileLayout>
);
}

Imagine que <Posts /> precisa carregar alguns dados, o que leva algum tempo. Idealmente, você gostaria de esperar até os posts terminarem para que fossem incluídos no HTML. Para fazer isso, você pode usar Suspense para suspender nos dados, e prerender vai esperar o conteúdo suspenso acabar antes de resolver o HTML estático.

Note

Somente fontes de dados habilitadas para Suspense ativarão o componente Suspense. Elas incluem:

  • Busca de dados com frameworks habilitados para Suspense como Relay e Next.js
  • Carregamento lento do código do componente com lazy
  • Lendo o valor de uma Promise com use

Suspense não detecta quando os dados são buscados dentro de um Effect ou manipulador de eventos.

A maneira exata de como você carregaria dados no componente Posts acima depende do seu framework. Se você usa um framework habilitado para Suspense, você encontrará os detalhes na documentação de busca de dados dele.

A busca de dados habilitada para Suspense sem o uso de um framework que dê opiniões ainda não é suportada. Os requisitos para implementar uma fonte de dados habilitada para Suspense são instáveis e não documentados. Uma API oficial para integrar fontes de dados com Suspense será lançada em uma versão futura do React.


Solução de problemas

Meu stream não começa até que o app inteiro seja renderizado

A resposta de prerender espera a renderização completa de todo o aplicativo, incluindo a espera pela resolução de todos os limites de Suspense, antes de resolver. Ele é projetado para a geração estática de sites (SSG) com antecedência e não suporta o streaming de mais conteúdo conforme ele carrega.

Para transmitir conteúdo conforme ele carrega, use uma API de renderização de servidor por streaming como renderToReadableStream.