---
schema_version: "1"
id: b8ca5b42cbbf0142396ebca96ff31dfff705e2ccef5fdd50a59e8cd6ad0bb782
title: Test Your Components
collection: developer
version: current
locale: en-us
portal_url: https://docs.salesforce.com/developer/current/en-us/lwr-node/lwr/guides/lwr-ssr-components-test.md
canonical_url: https://developer.salesforce.com/docs/platform/lwr/guide/lwr-ssr-components-test.html
source_site: developer.salesforce.com
content_type: documentation
content_hash: f7f13ba13560c8318419251b7a46d842ca1c7364ce8fb9ff31b6f1361fb289b8
products:
  - Lightning Web Runtime
---
# Test Your Components

**important:** Before you test components, make sure you've reviewed the [best practices for portable components](https://developer.salesforce.com/docs/platform/lwr/guide/lwr-portable-best-practices.md).

To check if you can server-side render (SSR) your components, evaluate them with the [SSR playground](#debug-with-the-ssr-playground), the [SSR test runner](#run-the-test-runner), and unit tests with Jest. You can also [manually verify](#manually-verify-ssr-for-a-component) that SSR was successful for a component.

## Debug with the SSR Playground

The SSR playground lets you render, debug, and experiment with individual components and their children. You can use the playground in client-side rendering (CSR) mode or SSR mode.

To run the playground, navigate to a directory that contains one of these files and run the appropriate [playground command](#ssr-playground-commands).

- `lwc.config.json` file
- `package.json` file containing an `lwc` field
- `sfdx-project.json` file that points to a project directory containing LWCs

### SSR Playground Commands

To open the playground in Chrome, run this command in your terminal:

```bash
npx -p @lwc/wds-playground playground namespace/component
```

To open the playground in Chrome with DevTools, run this terminal command:

```bash
npx -p @lwc/wds-playground playground namespace/component --open --devtools
```

### Step-By-Step Testing with the SSR Playground

Start at the "leaves" of your component tree, or components that don't contain other components.

1. Open the component in the SSR playground.
   1. Modify the component properties to test important use cases.
   2. Address errors that are reported during the three phases of hydration—SSR, DOM insertion, and rehydration.
   3. Look for and address any visual bugs in your component.
2. Enable the CSR toggle to render your component with SSR and CSR side by side.
3. Use the playground’s comparison tool to ensure the SSR and CSR component instances are visually identical.
4. Enable the layout shift tool in the Misc section in config to observe any layout shifts during SSR hydration.

![The SSR playground's comparison tool.](https://developer.salesforce.com/docs/media/uplift-playground.png)

By default, the SSR playground takes your component through three stages of its SSR lifecycle:

1. Render the component to HTML markup on the server.
2. Insert the markup into the DOM on the client.
3. Hydrate the DOM subtree and associate it with an instance of your component class.

We recommend that you write tests using the SSR test runner to ensure that your components don't regress as you make changes.

### Install the SSR Test Runner

**note:** This tool works best with Node v18 or v20.

You have to install the SSR Test Runner before you can use it on your project.

To add the test runner to your project, download the `@lwc/test-runner` package using a package management tool. We recommend using Node Package Manager (npm).

```
npm install --save-dev @lwc/test-runner
```

### Write SSR tests

**note:** By default, the SSR Test Runner executes SSR tests in headless Chrome. Unlike Jest tests, the SSR tests run in a full-featured web browser.

To create your own SSR tests, you can use these functions from the `@lwc/test-runner` package.

#### `renderToMarkup()`

`renderToMarkup()` is an asynchronous function that takes:

- The path to your component
- The properties that you use for rendering

It returns:

- `Promise<String>` where the `String` is HTML markup

#### `insertMarkupIntoDom()`

`insertMarkupIntoDom()` is an asynchronous function that takes:

- SSR markup, like `Promise<String>` returned by `renderToMarkup()`

It returns:

- `Promise<HtmlElement>()`, which is a handle to the root element of your SSR-rendered DOM subtree.

#### `hydrateElement()`

`hydrateElement()` is an async function that takes:

- A root element, like `Promise<HtmlElement>` returned from `insertMarkupIntoDom()`
- Component properties, which should be the same as those passed into `renderToMarkup()`

It returns:

- `Promise<Boolean>`, where the `Boolean` indicates whether hydration completed without validation errors. In most cases, you'll want this Boolean value to be true. If hydration failed, review the errors in the console.

#### `expect()`

You can use this function to chain assertions. To make it easier to test your components, it includes all the assertions from `Chai.expect` as well as the following additions.

- `throwErrorInConnectedCallback`: Asserts any errors thrown in `connectedCallback`.
- `SSRCorrectly`: Checks that all three stages of SSR lifecycle are performed correctly.
- `visuallyIdenticalInCSRandSSR`: Performs a pixel match of SSR and CSR components.
- `noLayoutShifts`: Ensures that the component didn't have any layout shifts when it was hydrated in DOM.
- `notMakeDomMutationsDuringSSR`: Ensures that the component didn't make any DOM mutation.

#### Example SSR Test

Here's a sample SSR test that you can execute with the SSR Test Runner.

```
import {
    // Importing `expect` from the toolkit is subject to change
    expect,
    querySelectorDeep,
    // These three functions contribute heavily to your tests
    renderToMarkup,
    insertMarkupIntoDom,
    hydrateElement,
} from '@lwc/test-runner';

// Instead of importing the component directly, use the path of
// the component's JS file. This value is passed to `renderToMarkup`
// and `hydrateElement` where the component is imported automatically.
const componentPath = import.meta.resolve('./parent.js');

describe('<x-parent>', () => {
    it('is SSR-able and very snazzy', async () => {
    // Errors thrown during SSR cause the test to fail
    const markup = await renderToMarkup(componentPath, {});

    // Once you have the raw HTML markup, you can make related assertions.
    expect(markup).to.contain('</x-parent>');

    // Insert that markup into the DOM. It doesn't get attached
    // to an instance of your component class yet.
    const el = await insertMarkupIntoDom(markup);

    // Make assertions about pre-hydrated DOM
    expect(el).to.haveShadowChild('p.child-content');

    // Finally, hydrate the HTML that was generated on the server and
    // inserted into the DOM in previous steps.
    const hydratedWithSsrDOM = await hydrateElement(el, componentPath);

    // Check that hydration occurred without validation errors.
    expect(hydratedWithSsrDOM).to.be.true;

    // Now that the SSR-generated markup has been inserted and hydrated,
    // the component should behave as though it were originally rendered
    // in the browser with CSR. Check the component to ensure expected behavior.
    expect(querySelectorDeep('p.child-content', el)).to.have.text('Hmm! hello from parent');
    });

    // Unless you want to make assertions on output of each individual stage of SSR,
    // you can use this one-liner to check that DOM was hydrated without any errors.
    it('SSR correctly', async () => {
        await expect(componentPath, {}).to.SSRCorrectly();
    });

    // Make sure no error is thrown when connected callback lifecycle is executed
    it('doesnt throw in connected callback', async () => {
        await expect(async () => {
            await renderToMarkup(componentPath, {});
        }).to.not.throwErrorInConnectedCallback();
    });

    // Pixel match rendered SSR and CSR components to check for visual correctness
    it('checks that SSR and CSR are visually the same ', async () => {
        await expect(componentPath, {}).to.be.visuallyIdenticalInCSRandSSR();
    });

    // No layout shift was observed during hydration stage
    it('has no layout shifts', async () => {
        await expect(componentPath, {}).to.have.noLayoutShifts();
    });

    // The component doesn't make any DOM mutation
    it('makes no DOM mutations', async () => {
        await expect(componentPath, {}).to.notMakeDomMutationsDuringSSR();
    });
});
```

### Run the Test Runner

Ready to invoke the test runner and run your own SSR tests? First, make sure you [installed](#install-the-ssr-test-runner) the `@lwc/test-runner` dependency for your project.

Then, run the test runner using `npx` or NPM scripts to invoke the `@lwc/test-runner` command. Tests run in parallel in separate headless Chrome tabs. The tests run fast, and as many as ten thousand tests can complete in under six seconds — depending on your hardware.

Here's the `npx` command to start the test runner. To customize your test runner output, you can use [supported command line flags](#lwctest-runner-flags). By default, the `@lwc/test-runner` command prints all the browser console output in your terminal.

```
npx @lwc/test-runner SPEC_FILE_PATTERN
```

**important:** If you use ZSH, surround `SPEC_FILE_PATTERN` in single quotes.

To distinguish your project's SSR tests from your Jest tests, use unique file extensions. For example, if your Jest tests follow the format `COMPONENT_NAME.spec.js`, you can follow the format `COMPONENT_NAME.spec-ssr.js` for your SSR test files. If you follow this filename format, run your tests like this:

```
npx @lwc/test-runner './src/**/*.spec-ssr.js'
```

You can also create a shortcut for this command in your project's `package.json` that uses NPM. Here's an example:

```
"scripts": {
    "test:ssr": "lwc-test-runner 'src/**/*.spec-ssr.js'"
}
```

#### @lwc/test-runner Flags

Add these flags to your `@lwc/test-runner` command to customize its command line output.

| Flag                            | Description                                                                        |
| ------------------------------- | ---------------------------------------------------------------------------------- |
| `--debug`                       | Enables debug mode to find issues with your tests.                                 |
| `--modulesDir <directory name>` | Invoke the test runner on a specific directory in your project.                    |
| `--puppeteer`                   | Execute the test runner in the [Puppeteer](https://pptr.dev/) browser environment. |
| `--quiet`                       | Prevents browser console output from printing in your terminal.                    |

### Interpret Test Runner Output

If the test runner returns failures for `hydrateElement`, `noLayoutShifts`, or `notMakeDomMutationsDuringSSR`, you likely have performance issues on the client-side.

## Manually Verify SSR for a Component

When a component is rendered on a page, you can verify that SSR was successful by looking at the DOM elements.
To determine if an island or component tree implemented SSR successfully:

1. In Chrome, right-click the page and select to **View Page Source**. If you use Firefox or another browser, the **View Page Source** tool might be named something else, like Inspect or View Selection Source.
2. Search for some HTML or text from the component. If you find it, then SSR was successful.

**CSR-only** — The wrapper has no inner HTML.

```
<webruntime-island-container-x1787601173>
</webruntime-island-container-x1787601173>‍
```

**SSR with hydration** — The wrapper has inner HTML and a `data-lwr-props-id` attribute.

```
<webruntime-island-container-x1783627592 data-lwr-props-id="lwcprops8de0">
    <c-cmp>
        <style type="text/css">...</style>
        <textarea></textarea>
    </c-cmp>
</webruntime-island-container-x1783627592>
```

**SSR-only (not hydrated)** — There's no wrapper, so the HTML exists in the page source.

```
<c-title>
    <style type="text/css">...</style>
    <h1>Page Title</h1>
</c-title>
```

## Write Jest Unit Tests

Jest is the primary tool used for testing in LWR projects. The [`lwc-test` NPM package](https://github.com/salesforce/lwc-test/) lets you write Jest unit tests that you can run in SSR and CSR environments. We recommend using two separate test suites to ensure that SSR and CSR are independently and thoroughly validated.

To start working with SSR or CSR unit tests, add the latest version of the following dependencies to your project:

- `@lwc/jest-ssr-snapshot-utils`
- `@lwc/jest-jsdom-test-env`
- `@lwc/jest-preset`

### SSR Unit Tests

Server-side tests focus on rendering components to static HTML on the server side. To make sure that server-side logic works as expected without client-side DOM interactions, these tests are executed in a Node environment.

Here's a sample Jest configuration file for server-side testing:

```
module.exports = {
    displayName: 'Server-side rendering',
    preset: '@lwc/jest-preset/ssr-server',
    testMatch: ['**/*.ssr-server.(spec|test).(js|ts)'],
    collectCoverageFrom: ['**/*.ssr-server.(spec|test).(js|ts)'],
};
```

To run a server-side test, add a script to your `package.json`. You can alternatively use `jest --selectProjects <project_name>` to target specific test suites.

```
"scripts": {
    "test:ssr:server": "jest --no-cache --projects=./example-ssr/jest.ssr-server.config.js"
}
```

Running server-side tests generates static HTML markup as snapshots. Every part of a snapshot associated with a table-driven test case is linked to a unique hash that's generated from the component's tag name, properties, and state. Use snapshots to verify the consistency of server-rendered output, like this:

1. Run server-side tests to generate baseline snapshots.
2. Whenever your components change, rerun the server-side tests to identify any related markup changes. The tests fail if any discrepancies between component versions arise.
3. After you confirm that your changes are safe and valid, update your HTML markup snapshots.

### CSR Unit Tests

Client-side tests validate how components behave [post-hydration](https://developer.salesforce.com/docs/platform/lwr/guide/lwr-understanding-islands.md) in a browser-like environment. To simulate browser behavior, these tests are executed using JSDOM.

Here's a sample Jest configuration file for client-side testing:

```
module.exports = {
    displayName: 'SSR with hydration',
    preset: '@lwc/jest-preset/ssr-for-hydration',
    setupFilesAfterEnv: ['./jest.ssr-client.setupAfterEnv.js'],
    testMatch: ['**/*.ssr-client.(spec|test).(js|ts)'],
    transformIgnorePatterns: ['node_modules/(?!(@webcomponents/.+)/)'],
};
```

To track hydration errors, monitor the `console.warn` event.

```
let hydrationMismatchOccurred = false;
let hydrationMismatchMessage = '';

beforeEach(() => {
    // Reset the flag and message before each test
    hydrationMismatchOccurred = false;
    hydrationMismatchMessage = '';

    // Spy on console.warn and intercept warnings
    jest.spyOn(console, 'warn').mockImplementation((message) => {
        if (message.includes('Hydration mismatch')) {
            // Set the flag to indicate a hydration mismatch occurred
            hydrationMismatchOccurred = true;
            // Store the hydration mismatch message
            hydrationMismatchMessage = message;
        } else {
            // If it's not a hydration mismatch, call the original console.warn
            console.warn(message);
        }
    });
});

afterEach(() => {
    // Restore original console.warn after each test
    jest.restoreAllMocks();

    // Check if a hydration mismatch occurred and fail the test if so
    if (hydrationMismatchOccurred) {
        throw new Error(`Test failed due to hydration mismatch: ${hydrationMismatchMessage}`);
    }
});
```

To run a client-side test, add a script to your `package.json`. You can alternatively use `jest --selectProjects <project_name>` to target specific test suites.

```
"scripts": {
    "test:ssr:client": "jest --no-cache --projects=./example-ssr/jest.ssr-client.config.js"
}
```

Client-side tests use [server-side snapshots](#ssr-unit-tests) to validate your components' post-hydration behavior, like this:

1. Read server-side-generated snapshots.
2. Insert the pre-rendered markup into the DOM.
3. Hydrate the component and validate its behavior in the client environment.

By comparing pre- and post-hydration markup, SSR client-rendering testing helps identify potential performance issues early on. It helps you address inconsistencies, layout shifts and inefficiencies before they affect your overall user experience.

## Next Steps

Now that you've...

- Configured your components for SSR
- Aligned them with the SSR best practices
- Evaluated them in SSR testing environments

...you can enable SSR for a page of your LWR site by following the instructions in [Enable SSR for LWR Apps](https://developer.salesforce.com/docs/platform/lwr/guide/lwr-enable-ssr-site.md). If you have an Experience Cloud site, review [Hydration Capabilities for Islands (Experience Cloud)](https://developer.salesforce.com/docs/platform/lwr/guide/lwr-ssr-hydration-experience-cloud.md) instead.

Enabling SSR for an entire page is a **prerequisite** for configuring a single component island for SSR.
