You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
update SSR documentation to show prerenderStatic and link to integration packages (#12958)
* update SSR documentation to show `prerenderStatic` and link to integration packages
* Apply suggestions from code review
Co-authored-by: Jerel Miller <[email protected]>
* Apply suggestions from code review
* Update docs/source/performance/server-side-rendering.mdx
* prettier
* bold -> h5
---------
Co-authored-by: Jerel Miller <[email protected]>
This page describes manual server side rendering setups with React. If you are using a modern SSR-focused framework like [Next.js](https://nextjs.org/), [React Router Framework mode](https://reactrouter.com/en/main/routers/framework), or [TanStack Start](https://start.tanstack.com/), you need to use one of our Apollo Client Framework integrations instead.
7
+
You can find these integrations [on GitHub](https://github.com/apollographql/apollo-client-integrations):
**Server-side rendering** (**SSR**) is a performance optimization for modern web apps. It enables you to render your app's initial state to raw HTML and CSS on the server _before_ serving it to a browser. This means users don't have to wait for their browser to download and initialize React (or Angular, Vue, etc.) before content is available:
6
16
7
17
```mermaid
@@ -34,12 +44,12 @@ When you render your React app on the server side, _most_ of the code is identic
34
44
35
45
Here's an example _server-side_ initialization of Apollo Client:
@@ -50,13 +60,9 @@ const client = new ApolloClient({
50
60
});
51
61
```
52
62
53
-
You'll notice a couple differences from a typical client-side initialization:
54
-
55
-
- You provide `ssrMode: true`. This prevents Apollo Client from refetching queries unnecessarily, and it also enables you to use the `getDataFromTree` function (covered below).
63
+
Provide `ssrMode: true` to prevent Apollo Client from polling on the server. This setting also tells the client to prioritize cache values over network requests when possible.
56
64
57
-
- Instead of providing a `uri` option, you provide an `HttpLink` instance to the `link` option. This enables you to specify any required authentication details when sending requests to your GraphQL endpoint from the server side.
58
-
59
-
Note that you also might need to make sure your GraphQL endpoint is configured to accept GraphQL operations from your SSR server (for example, by safelisting its domain or IP).
65
+
You also might need to configure your GraphQL endpoint to accept GraphQL operations from your SSR server (for example, by safelisting its domain or IP). Use absolute URLs for your GraphQL endpoint on the server, because relative network requests can only be made in a browser.
60
66
61
67
> It's possible and valid for your GraphQL endpoint to be hosted by the _same server_ that's performing SSR. In this case, Apollo Client doesn't need to make network requests to execute queries. For details, see [Avoiding the network for local queries](#avoiding-the-network-for-local-queries).
62
68
@@ -69,7 +75,7 @@ First, here's an example `app.js` file, _without_ the code for rendering React t
@@ -121,62 +127,209 @@ It's important to create an _entirely new instance_ of Apollo Client for each re
121
127
122
128
</Caution>
123
129
124
-
### Executing queries with `getDataFromTree`
130
+
### Executing queries with `prerenderStatic`
131
+
132
+
You can instruct Apollo Client to execute all of the queries executed by the `useQuery` or the suspenseful query hooks (like `useSuspenseQuery` and `useBackgroundQuery`) in the React tree's components with the `prerenderStatic` function.
125
133
126
-
Because our app uses Apollo Client, some of the components in the React tree probably execute a GraphQL query with the `useQuery` hook. We can instruct Apollo Client to execute _all_ of the queries required by the tree's components with the `getDataFromTree` function.
134
+
This function rerenders your React tree until no more network requests are made. When you use suspenseful hooks with a suspense-ready rendering function, the tree is rendered once and suspends while network requests are executed. When you use non-suspenseful hooks (like `useQuery`), this function renders all components, waits for all requests to finish, and then re-renders the tree until no more requests are made.
127
135
128
-
This function walks down the entire tree and executes every required query it encounters (including nested queries). It returns a `Promise` that resolves when all result data is ready in the Apollo Client cache.
136
+
The function returns a `Promise` that resolves when all result data is ready in the Apollo Client cache and the final render is complete.
129
137
130
-
When the `Promise` resolves, you're ready to render your React tree and return it, along with the current state of the Apollo Client cache.
138
+
#### Choosing a rendering function
131
139
132
-
> Note that if you are rendering your React tree directly to a string (instead of the component-based example below), you will need to use [`renderToStringWithData`](../api/react/ssr/#rendertostringwithdata) instead of `getDataFromTree`. This will ensure the client-side React hydration works correctly by using [`ReactDOMServer.renderToString`](https://react.dev/reference/react-dom/server/renderToString) to generate the string.
-**`prerender`** from `react-dom/static` - Recommended for Deno or modern edge runtimes with Web Streams. Supports React Suspense.
143
+
-**`prerenderToNodeStream`** from `react-dom/static` - Recommended for Node.js. Supports React Suspense.
144
+
-**`renderToString`** from `react-dom/server` - Legacy API without Suspense support. Won't work with suspenseful hooks.
145
+
-**`renderToStaticMarkup`** from `react-dom/server` - Legacy API without Suspense support. Slightly faster than `renderToString`, but the result cannot be hydrated.
133
146
134
147
The following code replaces the `TODO` comment within the `app.use` call in the example above:
After `prerenderStatic` completes, you need to send the HTML response to the client. The approach depends on the rendering function you chose.
171
+
172
+
##### Using streaming with `renderToPipeableStream`
173
+
174
+
For Node.js environments, you can stream the response to the client using `renderToPipeableStream`. This allows the browser to start displaying content before the entire page is rendered:
The definition of the top-level `Html` component that's rendered to static markup might look like this:
218
+
<Note>
219
+
Instead of rendering `<div id="root"><App /></div>`, you can also render `<div id="root" dangerouslySetInnerHTML={{ __html: result }} />` to avoid rendering the entire tree twice.
220
+
However, if you go this approach, React cannot decide on the order in which components are streamed to the browser, which might be suboptimal.
221
+
In the end, it's a tradeoff between more work on the server and a potential delay in displaying content in the browser that you need to make based on your individual requirements.
222
+
</Note>
223
+
224
+
<Tip>
225
+
If you are only using suspenseful hooks (like `useSuspenseQuery` or `useBackgroundQuery`) and no `useQuery` hooks, is also possible to use the [@apollo/client-react-streaming](https://github.com/apollographql/apollo-client-integrations/tree/main/packages/client-react-streaming) package to stream data dynamically while it is fetching, without requiring a first render pass with `prerenderStatic`.
226
+
You can find an example application that uses this approach in the [integration tests for that package](https://github.com/apollographql/apollo-client-integrations/tree/main/integration-test/vite-streaming).
227
+
</Tip>
228
+
229
+
<Note>
230
+
The `replace` call in these examples escapes the `<` character to prevent cross-site scripting attacks that are possible via the presence of `</script>` in a string literal.
231
+
</Note>
156
232
157
-
```jsx title="components/html.js"
158
-
exportfunctionHtml({ content, state }) {
159
-
return (
233
+
##### Using `renderToString` for synchronous rendering
234
+
235
+
For simpler use cases or environments without stream support, you can use `renderToString` to render the entire page synchronously:
236
+
237
+
```js title="app.js"
238
+
import { renderToString } from"react-dom/server";
239
+
240
+
// After prerenderStatic completes
241
+
prerenderStatic({
242
+
tree: App,
243
+
context: { client },
244
+
renderFunction: renderToString,
245
+
}).then(async ({ result }) => {
246
+
constinitialState=client.extract();
247
+
248
+
// Create a complete HTML document with the cache state
The `replace` call in these examples escapes the `<` character to prevent cross-site scripting attacks that are possible via the presence of `</script>` in a string literal.
280
+
</Note>
281
+
282
+
#### Advanced options
283
+
284
+
`prerenderStatic` provides several options to customize the rendering process:
285
+
286
+
##### Diagnostics
287
+
288
+
You can enable diagnostics to detect inefficient rendering structures (like `useQuery` waterfalls) in your app:
This results in the rendered React tree being added as a child of the `root``div`, and the initial cache state is assigned to the `__APOLLO_STATE__` global object.
321
+
##### Maximum rerenders
178
322
179
-
> The `replace` call in this example escapes the `<` character to prevent cross-site scripting attacks that are possible via the presence of `</script>` in a string literal.
323
+
If you have deep `useQuery` waterfalls, you can increase the `maxRerenders` option (default: 50):
324
+
325
+
```js
326
+
awaitprerenderStatic({
327
+
tree: App,
328
+
context: { client },
329
+
renderFunction: prerenderToNodeStream,
330
+
maxRerenders:100,
331
+
});
332
+
```
180
333
181
334
### Rehydrating the client-side cache
182
335
@@ -209,7 +362,7 @@ const client = new ApolloClient({
209
362
210
363
If your GraphQL endpoint is hosted by the same server that you're rendering from, you can optionally avoid using the network when executing your SSR queries. This is particularly helpful if `localhost` is firewalled in the server's environment (e.g., on Heroku).
211
364
212
-
One option is to use Apollo Link to fetch data using a local GraphQL schema instead of making a network request. To achieve this, when creating an Apollo Client on the server, you could use a [SchemaLink](../api/link/apollo-link-schema/) instead of using `createHttpLink`. `SchemaLink` uses your schema and context to run the query immediately, without any additional network requests:
365
+
When creating an Apollo Client on the server, use a [SchemaLink](../api/link/apollo-link-schema/) instead of an `HttpLink`. `SchemaLink` uses your schema and context to run the query immediately, without the need for a network request:
@@ -218,20 +371,41 @@ import { SchemaLink } from "@apollo/client/link/schema";
218
371
// ...
219
372
220
373
constclient=newApolloClient({
221
-
ssrMode:true,
222
-
// Instead of "createHttpLink" use SchemaLink here
374
+
// Instead of HttpLink use SchemaLink here
223
375
link:newSchemaLink({ schema }),
224
376
cache:newInMemoryCache(),
225
377
});
226
378
```
227
379
228
-
## Skipping a query
380
+
## Selectively disabling query execution during SSR
229
381
230
-
If you want to intentionally skip a particular query during SSR, you can include `ssr: false` in that query's options. Typically, this means the component is rendered in its "loading" state on the server. For example:
382
+
If you want to prevent a particular query from executing during SSR, use `ssr: false` in that query's options. This is useful for queries that should only run on the client side, such as user-specific data that isn't available during SSR.
383
+
384
+
When `ssr: false` is set, the component receives a result with `loading: true`, `dataState: "empty"`, and `data: undefined` during server-side rendering. The query will execute normally once the component hydrates on the client.
231
385
232
386
```jsx
233
-
functionwithClientOnlyUser() {
234
-
useQuery(GET_USER_WITH_ID, { ssr:false });
235
-
return<span>My query won't be run on the server</span>;
387
+
functionClientOnlyUser() {
388
+
const { loading, data } =useQuery(GET_USER_WITH_ID, { ssr:false });
`ssr:false` behaves differently than `skip:true`, which prevents the query from executing on both the server and client until `skip` is set to `false`. During SSR, `skip:true` results in `loading:false` and `networkStatus:NetworkStatus.ready`.
401
+
402
+
</Note>
403
+
404
+
## Legacy APIs
405
+
406
+
Apollo Client provides two legacy SSR functions that are both replaced by `prerenderStatic`:
407
+
408
+
- **`getDataFromTree`** - Executes all queries in a component tree and returns when data is ready. Uses `renderToStaticMarkup` by default.
409
+
- **`renderToStringWithData`** - Similar to `getDataFromTree`, but returns the rendered string using `renderToString`.
410
+
411
+
These functions are deprecated. Use `prerenderStatic` instead, which offers better flexibility and performance with modern React rendering APIs.
0 commit comments