---
title: "Instrument Session Replay | Grafana Cloud documentation"
description: "Instrument Session Replay in the Faro Web SDK"
---

> For a curated documentation index, see [llms.txt](/llms.txt). For the complete documentation index, see [llms-full.txt](/llms-full.txt).

# Instrument Session Replay

> Note
> 
> Session Replay is currently in [private preview](/docs/release-life-cycle/). Grafana Labs offers support on a best-effort basis, and breaking changes might occur prior to the feature being made generally available.

On this page, you’ll learn how to install and instrument Session Replay in Faro.

## Before you begin

To use Session Replay, you need:

- A [Grafana Cloud account](/auth/sign-up/create-user).
- An application instrumented with the [Faro Web SDK v2](/docs/grafana-cloud/monitor-applications/frontend-observability/get-started/).
- [`npm`](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm) installed on your machine.

## Install the Faro Instrumentation Replay package

To install Session Replay:

1. Open your terminal.
2. Navigate to your application folder.
3. Run:

Bash ![Copy code to clipboard](/media/images/icons/icon-copy-small-2.svg) Copy

```bash
npm install @grafana/faro-instrumentation-replay
```

## Instrument your application

To enable Session Replay in your application:

1. Import the `faro-instrumentation-replay` package:
   
   js ![Copy code to clipboard](/media/images/icons/icon-copy-small-2.svg) Copy
   
   ```js
   import { ReplayInstrumentation } from '@grafana/faro-instrumentation-replay';
   
   // Make sure to import Faro as well
   import { getWebInstrumentations, initializeFaro } from '@grafana/faro-web-sdk';
   ```
2. Pass the `ReplayInstrumentation` object when initializing Faro:
   
   js ![Copy code to clipboard](/media/images/icons/icon-copy-small-2.svg) Copy
   
   ```js
   initializeFaro({
     // Mandatory, the URL of the Grafana Cloud collector with embedded application key.
     // Copy from the configuration page of your application in Grafana.
     url: 'http://faro-collector-us-central-0.grafana.net/collect/{app-key}',
   
     // Mandatory, the identification label(s) of your application
     app: {
       name: 'my-app',
       version: '1.0.0', // Optional, but recommended
     },
   
     instrumentations: [
      ...getWebInstrumentations(),
      new ReplayInstrumentation()
     ],
   });
   ```

Refer to [configuration options](#configuration-options) for more details on how to customize your session recordings.

## Understand data masking and sub-sampling

Before you record sessions in production, it’s important to understand how Session Replay protects sensitive data and how it controls the volume of recordings you collect.

### Data masking

Session Replay includes masking and privacy configuration options so that you can control exactly what is and isn’t recorded, and make sure there’s no sensitive or personal data being exposed in session recordings.

All input values and text content are masked by default, client-side, before data leaves the browser. You can make masking more selective by using the `maskAllInputs` and `maskInputOptions` properties, or by using CSS selectors combined with the `maskTextSelector` and `blockSelector` properties. You can also use the `beforeSend` hook to filter or transform events before sending them.

After making changes to your input masking properties, make sure to test your configuration locally or in development environments before enabling it in production.

> Caution
> 
> Personally identifiable information (PII) in page URLs is not masked.

For the full list of privacy properties, refer to [Privacy and masking options](#privacy-and-masking-options). For broader guidance on what data is recorded and how to meet privacy requirements, refer to [Data privacy in Session Replay](/docs/grafana-cloud/monitor-applications/frontend-observability/session-replay/data-privacy/).

### Sub-sampling

Frontend Observability with Session Replay has two independent layers of sampling:

- **Frontend session sampling**: Controls which sessions Faro collects telemetry for (logs, errors, traces).
- **Session Replay sampling**: Controls what fraction of those sessions also get a recording, configured via `samplingRate` on `ReplayInstrumentation`.

For example, a Faro `samplingRate` of `1.0` combined with a replay `samplingRate` of `0.1` means telemetry is collected for all sessions, but only `10%` of those sessions have a recording:

typescript ![Copy code to clipboard](/media/images/icons/icon-copy-small-2.svg) Copy

```typescript
initializeFaro({
  url: 'https://your-faro-endpoint.com',
  sessionTracking: {
    samplingRate: 1.0, // collect telemetry (logs, errors, web-vitals) for all sessions
  },
  instrumentations: [
    ...getWebInstrumentations(),
    new ReplayInstrumentation({
      samplingRate: 0.1, // record replay for only 10% of sessions
    }),
  ],
});
// Effective replay coverage: 1.0 × 0.1 = 10% of globally sampled sessions
```

To ensure you have recordings for the sessions you care about most, review any existing Frontend Session sampler logic to control which sessions are kept or dropped upstream. Refer to the [Faro Sampling docs](/docs/grafana-cloud/monitor-applications/frontend-observability/configure/sampling/).

## Configuration options

You can customize how Session Replay records sessions by passing options to `ReplayInstrumentation`. The following tables list the available recording, privacy and masking, and hook options, along with their types and default values.

### Recording options

Expand table

| Key                        | Type                          | Default  | Description                                                                                                                                 |
|----------------------------|-------------------------------|----------|---------------------------------------------------------------------------------------------------------------------------------------------|
| `samplingRate`             | `number`                      | `1`      | Fraction of globally sampled sessions that are recorded. Applied on top of `sessionTracking.samplingRate`. Out-of-range values are clamped. |
| `recordAfter`              | `'load' | 'DOMContentLoaded'` | `'load'` | When to start recording if the document is not ready yet                                                                                    |
| `recordCrossOriginIframes` | `boolean`                     | `false`  | Whether to record cross-origin iframes. Each child iframe must also be instrumented for this to work                                        |
| `recordCanvas`             | `boolean`                     | `false`  | Whether to record canvas element content                                                                                                    |
| `collectFonts`             | `boolean`                     | `false`  | Whether to collect fonts used in the website                                                                                                |
| `inlineImages`             | `boolean`                     | `false`  | Whether to record image content                                                                                                             |
| `inlineStylesheet`         | `boolean`                     | `false`  | Whether to inline stylesheets in the recording events                                                                                       |
| `inactivityThresholdMs`    | `number`                      | `60000`  | Pause recording after this many milliseconds of inactivity. Recording resumes automatically on the next interaction. Set to `0` to disable. |

### Privacy and masking options

Expand table

| Key                | Type                                              | Default              | Description                                                                                                                              |
|--------------------|---------------------------------------------------|----------------------|------------------------------------------------------------------------------------------------------------------------------------------|
| `maskAllInputs`    | `boolean`                                         | `true`               | Mask all input content as `*`                                                                                                            |
| `maskInputOptions` | `MaskInputOptions`                                | `{ password: true }` | Selectively mask specific input types (used only when `maskAllInputs` is `false`)                                                        |
| `maskInputFn`      | `(value: string, element: HTMLElement) => string` | `undefined`          | Customize mask input content recording logic                                                                                             |
| `maskTextSelector` | `string`                                          | `'*'`                | CSS selector for elements whose text content should be masked. The default value matches all elements                                    |
| `blockSelector`    | `string`                                          | `undefined`          | CSS selector for elements that should be blocked from recording. Blocked elements are replaced with a placeholder of the same dimensions |
| `ignoreSelector`   | `string`                                          | `undefined`          | CSS selector for elements whose input events should be ignored                                                                           |

#### `maskInputOptions`

Expand table

| Key              | Type      | Description           |
|------------------|-----------|-----------------------|
| `password`       | `boolean` | Password inputs       |
| `text`           | `boolean` | Text inputs           |
| `email`          | `boolean` | Email inputs          |
| `tel`            | `boolean` | Telephone inputs      |
| `number`         | `boolean` | Number inputs         |
| `search`         | `boolean` | Search inputs         |
| `url`            | `boolean` | URL inputs            |
| `date`           | `boolean` | Date inputs           |
| `datetime-local` | `boolean` | Datetime-local inputs |
| `month`          | `boolean` | Month inputs          |
| `week`           | `boolean` | Week inputs           |
| `time`           | `boolean` | Time inputs           |
| `color`          | `boolean` | Color inputs          |
| `range`          | `boolean` | Range inputs          |
| `textarea`       | `boolean` | `textarea` elements   |
| `select`         | `boolean` | Select dropdowns      |

### Hooks

Expand table

| Key          | Type                                                         | Default     | Description                                                                                   |
|--------------|--------------------------------------------------------------|-------------|-----------------------------------------------------------------------------------------------|
| `beforeSend` | `(event: eventWithTime) => eventWithTime | null | undefined` | `undefined` | Transform or filter events before they are sent. Return `null` or `undefined` to skip sending |

## Examples

The following examples show common ways to configure `ReplayInstrumentation`. Use them as a starting point and adjust the masking and privacy options to match your application’s requirements.

### Mask passwords only configuration

typescript ![Copy code to clipboard](/media/images/icons/icon-copy-small-2.svg) Copy

```typescript
new ReplayInstrumentation({
  // Disable global input masking and use selective masking rules instead
  maskAllInputs: false,
  // Mask all password inputs
  maskInputOptions: { password: true },
  // Override default CSS selector to stop masking text content
  maskTextSelector: undefined,
})
```

### Advanced privacy configuration

typescript ![Copy code to clipboard](/media/images/icons/icon-copy-small-2.svg) Copy

```typescript
new ReplayInstrumentation({
  // Disable global input masking and use selective masking rules instead
  maskAllInputs: false,
  // Mask all text and email inputs, but allow number inputs
  maskInputOptions: {
    password: true,
    text: true,
    email: true,
    tel: true,
    textarea: true,
  },
  // Mask elements with specific CSS classes
  maskTextSelector: '.sensitive-data, .pii',
  // Block elements completely from recording
  blockSelector: '.payment-form, .credit-card-info',
  // Ignore certain elements (won't be recorded at all)
  ignoreSelector: '.analytics-widget',
  // Filter or transform events before sending
  beforeSend: (event) => {
    // Example: Skip events that might contain sensitive data
    // type 3 = IncrementalSnapshot, source 9 = CanvasMutation
    if (event.type === 3 && event.data?.source === 9) {
      return null; // Skip this event
    }
    return event; // Send the event as-is
  },
});
```

## Best practices

### Override `maskTextSelector` default value to display elements

The `maskTextSelector` option accepts a CSS selector string and masks any matching elements in your Session Replay recordings. The default value is `'*'`, which means it’ll match and mask all elements on a page, regardless of other options.

To make sure your masking configuration options work correctly when using `maskAllInputs` and `maskInputOptions`, override the `maskTextSelector` default value:

typescript ![Copy code to clipboard](/media/images/icons/icon-copy-small-2.svg) Copy

```typescript
new ReplayInstrumentation({
  // Disable global input masking and use selective masking rules instead
  maskAllInputs: false,
  // Mask all password and email inputs
  maskInputOptions: {
    password: true,
    email: true,
  },
  // Override default CSS selector to display other inputs
  maskTextSelector: null,
})
```

### Use `maskTextSelector` to mask all elements with a CSS class

The `maskTextSelector` option accepts a CSS selector string and masks any matching elements in your Session Replay recordings.

To selectively mask specific elements, add a class such as `masked` to any element you want redacted, then pass that class as the selector. This works regardless of which CSS framework you use:

js ![Copy code to clipboard](/media/images/icons/icon-copy-small-2.svg) Copy

```js
new ReplayInstrumentation({
  // Disable global input masking and use selective masking rules instead
  maskAllInputs: false,
  // Mask all text and email inputs, but allow number inputs
  maskInputOptions: {
    password: true,
    text: true,
    email: true,
    tel: true,
    textarea: true,
  },
  // Mask elements with specific CSS classes
  maskTextSelector: '.masked',
});
```
