Best Practices for SSRed Components

The following sections describe best practices on how to make your components portable.

To determine whether your components follow best practices on APIs or methods usage, use the ESLint Plugin for LWC. Specifically, use the SSR preset to help you make your components server-side renderable.

For example, here are a few rules to identify non-portable components.

  • Disallow access to global browser APIs during SSR
  • Disallow access to unsupported properties on this during SSR
  • Disallow usage of process.env.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.

Rendering components on the server means that as a component author, you don’t have access to certain general browser APIs (like window, document, or querySelectors). If you’re performing these operations during any component lifecycle events that execute during SSR, you need to protect your code so that it becomes portable.

To guard non-portable code, use the import.meta.env.SSR boolean. For example:

To enable feature detection via optional chaining, use the globalThis property. This approach works best when the return value of the browser-only function is not needed by the component.

The window object is undefined on the server, so it can't be used.

Slotted 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 some changes to your slotchange components’ behavior.

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

Scoped modules provide functionality specific to users, sites, or orgs. Org and site-specific modules are frozen, which means that their values are immutable at publishing time. User-specific modules are evaluated on the client without any server calls. User-specific modules include:

  • @salesforce/user
  • @salesforce/userPermission
  • @salesforce/customPermission

User-specific modules are considered "live scoped modules" because they are mutable and can change independently of publishing. User-specific modules generally cannot be cached and they default to guest-only values for SSR. When a user authenticates, the values are re-fetched on the client.

If you import @salesforce/user/isGuest, it resolves to true during SSR regardless of the actual authentication status. However, when the page is hydrated on the client, it resolves to the correct value.

Rendering as a guest on the server and rehydrating as an authenticated user on the client can cause rendering issues. For example, the SSR of content as a guest on the server can display something completely different than the page displayed for the authenticated user. If you use modules that require personalized content, we recommend following these guidelines so that your content renders and hydrates successfully.

  • During SSR, render an empty placeholder element. Then, render something more appropriate during CSR.
  • Use a CSR-only island to render only on the client.

To communicate feedback after a user action, display a toast using the lightning/toast module. A toast conveys small pieces of information to the user in an unobtrusive way. It can contain an icon, label, message, and links. For example, display a toast notification after a user creates or edit a record.

The lightning/platformShowToastEvent module isn't supported for SSR

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

For example, you're importing a portable and a non-portable module in your component.

To import a module dynamically, use an async/await function. Guard non-portable code using the import.meta.env.SSR boolean.

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.