---
schema_version: "1"
id: 79da98a4215c1dfd17d44f8d3ae3fa33f77d7d48563a5ce4015bbc6408f6daad
title: Configure Components for Server-Side Rendering (SSR)
collection: developer
version: current
locale: en-us
portal_url: https://docs.salesforce.com/developer/current/en-us/lwr-node/lwr/guides/lwr-configure-component-ssr.md
canonical_url: https://developer.salesforce.com/docs/platform/lwr/guide/lwr-configure-component-ssr.html
source_site: developer.salesforce.com
content_type: documentation
content_hash: 8efc4362f66dd9afd27d9fe4c4176d710d6cea1bf252c8a59cb4b3e3aa2d6f05
products:
  - Lightning Web Runtime
---
# Configure Components for Server-Side Rendering (SSR)

To successfully render a component on the server side, it has to meet all of the following criteria:

1. The functions that execute during SSR must be **portable**.
2. The functions that execute during SSR must be **synchronous**.
3. The component uses **light DOM** (recommended) or **native shadow DOM**.

Follow these steps (in order) to make components SSRable.

## 1. Make Components Portable

A component is **portable** if it can run without browser APIs. Your components can’t have any dependencies on browser APIs because the server isn’t a browser.

During SSR, the Lightning Web Components (LWC) framework executes only these functions for each of your components and all of their imported dependencies:

- `constructor`
- `connectedCallback()`
- getters
- setters
- any other functions called by these functions

Components with non-portable code will break your site. If a component uses browser APIs, you have to modify it to ensure it doesn't assume that those APIs are always available.

For example, you should remove the following non-portable code and objects from components that you want to SSR.

- `window`
- `document`
- selector functions (such as `querySelector` and `querySelectorAll`)
- JavaScript eventing

### Detect Non-Portable Code with the ESLint Plugin

The LWC [ESLint Plugin](https://github.com/salesforce/eslint-plugin-lwc/tree/master) helps you determine if your components follow best practices for API or method usage. The plugin's [SSR preset](https://github.com/salesforce/eslint-config-lwc?tab=readme-ov-file#salesforceeslint-config-lwcssr-configuration), `@salesforce/eslint-config-lwc/ssr`, lets you identify non-portable components.

You can configure the plugin with rules from [this list](https://github.com/salesforce/eslint-plugin-lwc/blob/master/README.md#lwc). For example, here are a few rules you should enable to identify non-portable components.

- Disallow access to global browser APIs during SSR (`lwc/no-restricted-browser-globals-during-ssr`)
- Disallow access to unsupported properties on `this` during SSR (`lwc/no-unsupported-ssr-properties`)
- Disallow usage of `process.env.NODE_ENV` in SSR (`lwc/no-node-env-in-ssr`)

Additionally, the plugin can help you identify non-portable utilities and libraries. If a library isn't portable, your page won't load correctly during SSR.

### Avoid Certain Salesforce Scoped Modules

The following Salesforce scoped modules are deprecated during SSR:

- `@salesforce/user/Id`
- `@salesforce/user/isGuest`
- `@salesforce/userPermission/*`
- `@salesforce/customPermission/*`

For LWR Node apps, we recommend using the [`getServerData` hook](https://developer.salesforce.com/docs/platform/lwr/guide/lwr-enable-ssr-site.md#load-data-during-ssr) to fetch information from these modules instead.

If a component has to use one of these scoped modules on the server side, render a placeholder to avoid negatively impacting [web vitals](https://web.dev/articles/vitals).

If a component must use one of these scoped modules on the client side, [dynamically import](https://developer.salesforce.com/docs/platform/lwr/guide/lwr-portable-best-practices.md#dynamically-import-non-portable-modules) the module after hydration. We don't recommend this approach.

### Use Portable Styling

To style components and set image URLs, use code that's executed on both the server and the client. Styling only on the client side can cause UI shifting and hydration warnings.

LWC logs a hydration warning if an SSRed component's HTML doesn't match the output of its first rendering cycle on the client. Hydration warnings cause unexpected UI shifting while the LWC framework recovers from the mismatch.

To avoid hydration warnings, make sure that component template updates are triggered only by asynchronous actions, like user interaction, data updates, and [eventing](https://developer.salesforce.com/docs/platform/lwc/guide/events.html).

```
import { LightningElement, api } from 'lwc';
import basePath from '@salesforce/community/basePath';

export default class Logo extends LightningElement {
    static renderMode = 'light';
    @api highlight = false;
    @api borderWidth = '1';

    renderedCallback() {
        // this code only runs on the client
        const image = this.querySelector('img.logo');
        image?.setAttribute('src', getLogoUrl(basePath));
        if (this.highlight) image?.classList.add('highlight');
        image?.style.setProperty('border-width', `${this.borderWidth}px`);
    }
}
```

```
<!-- before -->
<template>
    <img src="" class="logo" />
</template>
```

```
import { LightningElement, api } from 'lwc';
import basePath from '@salesforce/community/basePath';

export default class Logo extends LightningElement {
    static renderMode = 'light';
    @api highlight = false;
    @api borderWidth = '1';

    // this code works on both the server and client
    // this component is now static and no longer requires hydration
    get url() {
        return getLogoUrl(basePath);
    }
    get className() {
        return this.highlight ? 'logo highlight' : 'logo';
    }
    get borderStyle() {
        return `border-width:${this.borderWidth}px`;
    }
}
```

```
<template>
    <img src="{url}" class="{className}" style="{borderStyle}" />
</template>
```

## 2. Refactor Asynchronous Code

Since the SSR process for Lightning web components (LWCs) runs in one synchronous pass, you should only have synchronous code in server-rendered pages and components.

Server-side asynchronous code executes but doesn't complete during SSR. The resulting page won't break, but it can render unexpected content that's difficult to debug.

Asynchronous code includes:

- [`Promise` objects](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)
- [`async/await` keywords](https://www.w3schools.com/js/js_async.asp)
- [dynamic imports](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/import)

However, server-rendered pages and components can include asynchronous code that doesn't execute on the server. For example, you can include asynchronous code in `renderedCallBack()` or event handlers, but you can't add it to `connectedCallback()` or getters. To test if asynchronous code is unsafe, use the [ESLint plugin](#detect-non-portable-code-with-the-eslint-plugin).

## 3. Enable Light DOM or Native Shadow DOM

By default, server-rendered components are rendered in native shadow DOM. However, we recommend using light DOM instead.

### Light DOM

[Light DOM](https://developer.salesforce.com/docs/platform/lwc/guide/create-light-dom.html) eases third-party integrations (like Google Analytics) and global styling. Enabling light DOM ensures that LWC renders regular HTML markup instead of creating a native web component. The markup is also referred to as light DOM since it isn’t contained within a shadow root.

To enable light DOM on your Lightning web component, use the `renderMode` static property.

```
import { LightningElement } from 'lwc';
export default class Heading extends LightningElement {
  static renderMode = 'light';
  @api text;
}
```

In your template, use the `lwc:render-mode` directive.

```
<template lwc:render-mode="light">
  <h2 class="global-style">{text}</h2>
</template>
```

### Shadow DOM

Shadow DOM encapsulates a component's internals and styling. Server-rendered components can use native shadow DOM instead of light DOM.

Synthetic shadow DOM is **not** supported due to [limitations on browser API usage](https://developer.salesforce.com/docs/platform/lwr/guide/lwr-portable-best-practices.md#protect-your-code-from-accessing-general-browser-apis).

## Next Steps

After you've configured a component for SSR, make sure it follows the [Best Practices for Portable Components](https://developer.salesforce.com/docs/platform/lwr/guide/lwr-portable-best-practices.md).

**See Also**

- [Test Your Components](https://developer.salesforce.com/docs/platform/lwr/guide/lwr-ssr-components-test.md)
- [Lightning Web Components: Light DOM](https://developer.salesforce.com/docs/component-library/documentation/en/lwc/lwc.create_light_dom)
