# Enable SSR for LWR Apps

Learn how to activate SSR in your LWR app.

:::note
You have to enable SSR for an app before you can configure [islands architecture](/docs/platform/lwr/guide/lwr-understanding-islands.md).
:::

## Opt-in to SSR

SSR is activated on a per-route basis. To enable SSR for a route, set `bootstrap.ssr` to `true` in `lwr.config.json`. Configure this property for every page that you want to render on the server-side.

```sfdocs-code {"lang":"json", "title": "my-app/lwr.config.json", "src": "" }
{
    "routes": [
        {
            "id": "ssr-page",
            "path": "/category/:category",
            "contentTemplate": "$contentDir/page.html",
            "bootstrap": {
                "ssr": true
            }
        }
    ]
}
```

When a route with `bootstrap.ssr` is requested, LWR uses the LWC `renderComponent()` function to SSR every root component on that page, regardless of whether the page is generated at runtime or pre-built using `generateStaticSite`.

LWR automatically passes root component attributes from a template as [public properties](https://developer.salesforce.com/docs/platform/lwc/guide/reactivity-public.md) during SSR. For example, the following code prompts LWR to pass the public property `{ limit: '10' }` to `my/root`.

```sfdocs-code {"lang":"html", "title": "my-app/src/content/page.html", "src": "" }
<section>
    <!-- "limit" is a template attribute -->
    <my-root limit="10"></my-root>
</section>
```

## SSR Detection

To determine where component code is running, use a script to check the `import.meta.env.SSR` flag. If code runs on the server-side, the flag is `true`. If it runs on the client-side, the flag is [`falsy`](https://developer.mozilla.org/en-US/docs/Glossary/Falsy).

You can use this flag to control where your code runs. For example, this code uses an if-else statement that lets you pivot the runtime environment on the SSR flag.

```sfdocs-code {"lang":"javascript", "title": "", "src": "" }
export default class App extends LightningElement {
    connectedCallback() {
        if (import.meta.env.SSR) {
            // run code on the server
        } else {
            // run code on the client
        }
    }
}
```

## Load Data During SSR

To fetch server-side data for components that rely on external data and resources, use LWR's `getServerData` hook. `getServerData` is only available for **LWR on Node.js-only sites**. During SSR, LWR calls `getServerData()` for every **root component**. Then, the `props` returned by the hook get passed to the root component as public properties during SSR **and** on the client.

Data in `markup` is serialized into the page document as HTML. LWR adds each `markup.link` returned by `getServerData()` to the `<head>` section of the page document.

For example:

```sfdocs-code {"lang":"typescript", "title": "my-app/src/modules/example/bookDetails/bookDetails.ts", "src": "" }
export default class BookDetails extends LightningElement {
    // These public properties are bound to the "props" returned by getServerData() below
    @api data?: Book;
    @api bookId?: string;
}

export async function getServerData(context: SsrRequestContext): Promise<SsrDataResponse> {
    // This is called to fetch data before the root component is SSRed
    const bookId = context.params.bookId; // The "bookId" path param is declared in lwr.config.json
    const response = await fetch(`https://www.googleapis.com/books/v1/volumes/${bookId}`);
    const data = await response.json();
    return {
        props: { data, bookId }, // Return public properties for the BookDetails component
        cache: { ttl: '60s' }, // Cache the page for 1 minute
        markup: {
            links: [
                {
                    // The thumbnail for the book is important, so the page will preload the image
                    href: data.volumeInfo.imageLinks.thumbnail,
                    as: 'image',
                    rel: 'preload',
                    fetchpriority: 'high',
                },
            ],
        },
    };
}
```

### Time-to-Live

`getServerData()` can return a time-to-live (TTL) for a page in `PageDataResponse.cache.ttl`. To prevent your data from becoming stale, LWR uses `ttl` to set the `max-age` of the [`Cache-control`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control#max-age) header on the page. LWR uses the shortest TTL from all sources to set `max-age`.

### `getServerData()` Considerations

- In LWR, the SSR process runs in a secure sandbox. The sandbox supports `globalThis.fetch`, which lets you use `fetch` in `getServerData()` hooks as if the module executes in a browser.
  - Even within `getServerData()` context, your code still has to be portable.
- If `getServerData()` throws an error, the page fails to render and the server returns an HTTP 500 error for the page request. To prevent this, address any errors thrown while fetching data and return default values from `getServerData`.
- The `getServerData()` hook can choose to merge the properties from `SsrRequestContext.props` into its return object, or it can ignore/discard them.
- The author of `getServerData()` is responsible for validating the params and query from `SsrRequestContext` before using them.
- The same props returned by `getServerData()` are passed to the component during server rendering and client hydration.

## Next Steps

Want to implement SSR with more granular control over how specific components on a page get rendered? Take a look at [Understanding Islands](/docs/platform/lwr/guide/lwr-understanding-islands.md) to learn more.
