Best Practices for SSRed Components

The following sections describe how to maintain portable components for server-side rendering (SSR).

Rendering components on the server means you don’t have access to general browser APIs like window, document, and querySelector. If you’re performing these operations during any component lifecycle events that execute during SSR, you need to revise your code so that it becomes portable.

To prevent non-portable code from running during SSR, use the import.meta.env.SSR boolean. For example, this connectedCallback() doesn't run window.addEventListener() if App gets SSRed.

Feature detection ensures that code only runs in supported browsers. Optional chaining causes failed expressions to return undefined instead of an error. This works best when the return value of the browser-only function isn't needed by the component.

To enable feature detection via optional chaining, use the globalThis property.

You can't use the window object because it's undefined on the server.

Slotted content refers to anything contained in a <slot> element that you pass into a slot. This content renders as expected during SSR. However, native browser built-in events like slotchange can’t fire during SSR because they’re rendered outside of a browser context. As a result, you might observe changes to slotchange behavior in your components.

For example, a slotchange event determines the number of elements to display in this carousel component. The carousel is outlined in blue, and a pagination component is outlined in pink. The pagination component acts as a control for the number of images being displayed, and it shows a count of three in this example.

A basic carousel component with three cards.

The pagination component logic runs during the slotchange event. Since the slotchange event doesn’t happen during an SSR flow, the first initial render only shows a count of 1.

When the component is rendered in the browser, the slotchange event fires and hydration updates the count to three.

When these browser-specific events occur, the webpage can sometimes display a small “flash” effect or layout shift. You can reduce the visibility of these effects by adding placeholders or loading messages to your code. This approach is useful when logic takes a long time to run on the client, like a REST or GraphQL data fetch.

To ensure that non-portable modules don't get processed on the server, import them dynamically in your components.

For example, this component imports a portable and a non-portable module.

To import these modules dynamically, the component can use an async/await function, like this. Remember to guard non-portable code using the import.meta.env.SSR boolean.

Mutating the host element in connectedCallback() isn't supported in SSR and CSR.

SSR hydration validates that the virtual DOM exactly matches the HTMLElement. For validation to succeed, each virtual DOM attribute has to have an equivalent HTMLElement counterpart. If a mutation occurs in a component's connectedCallback(), changes that may appear in HTMLElement don't appear in the virtual DOM.

For example, this connectedCallback() uses the classList anti-pattern to try to add class names to the host element. However, directly mutating the HTMLElement means the container class never appears in the virtual DOM.

To correctly add the container class to the host, use a semantic element in your <template> instead.

You can pass a class from a parent component to a child component by wrapping the child in a <div> with a dynamic value for the class property. Then, implement the corresponding getter in the parent component's JavaScript.

The parent component's JavaScript uses the fromOutside property to set <c-parent from-outside="parent-class">. The getter ensures that the child component renders the my-child-needs-parent-class value on the class attribute.

You can speed up app development by using base Lightning web components (LBCs), which are out-of-the-box building blocks for user interfaces like Lightning Web Runtime (LWR) sites. If you add a supported LBC to your component in your Experience Cloud site, set the hydration capability lightning__ServerRenderableWithHydration in the component metadata.

Only the following LBCs are supported for SSR. For more information about LBCs, see the Component Library.

lightning-accordionlightning-dynamic-iconlightning-input-rich-textlightning-rich-text-toolbar-button
lightning-accordion-sectionlightning-file-uploadlightning-layoutlightning-rich-text-toolbar-button-group
lightning-alertlightning-formatted-addresslightning-layout-itemlightning-select
lightning-avatarlightning-formatted-date-timelightning-menu-dividerlightning-spinner
lightning-badgelightning-formatted-emaillightning-menu-itemlightning-tab
lightning-breadcrumblightning-formatted-locationlightning-menu-subheaderlightning-tabset
lightning-breadcrumbslightning-formatted-namelightning-modallightning-textarea
lightning-buttonlightning-formatted-numberlightning-modal-bodylightning-tile
lightning-button-grouplightning-formatted-phonelightning-modal-footerlightning-toast
lightning-button-iconlightning-formatted-rich-textlightning-modal-headerlightning-toast-container
lightning-button-icon-statefullightning-formatted-textlightning-pilllightning-vertical-navigation
lightning-button-menulightning-formatted-timelightning-pill-containerlightning-vertical-navigation-item
lightning-button-statefullightning-formatted-urllightning-progress-barlightning-vertical-navigation-item-badge
lightning-cardlightning-helptextlightning-progress-indicatorlightning-vertical-navigation-item-icon
lightning-checkbox-grouplightning-iconlightning-progress-ringlightning-vertical-navigation-overflow
lightning-comboboxlightning-inputlightning-progress-steplightning-vertical-navigation-section
lightning-confirmlightning-input-addresslightning-prompt
lightning-dual-listboxlightning-input-locationlightning-radio-group

SSR can impact your existing style rules for LBCs on your site. To learn how to adapt your styles for SSR, see Update Base Lightning Web Components Styling.

Now that your components can opt-into SSR, we recommend testing them with the SSR playground and test runner to catch and correct unexpected behavior. To learn more, follow the instructions in Test Your Components.