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:
The functions that execute during SSR must be portable.
The functions that execute during SSR must be synchronous.
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';34export default class Logo extends LightningElement {5 static renderMode = 'light';6 @api highlight = false;7 @api borderWidth = '1';89 renderedCallback() {10 // this code only runs on the client11 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';34export default class Logo extends LightningElement{5 static renderMode = 'light';6 @api highlight = false;7 @api borderWidth = '1';89 // this code works on both the server and client10 // this component is now static and no longer requires hydration11 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}
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.
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.
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.