> Meticulous records real user sessions from a web app and replays them against each commit to catch visual regressions.
>
> Full AI-readable docs index (complete project setup journey + every page as markdown): https://app.meticulous.ai/llms.txt

# Install the Meticulous recorder via a script tag

Please select your framework or build tool:

### NextJS with the /pages directory

## Installing on NextJS with the /pages directory

> **note**
> **Important: The Meticulous Recorder script should use the native `script` tag instead of the NextJS `Script` component, be the first script to load, and have no async or defer attributes**
>
> Libraries you depend on may snapshot references
> to `window.fetch` or `window.XMLHttpRequest` early in the page lifecycle, which means if Meticulous is not the first script to load
> it may not be able to record all the network
> responses required for your app to function ([learn more](/docs/how-to/ensure-recorder-captures-all-requests)). Therefore the recorder script
> must be the first script to load in order to be guaranteed to capture all network requests correctly. This means:
>
> 1. It should be added to your `index.html` file, before any other script tags.
> 2. It should not have any async or defer attributes set, and use the native `script` tag instead of the NextJS `Script` component
> 3. It should be present in the initial HTML returned from the server -- you cannot add the script tag dynamically using JavaScript, since if
> you do so the browser may execute the script after other scripts have loaded. If you need to include the script tag in your HTML only
> in certain environments then this must be done either server-side, or at build time by templating your HTML.
>
> If it's not possible to meet these requirements then you can [use an NPM dependency instead of a script tag](/docs/session-recording/recorder-npm-dependency). If you need to wait for a network request to complete before you know whether you should record the session then you can [buffer the requests in memory, and only send them later](/docs/session-recording/controlling-when-recording-starts-and-stops).

Add a script tag to your `_document.js` file within `Head`. If the layout doesn't yet have a `<Head>` tag then
you can add one within the `<Html>` tag.

```
### Dev & Staging Only

```jsx
<Head>
  ...
      {(process.env.NODE_ENV === "development" || process.env.VERCEL_ENV === "preview") && (
        // eslint-disable-next-line @next/next/no-sync-scripts
        <script
          data-recording-token="<RECORDING_TOKEN>"
          data-is-production-environment="false"
          src="https://snippet.meticulous.ai/v1/meticulous.js"
        />
      )}
  ...
</Head>
```
### All Environments

```jsx
<Head>
  ...
      // eslint-disable-next-line @next/next/no-sync-scripts
      <script
      data-recording-token="<RECORDING_TOKEN>"
      data-is-production-environment={process.env.NODE_ENV === "production" || process.env.VERCEL_ENV === "production"}
      src="https://snippet.meticulous.ai/v1/meticulous.js"
      />
  ...
</Head>
```
```

If you have any cross-origin or sandboxed iFrames then the recorder should be added to each of these iFrames as well as the main frame. If you have any issues setting up the recorder then click [here](https://calendly.com/gabriel-h/meticulous-demo-booking) to book a call with us.

## Validating installation

Once you add the Meticulous snippet, open your webapp (either locally or on the environment that you injected the snippet into) and record a session by clicking around on your web app.

If the snippet was installed successfully you should be able to view the recorded session in your
Meticulous dashboard in the **Sessions** section.

If you set a CSP policy on your application then you'll need to add [these](/docs/session-recording/csp-exceptions) CSP exceptions.

## I've installed the snippet but why do I not see any sessions in my Meticulous dashboard?

See [troubleshooting](/docs/how-to/troubleshoot-recorder) for more information on why this might be happening.

## Issues / questions?

We're always happy to help you with any issues you encounter while setting up or anything you might be unsure about.

Get in touch by emailing [support@meticulous.ai](mailto:support@meticulous.ai).
### NextJS with the /app directory

## Installing on NextJS with the /app directory

> **note**
> **Important: The Meticulous Recorder script should use the native `script` tag instead of the NextJS `Script` component, be the first script to load, and have no async or defer attributes**
>
> Libraries you depend on may snapshot references
> to `window.fetch` or `window.XMLHttpRequest` early in the page lifecycle, which means if Meticulous is not the first script to load
> it may not be able to record all the network
> responses required for your app to function ([learn more](/docs/how-to/ensure-recorder-captures-all-requests)). Therefore the recorder script
> must be the first script to load in order to be guaranteed to capture all network requests correctly. This means:
>
> 1. It should be added to your `index.html` file, before any other script tags.
> 2. It should not have any async or defer attributes set, and use the native `script` tag instead of the NextJS `Script` component
> 3. It should be present in the initial HTML returned from the server -- you cannot add the script tag dynamically using JavaScript, since if
> you do so the browser may execute the script after other scripts have loaded. If you need to include the script tag in your HTML only
> in certain environments then this must be done either server-side, or at build time by templating your HTML.
>
> If it's not possible to meet these requirements then you can [use an NPM dependency instead of a script tag](/docs/session-recording/recorder-npm-dependency). If you need to wait for a network request to complete before you know whether you should record the session then you can [buffer the requests in memory, and only send them later](/docs/session-recording/controlling-when-recording-starts-and-stops).

Add a script tag to your `/app/layout.tsx` or `/app/layout.jsx` file within `head`. If the layout doesn't yet have a `<head>` tag then
you can add one within the `<html>` tag.

```
### Dev & Staging Only

```jsx
<head>
  ...
      {(process.env.NODE_ENV === "development" || process.env.VERCEL_ENV === "preview") && (
        // eslint-disable-next-line @next/next/no-sync-scripts
        <script
          data-recording-token="<RECORDING_TOKEN>"
          data-is-production-environment="false"
          src="https://snippet.meticulous.ai/v1/meticulous.js"
        />
      )}
  ...
</head>
```
### All Environments

```jsx
<head>
  ...
      // eslint-disable-next-line @next/next/no-sync-scripts
      <script
      data-recording-token="<RECORDING_TOKEN>"
      data-is-production-environment={process.env.NODE_ENV === "production" || process.env.VERCEL_ENV === "production"}
      src="https://snippet.meticulous.ai/v1/meticulous.js"
      />
  ...
</head>
```
```

After adding the snippet you'll need to follow a [few additional steps](/docs/frameworks/nextjs/app-router) to ensure Meticulous can
correctly test your app.

If you have any cross-origin or sandboxed iFrames then the recorder should be added to each of these iFrames as well as the main frame. If you have any issues setting up the recorder then click [here](https://calendly.com/gabriel-h/meticulous-demo-booking) to book a call with us.

## Validating installation

Once you add the Meticulous snippet, open your webapp (either locally or on the environment that you injected the snippet into) and record a session by clicking around on your web app.

If the snippet was installed successfully you should be able to view the recorded session in your
Meticulous dashboard in the **Sessions** section.

If you set a CSP policy on your application then you'll need to add [these](/docs/session-recording/csp-exceptions) CSP exceptions.

## I've installed the snippet but why do I not see any sessions in my Meticulous dashboard?

See [troubleshooting](/docs/how-to/troubleshoot-recorder) for more information on why this might be happening.

## Issues / questions?

We're always happy to help you with any issues you encounter while setting up or anything you might be unsure about.

Get in touch by emailing [support@meticulous.ai](mailto:support@meticulous.ai).
### Nuxt

## Installing on NuxtJS

> **note**
> **Important: The Meticulous Recorder script should be the first script to load, and have no async or defer attributes**
>
> Libraries you depend on may snapshot references
> to `window.fetch` or `window.XMLHttpRequest` early in the page lifecycle, which means if Meticulous is not the first script to load
> it may not be able to record all the network
> responses required for your app to function ([learn more](/docs/how-to/ensure-recorder-captures-all-requests)). Therefore the recorder script
> must be the first script to load in order to be guaranteed to capture all network requests correctly. This means:
>
> 1. It should be added to your `index.html` file, before any other script tags.
> 2. It should not have any async or defer attributes set.
> 3. It should be present in the initial HTML returned from the server -- you cannot add the script tag dynamically using JavaScript, since if
> you do so the browser may execute the script after other scripts have loaded. If you need to include the script tag in your HTML only
> in certain environments then this must be done either server-side, or at build time by templating your HTML.
>
> If it's not possible to meet these requirements then you can [use an NPM dependency instead of a script tag](/docs/session-recording/recorder-npm-dependency). If you need to wait for a network request to complete before you know whether you should record the session then you can [buffer the requests in memory, and only send them later](/docs/session-recording/controlling-when-recording-starts-and-stops).

Install the Meticulous recorder plugin and add it to your Nuxt config. The plugin injects the recorder script as the first script tag in your app's `<head>`, with no async or defer attributes ([learn more](/docs/how-to/ensure-recorder-captures-all-requests)).

```shell
npm install @alwaysmeticulous/recorder-plugin --save-dev
```

Modify your `nuxt.config.ts` file to include the plugin:

```
### Dev Only (default)

```typescript
export default defineNuxtConfig({
  modules: [
    [
      "@alwaysmeticulous/recorder-plugin/nuxt",
      { recordingToken: "<RECORDING_TOKEN>" },
    ],
  ],
});
```
### All Environments

```typescript
export default defineNuxtConfig({
  modules: [
    [
      "@alwaysmeticulous/recorder-plugin/nuxt",
      {
        recordingToken: "<RECORDING_TOKEN>",
        enabled: "always",
      },
    ],
  ],
});
```
```

By default, the plugin injects the recorder only during Nuxt development builds. If you set `enabled: "always"`, the plugin will inject the recorder in every environment and automatically set `data-is-production-environment` based on Nuxt's detected mode.

By default Meticulous will stub out all requests to server side rendered pages, and so won't test server side rendered content. If you
use server side rendering and wish to test your server side rendered pages then please reach out to
[support@meticulous.ai](mailto:support@meticulous.ai), or [book a call with us](https://calendly.com/gabriel-h/meticulous-demo-booking), and we'll help you get set up.

If you have any cross-origin or sandboxed iFrames then the recorder should be added to each of these iFrames as well as the main frame. If you have any issues setting up the recorder then click [here](https://calendly.com/gabriel-h/meticulous-demo-booking) to book a call with us.

## Validating installation

Once you add the Meticulous snippet, open your webapp (either locally or on the environment that you injected the snippet into) and record a session by clicking around on your web app.

If the snippet was installed successfully you should be able to view the recorded session in your
Meticulous dashboard in the **Sessions** section.

If you set a CSP policy on your application then you'll need to add [these](/docs/session-recording/csp-exceptions) CSP exceptions.

## I've installed the snippet but why do I not see any sessions in my Meticulous dashboard?

See [troubleshooting](/docs/how-to/troubleshoot-recorder) for more information on why this might be happening.

## Issues / questions?

We're always happy to help you with any issues you encounter while setting up or anything you might be unsure about.

Get in touch by emailing [support@meticulous.ai](mailto:support@meticulous.ai).
### SvelteKit

## Installing on SvelteKit

> **note**
> **Important: The Meticulous Recorder script should be the first script to load, and have no async or defer attributes**
>
> Libraries you depend on may snapshot references
> to `window.fetch` or `window.XMLHttpRequest` early in the page lifecycle, which means if Meticulous is not the first script to load
> it may not be able to record all the network
> responses required for your app to function ([learn more](/docs/how-to/ensure-recorder-captures-all-requests)). Therefore the recorder script
> must be the first script to load in order to be guaranteed to capture all network requests correctly. This means:
>
> 1. It should be added to your `index.html` file, before any other script tags.
> 2. It should not have any async or defer attributes set.
> 3. It should be present in the initial HTML returned from the server -- you cannot add the script tag dynamically using JavaScript, since if
> you do so the browser may execute the script after other scripts have loaded. If you need to include the script tag in your HTML only
> in certain environments then this must be done either server-side, or at build time by templating your HTML.
>
> If it's not possible to meet these requirements then you can [use an NPM dependency instead of a script tag](/docs/session-recording/recorder-npm-dependency). If you need to wait for a network request to complete before you know whether you should record the session then you can [buffer the requests in memory, and only send them later](/docs/session-recording/controlling-when-recording-starts-and-stops).

**(A)** Add the Meticulous recorder script tag in a `<svelte:head>` tag at the top of your `__layout.svelte` file. It's important the script is the
first script, and async and defer are not set to true ([learn more](/docs/how-to/ensure-recorder-captures-all-requests)):

```
### Dev & Staging Only

```svelte
<svelte:head>
{#if !import.meta.env.PROD}
  <script
    data-recording-token="<RECORDING_TOKEN>"
    data-is-production-environment="false"
    src="https://snippet.meticulous.ai/v1/meticulous.js"
  ></script>
{/if}
</svelte:head>
```
### All Environments

```svelte
<svelte:head>
  <script
    data-recording-token="<RECORDING_TOKEN>"
    data-is-production-environment={import.meta.env.PROD}
    src="https://snippet.meticulous.ai/v1/meticulous.js"
  ></script>
</svelte:head>
```
```

**(B)** In your app.html, make sure that `%svelte.head%` is above any other scripts in the `<head>` tag:

Good:

```html
<head>
    %svelte.head%
    <script src="another-script.js"></script>
</head>
```

Bad:

```html
<head>
    <script src="another-script.js"></script>
    %svelte.head%
</head>
```

**(C)** Wire through the MODE environment variable, and make sure MODE is set to `production` only for production builds:

Add `mode: process.env.MODE || 'development'` to the `vite` section of your `kit` config in your `svelte.config.js` file. For example:

```javascript
const config = {
  kit: {
      vite: {
          // default to development as a guard
          mode: process.env.MODE || 'development',
      }
  },
}
```

For all builds that get deployed to production, build your application using:

```bash
MODE=production npm run build
```

And for all other builds, including builds that get deployed to staging stacks and preview URLs, build your app using:

```bash
MODE=development npm run build
```

or

```bash
MODE=staging npm run build
```

**(D)** If you want to test your server side rendered content, then contact us

By default Meticulous will stub out all requests to server side rendered pages, and so won't test server side rendered content. If you
use server side rendering and wish to test your server side rendered pages then please reach out to
[support@meticulous.ai](mailto:support@meticulous.ai), or [book a call with us](https://calendly.com/gabriel-h/meticulous-demo-booking), and we'll help you get set up.

If you have any cross-origin or sandboxed iFrames then the recorder should be added to each of these iFrames as well as the main frame. If you have any issues setting up the recorder then click [here](https://calendly.com/gabriel-h/meticulous-demo-booking) to book a call with us.

## Validating installation

Once you add the Meticulous snippet, open your webapp (either locally or on the environment that you injected the snippet into) and record a session by clicking around on your web app.

If the snippet was installed successfully you should be able to view the recorded session in your
Meticulous dashboard in the **Sessions** section.

If you set a CSP policy on your application then you'll need to add [these](/docs/session-recording/csp-exceptions) CSP exceptions.

## I've installed the snippet but why do I not see any sessions in my Meticulous dashboard?

See [troubleshooting](/docs/how-to/troubleshoot-recorder) for more information on why this might be happening.

## Issues / questions?

We're always happy to help you with any issues you encounter while setting up or anything you might be unsure about.

Get in touch by emailing [support@meticulous.ai](mailto:support@meticulous.ai).
### Vite

## Installing on Vite

> **note**
> **Important: The Meticulous Recorder script should be the first script to load, and have no async or defer attributes**
>
> Libraries you depend on may snapshot references
> to `window.fetch` or `window.XMLHttpRequest` early in the page lifecycle, which means if Meticulous is not the first script to load
> it may not be able to record all the network
> responses required for your app to function ([learn more](/docs/how-to/ensure-recorder-captures-all-requests)). Therefore the recorder script
> must be the first script to load in order to be guaranteed to capture all network requests correctly. This means:
>
> 1. It should be added to your `index.html` file, before any other script tags.
> 2. It should not have any async or defer attributes set.
> 3. It should be present in the initial HTML returned from the server -- you cannot add the script tag dynamically using JavaScript, since if
> you do so the browser may execute the script after other scripts have loaded. If you need to include the script tag in your HTML only
> in certain environments then this must be done either server-side, or at build time by templating your HTML.
>
> If it's not possible to meet these requirements then you can [use an NPM dependency instead of a script tag](/docs/session-recording/recorder-npm-dependency). If you need to wait for a network request to complete before you know whether you should record the session then you can [buffer the requests in memory, and only send them later](/docs/session-recording/controlling-when-recording-starts-and-stops).

Install the Meticulous recorder plugin and add it to your Vite config. The plugin injects the recorder script as the first script tag in your app's `<head>`, with no async or defer attributes ([learn more](/docs/how-to/ensure-recorder-captures-all-requests)).

```shell
npm install @alwaysmeticulous/recorder-plugin --save-dev
```

Modify your `vite.config.ts` file to include the plugin:

```
### Dev Only (default)

```typescript
import { defineConfig } from "vite";
import meticulous from "@alwaysmeticulous/recorder-plugin/vite";

export default defineConfig({
  plugins: [
    meticulous({
      recordingToken: "<RECORDING_TOKEN>",
    }),
  ],
});
```
### All Environments

```typescript
import { defineConfig } from "vite";
import meticulous from "@alwaysmeticulous/recorder-plugin/vite";

export default defineConfig({
  plugins: [
    meticulous({
      recordingToken: "<RECORDING_TOKEN>",
      enabled: "always",
    }),
  ],
});
```
```

By default, the plugin injects the recorder only during Vite development builds. If you set `enabled: "always"`, the plugin will inject the recorder in every environment and automatically set `data-is-production-environment` based on Vite's detected mode.

If you have any cross-origin or sandboxed iFrames then the recorder should be added to each of these iFrames as well as the main frame. If you have any issues setting up the recorder then click [here](https://calendly.com/gabriel-h/meticulous-demo-booking) to book a call with us.

## Validating installation

Once you add the Meticulous snippet, open your webapp (either locally or on the environment that you injected the snippet into) and record a session by clicking around on your web app.

If the snippet was installed successfully you should be able to view the recorded session in your
Meticulous dashboard in the **Sessions** section.

If you set a CSP policy on your application then you'll need to add [these](/docs/session-recording/csp-exceptions) CSP exceptions.

## I've installed the snippet but why do I not see any sessions in my Meticulous dashboard?

See [troubleshooting](/docs/how-to/troubleshoot-recorder) for more information on why this might be happening.

## Issues / questions?

We're always happy to help you with any issues you encounter while setting up or anything you might be unsure about.

Get in touch by emailing [support@meticulous.ai](mailto:support@meticulous.ai).
### rsbuild

## Installing on rsbuild

> **note**
> **Important: The Meticulous Recorder script should be the first script to load, and have no async or defer attributes**
>
> Libraries you depend on may snapshot references
> to `window.fetch` or `window.XMLHttpRequest` early in the page lifecycle, which means if Meticulous is not the first script to load
> it may not be able to record all the network
> responses required for your app to function ([learn more](/docs/how-to/ensure-recorder-captures-all-requests)). Therefore the recorder script
> must be the first script to load in order to be guaranteed to capture all network requests correctly. This means:
>
> 1. It should be added to your `index.html` file, before any other script tags.
> 2. It should not have any async or defer attributes set.
> 3. It should be present in the initial HTML returned from the server -- you cannot add the script tag dynamically using JavaScript, since if
> you do so the browser may execute the script after other scripts have loaded. If you need to include the script tag in your HTML only
> in certain environments then this must be done either server-side, or at build time by templating your HTML.
>
> If it's not possible to meet these requirements then you can [use an NPM dependency instead of a script tag](/docs/session-recording/recorder-npm-dependency). If you need to wait for a network request to complete before you know whether you should record the session then you can [buffer the requests in memory, and only send them later](/docs/session-recording/controlling-when-recording-starts-and-stops).

Install the Meticulous recorder plugin and add it to your rsbuild config. The plugin injects the recorder script as the first script tag in your app's `<head>`, with no async or defer attributes ([learn more](/docs/how-to/ensure-recorder-captures-all-requests)).

```shell
npm install @alwaysmeticulous/recorder-plugin --save-dev
```

Modify your `rsbuild.config.ts` file to include the plugin:

```
### Dev Only (default)

```typescript
import { defineConfig } from "@rsbuild/core";
import meticulous from "@alwaysmeticulous/recorder-plugin/rspack";

export default defineConfig({
  tools: {
    rspack: {
      plugins: [
        meticulous({
          recordingToken: "<RECORDING_TOKEN>",
        }),
      ],
    },
  },
});
```
### All Environments

```typescript
import { defineConfig } from "@rsbuild/core";
import meticulous from "@alwaysmeticulous/recorder-plugin/rspack";

export default defineConfig({
  tools: {
    rspack: {
      plugins: [
        meticulous({
          recordingToken: "<RECORDING_TOKEN>",
          enabled: "always",
        }),
      ],
    },
  },
});
```
```

By default, the plugin injects the recorder only during non-production rsbuild builds. If you set `enabled: "always"`, the plugin will inject the recorder in every environment and automatically set `data-is-production-environment` based on Rspack's detected mode.

If you have any cross-origin or sandboxed iFrames then the recorder should be added to each of these iFrames as well as the main frame. If you have any issues setting up the recorder then click [here](https://calendly.com/gabriel-h/meticulous-demo-booking) to book a call with us.

## Validating installation

Once you add the Meticulous snippet, open your webapp (either locally or on the environment that you injected the snippet into) and record a session by clicking around on your web app.

If the snippet was installed successfully you should be able to view the recorded session in your
Meticulous dashboard in the **Sessions** section.

If you set a CSP policy on your application then you'll need to add [these](/docs/session-recording/csp-exceptions) CSP exceptions.

## I've installed the snippet but why do I not see any sessions in my Meticulous dashboard?

See [troubleshooting](/docs/how-to/troubleshoot-recorder) for more information on why this might be happening.

## Issues / questions?

We're always happy to help you with any issues you encounter while setting up or anything you might be unsure about.

Get in touch by emailing [support@meticulous.ai](mailto:support@meticulous.ai).
### Storybook

## Installing on Storybook

> **note**
> **Important: The Meticulous Recorder script should be the first script to load, and have no async or defer attributes**
>
> Libraries you depend on may snapshot references
> to `window.fetch` or `window.XMLHttpRequest` early in the page lifecycle, which means if Meticulous is not the first script to load
> it may not be able to record all the network
> responses required for your app to function ([learn more](/docs/how-to/ensure-recorder-captures-all-requests)). Therefore the recorder script
> must be the first script to load in order to be guaranteed to capture all network requests correctly. This means:
>
> 1. It should be added to your `index.html` file, before any other script tags.
> 2. It should not have any async or defer attributes set.
> 3. It should be present in the initial HTML returned from the server -- you cannot add the script tag dynamically using JavaScript, since if
> you do so the browser may execute the script after other scripts have loaded. If you need to include the script tag in your HTML only
> in certain environments then this must be done either server-side, or at build time by templating your HTML.
>
> If it's not possible to meet these requirements then you can [use an NPM dependency instead of a script tag](/docs/session-recording/recorder-npm-dependency). If you need to wait for a network request to complete before you know whether you should record the session then you can [buffer the requests in memory, and only send them later](/docs/session-recording/controlling-when-recording-starts-and-stops).

If you want to record sessions using Storybook, you can add the Meticulous recorder script tag by
creating a `.storybook/preview-head.html` file and adding the following:

```html
<script
  data-recording-token="<RECORDING_TOKEN>"
  data-is-production-environment="false"
  src="https://snippet.meticulous.ai/v1/meticulous.js"
></script>
<script>
  // Record and replay Storybook events sent from the parent (manager) to the
  // component iframe. These events capture interactions in Storybook controls
  // and actions (e.g., switching between stories).
  if (window.Meticulous?.replay) {
    window.Meticulous.replay.addCustomEventListener(
      "storybook-event",
      (serializedData) =>
        window.postMessage(serializedData, "*")
      ,
    )
  } else {
    window.addEventListener("message", event => {
      // Check if it's a storybook event
      try {
        const data = JSON.parse(event.data)
        if (data.key === "storybook-channel") {
          if (window.Meticulous?.record) {
            window.Meticulous.record.recordCustomEvent(
              "storybook-event",
              event.data,
            )
          }
        }
      } catch (e) {
        // Not a JSON message, ignore
      }
    })
  }
</script>
```

For TypeScript type definitions for the `window.Meticulous` object, see [TypeScript Types for window.Meticulous](/docs/how-to/typescript-types).

If you have any cross-origin or sandboxed iFrames then the recorder should be added to each of these iFrames as well as the main frame. If you have any issues setting up the recorder then click [here](https://calendly.com/gabriel-h/meticulous-demo-booking) to book a call with us.

## Validating installation

Once you add the Meticulous snippet, open your webapp (either locally or on the environment that you injected the snippet into) and record a session by clicking around on your web app.

If the snippet was installed successfully you should be able to view the recorded session in your
Meticulous dashboard in the **Sessions** section.

If you set a CSP policy on your application then you'll need to add [these](/docs/session-recording/csp-exceptions) CSP exceptions.

## I've installed the snippet but why do I not see any sessions in my Meticulous dashboard?

See [troubleshooting](/docs/how-to/troubleshoot-recorder) for more information on why this might be happening.

## Issues / questions?

We're always happy to help you with any issues you encounter while setting up or anything you might be unsure about.

Get in touch by emailing [support@meticulous.ai](mailto:support@meticulous.ai).
### Any other framework or build tool

## Installing on any other framework or build tool

> **note**
> **Important: The Meticulous Recorder script should be the first script to load, and have no async or defer attributes**
>
> Libraries you depend on may snapshot references
> to `window.fetch` or `window.XMLHttpRequest` early in the page lifecycle, which means if Meticulous is not the first script to load
> it may not be able to record all the network
> responses required for your app to function ([learn more](/docs/how-to/ensure-recorder-captures-all-requests)). Therefore the recorder script
> must be the first script to load in order to be guaranteed to capture all network requests correctly. This means:
>
> 1. It should be added to your `index.html` file, before any other script tags.
> 2. It should not have any async or defer attributes set.
> 3. It should be present in the initial HTML returned from the server -- you cannot add the script tag dynamically using JavaScript, since if
> you do so the browser may execute the script after other scripts have loaded. If you need to include the script tag in your HTML only
> in certain environments then this must be done either server-side, or at build time by templating your HTML.
>
> If it's not possible to meet these requirements then you can [use an NPM dependency instead of a script tag](/docs/session-recording/recorder-npm-dependency). If you need to wait for a network request to complete before you know whether you should record the session then you can [buffer the requests in memory, and only send them later](/docs/session-recording/controlling-when-recording-starts-and-stops).

Add the recorder as the first script tag in your `<head>` tag. If you only want to record sessions in non-production environments then
you will need to template your HTML to only include the script tag in non-production environments (if this is not possible then you can
 [use an NPM dependency instead of a script tag](/docs/session-recording/recorder-npm-dependency#installation-instructions)).

```html
<head>
  ...
  <script
    data-recording-token="<RECORDING_TOKEN>"
    data-is-production-environment="<true/false>"
    src="https://snippet.meticulous.ai/v1/meticulous.js">
  </script>

  <!--Meticulous snippet should be added before your app -->
  ...
  <script src="main_app.js"></script>
</head>
```

If you have any cross-origin or sandboxed iFrames then the recorder should be added to each of these iFrames as well as the main frame. If you have any issues setting up the recorder then click [here](https://calendly.com/gabriel-h/meticulous-demo-booking) to book a call with us.

## Validating installation

Once you add the Meticulous snippet, open your webapp (either locally or on the environment that you injected the snippet into) and record a session by clicking around on your web app.

If the snippet was installed successfully you should be able to view the recorded session in your
Meticulous dashboard in the **Sessions** section.

If you set a CSP policy on your application then you'll need to add [these](/docs/session-recording/csp-exceptions) CSP exceptions.

## I've installed the snippet but why do I not see any sessions in my Meticulous dashboard?

See [troubleshooting](/docs/how-to/troubleshoot-recorder) for more information on why this might be happening.

## Issues / questions?

We're always happy to help you with any issues you encounter while setting up or anything you might be unsure about.

Get in touch by emailing [support@meticulous.ai](mailto:support@meticulous.ai).
