> ## Documentation Index
> Fetch the complete documentation index at: https://launchdarkly-preview.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# HTMX observability reference

> This topic documents how to get started with the LaunchDarkly observability plugins in an htmx application using the client-side JavaScript SDK.

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

## Overview

This topic documents how to get started with the LaunchDarkly observability plugins in an [htmx](https://htmx.org/) application.

htmx is a lightweight library that lets you build modern web applications using HTML attributes to issue AJAX requests, handle events, and update the DOM, without writing JavaScript. htmx applications render server-provided html and text fragments without a JavaScript framework, so you can use the LaunchDarkly client-side JavaScript SDK directly in htmx applications.

The LaunchDarkly JavaScript SDK supports the following observability plugins:

* An **observability plugin** for error monitoring, logging, and tracing.
* A **session replay plugin** that provides a way to record and replay end-user sessions from your application.

LaunchDarkly's observability and session replay plugins use DOM mutation observers to track changes when htmx swaps content in or out of the page. The plugins capture these changes automatically, requiring no additional configuration. To learn more, read [How htmx works with observability](#how-htmx-works-with-observability).

<Note title="SDK quick links">
  LaunchDarkly's SDKs are open source. In addition to this reference guide, we provide source, API reference documentation, and a sample application:
</Note>

| Resource | Location |
| - | - |
| SDK API documentation | [Observability plugin API docs](https://launchdarkly.github.io/observability-sdk/packages/@launchdarkly/observability/interfaces/api_observe.Observe.html) ,  [Session replay API docs](https://launchdarkly.github.io/observability-sdk/packages/@launchdarkly/observability/interfaces/api_record.Record.html) |
| GitHub repository | [@launchdarkly/observability](https://github.com/launchdarkly/observability-sdk/tree/main/sdk/%40launchdarkly/observability) |
| Published module | [npm](https://www.npmjs.com/package/@launchdarkly/observability) |

<Warning title="For use in client applications only">
  The session replay plugins are available only for the LaunchDarkly client-side JavaScript-based SDKs.

  To learn more about LaunchDarkly's different SDK types, read [Choosing an SDK type](/sdk/concepts/client-side-server-side).
</Warning>

## Prerequisites and dependencies

This reference guide assumes that you are familiar with the LaunchDarkly [JavaScript SDK](/sdk/client-side/javascript) and with [htmx](https://htmx.org/).

The observability plugin requires [JavaScript SDK](/sdk/client-side/javascript) version 3.7.0 or later, and is automatically included in the JavaScript Browser SDK v1.

## How htmx works with observability

htmx applications differ from traditional single-page applications (SPAs) in important ways that affect observability:

* **Server-rendered HTML**: htmx applications receive HTML fragments from the server rather than JSON data. The server responses determine what content is rendered.
* **Partial DOM updates**: htmx swaps portions of the DOM using AJAX requests, rather than performing full page reloads. The SDK and observability plugins load once and persist across all htmx interactions.
* **No client-side routing**: htmx uses `hx-boost` and `hx-push-url` attributes to update the browser URL, but does not use a client-side router.

The observability plugins work well with htmx because they use DOM mutation observers to track changes. When htmx swaps content in or out of the page, the session replay plugin automatically captures these updates. No additional configuration is needed for basic observability and session replay.

## Get started

Follow these steps to get started:

* [Install the plugins](#install-the-plugins)
* [Initialize the JavaScript SDK client](#initialize-the-client)
* [Configure the plugin options](#configure-the-plugin-options)
* [Explore supported features](#explore-supported-features)
* [Review observability data in LaunchDarkly](#review-observability-data-in-launchdarkly)

## Install the plugins

LaunchDarkly uses plugins to the JavaScript SDK to provide observability. Most customers use both the observability and session replay plugins. However, there is no dependency between them, and you can use only one or the other if you like.

The first step is to make both the SDK and the observability plugins available as dependencies.

If your htmx application uses a bundler such as Vite, esbuild, or webpack, install the packages with a package manager:

<CodeGroup>
  ```bash title="npm, JS Browser SDK v1" theme={null}
  npm install @launchdarkly/browser
  ```

  ```bash title="yarn, JS Browser SDK v1" theme={null}
  yarn add @launchdarkly/browser
  ```

  ```bash title="npm, JS SDK v4.x" theme={null}
  npm install @launchdarkly/js-client-sdk
  npm install @launchdarkly/observability
  npm install @launchdarkly/session-replay
  ```

  ```bash title="yarn, JS SDK v4.x" theme={null}
  yarn add @launchdarkly/js-client-sdk
  yarn add @launchdarkly/observability
  yarn add @launchdarkly/session-replay
  ```
</CodeGroup>

Then, import the plugins into your code:

<CodeGroup>
  ```js title="Import, JS Browser SDK v1" theme={null}
  import { initialize } from "@launchdarkly/browser";
  ```

  ```js title="Import, JS SDK v4.x" theme={null}
  import { initialize } from "@launchdarkly/js-client-sdk";
  import Observability, { LDObserve } from "@launchdarkly/observability";
  import SessionReplay, { LDRecord } from "@launchdarkly/session-replay";
  ```
</CodeGroup>

If your htmx application does not use a bundler, you can load the SDK and plugins using `<script>` tags in your HTML. Add these to your base template's `<head>` element so they load once and persist across all htmx navigations.

<Warning title="Do not use script tags from unpkg or jsDelivr in production">
  Do not use script tags with sources from unpkg or jsDelivr in production environments. These introduce a critical dependency on a third-party service. Use the unpkg and jsDelivr scripts only during development for ease of use when getting started.

  In production environments, we strongly recommend that you self-host the JavaScript SDK and observability plugins alongside your other JavaScript resources. To learn more, read [Make the SDK available with a script tag](/sdk/client-side/javascript#make-the-sdk-available-with-a-script-tag) in the JavaScript SDK reference guide.
</Warning>

<CodeGroup>
  ```html title="Self-hosted scripts (recommended for production)" theme={null}
      <head>
        <script src="path/to/htmx.min.js"></script>
        <script src="path/to/ldclient.min.js"></script>
        <script src="path/to/ld-observability.min.js"></script>
        <script src="path/to/ld-session-replay.min.js"></script>
        <script>
          var client = LDClient.initialize('example-client-side-id', {
            plugins: [
              new LDObservability(),
              new LDSessionReplay()
            ]
          });
        </script>
      </head>
  ```

  ```html title="Loading from jsDelivr (development only)" theme={null}
      <head>
        <script src="https://unpkg.com/htmx.org"></script>
        <script crossorigin="anonymous"
          src="https://cdn.jsdelivr.net/npm/@launchdarkly/js-client-sdk@EXAMPLE-VERSION">
        </script>
        <script crossorigin="anonymous"
          src="https://cdn.jsdelivr.net/npm/@launchdarkly/observability@EXAMPLE-VERSION">
        </script>
        <script crossorigin="anonymous"
          src="https://cdn.jsdelivr.net/npm/@launchdarkly/session-replay@EXAMPLE-VERSION">
        </script>
        <script>
          var client = LDClient.initialize('example-client-side-id', {
            plugins: [
              new LDObservability(),
              new LDSessionReplay()
            ]
          });
        </script>
      </head>
  ```
</CodeGroup>

## Initialize the client

Next, initialize the SDK and the plugins.

To initialize, you need your LaunchDarkly environment's client-side ID. This authorizes your application to connect to a particular environment within LaunchDarkly. To learn more, read [Initialize the client](/sdk/client-side/javascript#initialize-the-client) in the JavaScript SDK reference guide.

<Warning title="JavaScript observability SDK credentials">
  The JavaScript observability SDK requires a client-side ID. Client-side IDs are specific to each project and environment. They are not secret, and you can include them in client-side code. Do not embed a server-side SDK key in a client-side application.

  You can find client-side IDs and project keys in **Project settings**, on the **Environments** list. To learn more about key types, read [Keys](/sdk/concepts/client-side-server-side#keys).
</Warning>

In an htmx application, you should initialize the SDK once, typically in your base HTML template or application entry point. The SDK persists across htmx navigations because htmx swaps DOM content rather than performing full page reloads.

### Initialize SDK client and plugins together

Here's how to initialize the SDK and plugins:

<CodeGroup>
  ```js title="Initialize, JS Browser SDK v1" theme={null}
  const client = initialize('example-client-side-id', {
    observability: { /* observability plugin options here */ },
    sessionReplay: { /* session replay plugin options here */ }
  });
  ```

  ```js title="Initialize, JS SDK v4.x" theme={null}
  const client = initialize('example-client-side-id', {
    plugins: [ new Observability(), new SessionReplay() ]
  });
  ```
</CodeGroup>

### Initialize the plugins after the SDK client

You can initialize the observability and session replay plugins manually, after the SDK client is initialized.

This approach supports feature-flagged rollouts or dynamic initialization after end user consent. Both plugins use a `manualStart` option combined with `.start()` calls.

First, configure the plugins with `manualStart: true`:

<CodeGroup>
  ```js title="Manual start configuration, JS Browser SDK v1" theme={null}
  const client = initialize('example-client-side-id', {
    observability: {
      manualStart: true
      /* other observability plugin options here */
    },
    sessionReplay: {
      manualStart: true
      /* other session replay plugin options here */
    }
  });
  ```

  ```js title="Manual start configuration, JS SDK v4.x" theme={null}
  const client = initialize('example-client-side-id', {
    plugins: [
      new Observability({ manualStart: true }),
      new SessionReplay({ manualStart: true })
    ]
  });
  ```
</CodeGroup>

Then, start the plugins when appropriate, such as after receiving end user consent or when a feature flag enables observability.

Here's an example starting the observability plugin:

<CodeGroup>
  ```js title="Start observability plugin" theme={null}
      // Start observability after user consent or feature flag check
      if (userConsentReceived || featureFlagEnabled) {
        LDObserve.start();
      }
  ```

  ```js title="Start observability with feature flag" theme={null}
      // Example: Start observability based on a feature flag
      const observabilityEnabled = client.variation('enable-observability', false);
      if (observabilityEnabled) {
        LDObserve.start();
      }
  ```
</CodeGroup>

Here's an example with the session replay plugin:

<CodeGroup>
  ```js title="Start recording" theme={null}
      LDRecord.start({
        silent: false // if true, console.warn messages created in this method are skipped
      });
  ```

  ```js title="Stop recording" theme={null}
      LDRecord.stop();
  ```
</CodeGroup>

This approach allows you to:

* Feature-flag the rollout of observability to a subset of end users
* Wait for end user consent before starting data collection
* Dynamically enable observability based on runtime conditions
* Maintain compliance with privacy regulations

## Configure the plugin options

You can configure options for the observability plugins when you initialize the SDK. The plugin constructors take an optional object with the configuration details.

<Note title="Session replays are obscured by default">
  By default, session replays use the `strict` privacy setting, which obscures some data. Use <code>none</code> to turn off session replay obfuscation. This enables more detail on session replays, but may expose more of your customer data than your privacy or data retention policies allow. To learn more, read [Session replay config](/sdk/features/session-replay-config).
</Note>

Here is an example:

<CodeGroup>
  ```js title="Plugin options, JS Browser SDK v1" theme={null}
  const client = initialize('example-client-side-id', {
    observability: {
      tracingOrigins: true, // attribute frontend requests to backend domains
      networkRecording: {
        enabled: true,
        recordHeadersAndBody: true
      }
    },
    sessionReplay: {
      privacySetting: 'strict',
        // or 'default' to redact text matching common regex for PII
        // or 'none' to turn off obfuscation
    }
  });
  ```

  ```js title="Plugin options, JS SDK v4.x" theme={null}
  const client = initialize('example-client-side-id', {
    plugins: [
      new Observability({
        tracingOrigins: true, // attribute frontend requests to backend domains
        networkRecording: {
          enabled: true,
          recordHeadersAndBody: true
        }
      }),
      new SessionReplay({
        privacySetting: 'strict',
          // or 'default' to redact text matching common regex for PII
          // or 'none' to turn off obfuscation
      })
    ]
  });
  ```
</CodeGroup>

<Tip title="Network recording captures htmx requests">
  When `networkRecording` is enabled, the observability plugin automatically captures all HTTP requests made by htmx, including requests triggered by the `hx-get`, `hx-post`, `hx-put`, `hx-patch`, and `hx-delete` attributes. This gives you visibility into the performance and status of every htmx interaction.
</Tip>

For more information on plugin options, read [Configuration for client-side observability](/sdk/features/observability-config-client-side) and [Configuration for session replay](/sdk/features/session-replay-config).

## htmx-specific considerations

### Session replay and DOM swapping

The session replay plugin uses DOM mutation observers to record changes. htmx updates the page by swapping HTML content in and out of the DOM, which the mutation observers capture automatically. This means session replay works out of the box with htmx, regardless of which swap strategy you use (`innerHTML`, `outerHTML`, `beforebegin`, `afterend`, and so forth).

### Navigations with hx-boost

If your htmx application uses [`hx-boost`](https://htmx.org/attributes/hx-boost/) to convert standard links and forms into AJAX requests, the observability plugin handles these navigations correctly. The SDK stays initialized across boosted navigations because htmx swaps the page body content rather than performing a full page reload.

When `hx-boost` or `hx-push-url` updates the browser URL, the observability plugin detects this through the browser's History API and tracks it as a navigation event.

### Error tracking

The observability plugin automatically captures JavaScript errors through standard browser error handling. For htmx-specific errors, such as failed AJAX requests, the plugin captures these through network request monitoring when `networkRecording` is enabled.

If you want to track additional htmx-specific events, you can use the `LDObserve` API to log custom events:

<CodeGroup>
  ```js title="Using LDObserve to track htmx-specific events" theme={null}
      // Log htmx request errors as custom observability events
      document.addEventListener("htmx:responseError", function(evt) {
        LDObserve.error("htmx request failed", {}, {
          url: evt.detail.pathInfo.requestPath,
          status: evt.detail.xhr.status
        });
      });
  ```
</CodeGroup>

### Script persistence across navigations

In htmx applications, scripts loaded in the `<head>` element persist across `hx-boost` navigations by default. This means the LaunchDarkly SDK initializes once and continues running throughout the user's session. You do not need to reinitialize the SDK when htmx swaps page content.

If you use the htmx [head-support](https://htmx.org/extensions/head-support/) extension, be aware that it merges `<head>` elements during boosted navigations. Scripts that are textually identical are preserved and not re-executed. This is the correct behavior for the LaunchDarkly SDK.

## Set Content-Security-Policy (CSP)

If your application runs in an environment that enforces content security policies, you must set the `Content-Security-Policy` (CSP) in your application to tell the browser how your page can interact with third-party scripts.

Here are the policies you need to set to use the observability plugin:

* `connect-src: https://pub.observability.app.launchdarkly.com https://otel.observability.app.launchdarkly.com`: This policy allows connecting with LaunchDarkly servers to send recorded observability data.
* `worker-src: data: blob:`: This policy allows creating an inline web worker initialized by the `npm` package for this plugin.

Your CSP definition may look something like this:

<CodeGroup>
  ```html title="Example CSP definition" theme={null}
      <meta
        http-equiv="Content-Security-Policy"
        content="connect-src: https://pub.observability.app.launchdarkly.com https://otel.observability.app.launchdarkly.com; worker-src data: blob:;"
      />

  ```
</CodeGroup>

Alternatively, you can set the CSP in the HTML document response header `Content-Security-Policy`. Check your initial app HTML document load for the header to make sure you are setting it to the desired value.

## Explore supported features

The observability plugin supports the following features. After the SDK and plugins are initialized, you can access these from within your application:

* [Configuration for client-side observability](/sdk/features/observability-config-client-side)
* [Configuration for session replay](/sdk/features/session-replay-config#javascript)
* [Errors](/sdk/features/observability-errors#javascript)
* [Logs](/sdk/features/observability-logs#javascript)
* [Metrics](/sdk/features/observability-metrics#javascript)
* [Tracing](/sdk/features/observability-traces#javascript)

## Review observability data in LaunchDarkly

After you initialize the SDK and observability plugins, your application automatically starts sending observability data back to LaunchDarkly in the form of custom events. You can review this information in the LaunchDarkly user interface. To learn how, read [Observability](/home/observability).

Specifically, the observability data includes events that LaunchDarkly uses to automatically create the following metrics:

* Average, P95, and P99 Cumulative Layout Shift (CLS) per context (LaunchDarkly)
* Average, P95, and P99 Document Load Latency per context (LaunchDarkly)
* Percentage of users with errors (LaunchDarkly)
* Average, P95, and P99 First Contentful Paint (FCP) per context (LaunchDarkly)
* Average, P95, and P99 First Input Delay (FID) per context (LaunchDarkly)
* Average, P95, and P99 Interaction to Next Paint (INP) per context (LaunchDarkly)
* Average, P95, and P99 Largest Contentful Paint (LCP) (LaunchDarkly)
* Average, P95, and P99 Time to First Byte (TTFB) per context (LaunchDarkly)

To learn more, read [Metrics autogenerated from observability events](/home/metrics/autogen/observability).
