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 helps you determine if your components follow best practices for API or method usage. The plugin’s SSR preset, @salesforce/eslint-config-lwc/ssr, lets you identify non-portable components.

You can configure the plugin with rules from this list. 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 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.

If a component must use one of these scoped modules on the client side, dynamically import 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.

Component JavaScript Before Portable Styling
1import { LightningElement, api } from 'lwc';
2import basePath from '@salesforce/community/basePath';
3
4export default class Logo extends LightningElement {
5    static renderMode = 'light';
6    @api highlight = false;
7    @api borderWidth = '1';
8
9    renderedCallback() {
10        // this code only runs on the client
11        const image = this.querySelector('img.logo');
12        image?.setAttribute('src', getLogoUrl(basePath));
13        if (this.highlight) image?.classList.add('highlight');
14        image?.style.setProperty('border-width', `${this.borderWidth}px`);
15    }
16}
Component HTML Before Portable Styling
1<!-- before -->
2<template>
3    <img src="" class="logo" />
4</template>
Component HTML After Portable Styling
1import { LightningElement, api } from 'lwc';
2import basePath from '@salesforce/community/basePath';
3
4export default class Logo extends LightningElement {
5    static renderMode = 'light';
6    @api highlight = false;
7    @api borderWidth = '1';
8
9    // this code works on both the server and client
10    // this component is now static and no longer requires hydration
11    get url() {
12        return getLogoUrl(basePath);
13    }
14    get className() {
15        return this.highlight ? 'logo highlight' : 'logo';
16    }
17    get borderStyle() {
18        return `border-width:${this.borderWidth}px`;
19    }
20}
1<template>
2    <img src="{url}" class="{className}" style="{borderStyle}" />
3</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:

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.

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 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.

1import { LightningElement } from 'lwc';
2export default class Heading extends LightningElement {
3  static renderMode = 'light';
4  @api text;
5}

In your template, use the lwc:render-mode directive.

1<template lwc:render-mode="light">
2  <h2 class="global-style">{text}</h2>
3</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.

Next Steps 

After you’ve configured a component for SSR, make sure it follows the Best Practices for Portable Components.

See Also

Developer Preview Feature

Feature is available as a developer preview. Feature is not generally available unless or until Salesforce announces its general availability in documentation or in press releases or public statements. All commands, parameters, and other features are subject to change or deprecation at any time, with or without notice. Do not implement functionality developed with these commands or tools.