> ## Documentation Index
> Fetch the complete documentation index at: https://docs.honeycomb.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Get Started with Honeycomb for Web

> Instrument your web application with the Honeycomb Web Instrumentation package, built on OpenTelemetry, and get full visibility into your customer experience.

export const HnyIcon = ({alias, path, size = 16, iconColor}) => {
  const iconMap = {
    "home": "house.svg",
    "marker": "caretFilledDown.svg",
    "show-marker-options": "chatTextLeft.svg",
    "download": "arrowLineDown.svg",
    "trace-waterfall": "trace.svg",
    "show-query-details": "listDashes.svg",
    "table": "table.svg",
    "log-lines": "logLines.svg",
    "chart": "chartLine.svg",
    "show-settings": "gear.svg",
    "add": "plus.svg",
    "remove": "delete.svg",
    "persist": "caretDown.svg",
    "close": "close.svg",
    "copy": "copy.svg",
    "zoom-in": "magnifyingGlassPlus.svg",
    "zoom-out": "magnifyingGlassMinus.svg",
    "color-assignment": "drop.svg",
    "drag": "dots-six-vertical.svg",
    "drawer": "drawer.svg",
    "show-actions": "dotsThree.svg",
    "edit": "pencil.svg",
    "delete": "trash.svg",
    "move": "arrowsOutCardinal.svg",
    "show-legend": "circleInfo.svg",
    "usage-ok": "usageGood.svg",
    "usage-warning": "usageWarning.svg",
    "usage-danger": "usageDanger.svg",
    "open-query-builder": "query.svg",
    "home-menu": "house.svg",
    "query-menu": "query.svg",
    "boards-menu": "board.svg",
    "triggers-menu": "bell.svg",
    "slos-menu": "handshake.svg",
    "service-map-menu": "serviceMap.svg",
    "history-menu": "clockCounterClockwise.svg",
    "manage-data-menu": "cube.svg",
    "usage-menu": "usageGood.svg",
    "show-details": "dotsThreeVertical.svg",
    "resize-handle": "board-panel-resize-handle.png",
    "standard-dataset": "cube.svg",
    "trace-dataset": "cubeChat.svg",
    "all-datasets": "linkedSquares.svg",
    "share": "arrowBentRight.svg",
    "run-in-query-builder": "arrowSquareUpRight.svg",
    "link": "link.svg",
    "text": "text.svg",
    "receive": "arrowLineDown.svg",
    "process": "lightning.svg",
    "sample": "drop.svg",
    "send": "arrowLineUp.svg",
    "submit": "arrowUp.svg",
    "canvas-menu": "sparkle.svg",
    "canvas": "sparkle.svg",
    "private": "lockKey.svg",
    "shared": "people.svg",
    "expand": "caretDown.svg",
    "previous": "caretLeft.svg",
    "next": "caretRight.svg"
  };
  const iconBasePath = "/_assets/icons/";
  const iconPath = path || (alias ? `${iconBasePath}${iconMap[alias]}` : undefined);
  return <span className="hny-icon" style={{
    display: "inline-block",
    width: `${size}px`,
    height: `${size}px`,
    maskImage: `url(${iconPath})`,
    maskSize: "contain",
    maskRepeat: "no-repeat",
    maskPosition: "center",
    WebkitMaskImage: `url(${iconPath})`,
    WebkitMaskSize: "contain",
    WebkitMaskRepeat: "no-repeat",
    WebkitMaskPosition: "center",
    backgroundColor: iconColor || "var(--hny-icon-color)",
    verticalAlign: "middle"
  }} />;
};

export const CalloutLearn = ({children}) => {
  return <Callout icon="brain-circuit" color="#8B5CF6">
      {children}
    </Callout>;
};

Honeycomb offers web instrumentation, so you can get complete visibility into your customer experience, much like a Real User Monitoring (RUM) solution, but without any need for a proprietary agent.
Our Honeycomb Web Instrumentation package, a wrapper for the official OpenTelemetry Javascript SDK, allows you to get a rich set of data for your web service, so you can understand what users are experiencing when using your service and identify areas you can optimize.

<Tip>
  If you're using micro frontend architecture, visit [Observability and Micro Frontends](/get-started/best-practices/micro-frontends/) to see our recommendations for implementing OpenTelemetry and the Honeycomb OpenTelemetry Web SDK.
</Tip>

In this guide, we walk you through installing and configuring the default Honeycomb Web Instrumentation package, which will help you collect data on a variety of performance concerns for web services, as well as give you the necessary foundation for extending traces to any backend services that have OpenTelemetry instrumentation.

We will cover how to:

* Set up instrumentation on your web site or web app
* Send data to Honeycomb
* Start exploring your data in Honeycomb

After following this guide, you will:

* Have a rich set of default instrumentation available to help you debug your web service
* Know how to access your data in Honeycomb and find items of interest to investigate
* Be able to start improving your (and your team's) Observability practices

<CalloutLearn>
  For more structured learning, check out the [Honeycomb for Frontend Observability](https://academy.honeycomb.io/app/courses/beaa9b6e-0656-4722-a584-f4b8e6ca09f3) course from Honeycomb Academy.
</CalloutLearn>

## Before You Begin

Before you run the code, you'll need to do a few things.

### Sign up for Honeycomb

If you don't already have a Honeycomb account, you can sign up for one.
Signup is free.

Honeycomb stores your data in either a US-based or EU-based location, depending on your account region:

* [Create a US account](https://ui.honeycomb.io/signup)
* [Create an EU account](https://ui.eu1.honeycomb.io/signup)

### Get an API Key

For this guide, you'll need a [Honeycomb Ingest API Key](/configure/environments/manage-api-keys/#create-api-key) with the **Can create services/datasets** permission.
This lets your application send telemetry to Honeycomb and create a dataset for your service.

<Tip>
  When deploying to production, replace this key with a separate key that does not allow dataset creation.
  This helps protect your data structure in live environments.
</Tip>

Make note of your API key; for security reasons, you will not be able to see the key again, and you will need it later.

## Install the Honeycomb Web Instrumentation Package

We make our Honeycomb Web Instrumentation package available as an NPM package, so you can include it in your web bundle.

Navigate to the root directory of your service's repo, and then install the package:

<Tip>
  If your repo does not contain a `yarn.lock` file, install with NPM.
</Tip>

<CodeGroup>
  ```shell NPM theme={}
  npm install @honeycombio/opentelemetry-web '@opentelemetry/auto-instrumentations-web'
  ```

  ```shell Yarn theme={}
  yarn add @honeycombio/opentelemetry-web '@opentelemetry/auto-instrumentations-web'
  ```
</CodeGroup>

Confirm that the install was successful by opening your `package.json` file and checking that the `Dependencies` list now contains `@honeycomb/opentelemetry-web`.

## Send Data to Honeycomb

Once you have your Honeycomb Ingest API key and have installed Honeycomb's Web Instrumentation package, it's time to send telemetry data to Honeycomb!

### Instantiate Your Instrumentation

To get a comprehensive set of data about your application, you need to instantiate your instrumentation as early as possible in your application's lifecycle.

You can set up your web instrumentation using a popular JavaScript framework, using inline JavaScript, or using a JavaScript helper file.
To get started the most quickly, add your package configuration inline in the root instantiation file of your application:

<Info>
  Be sure to replace `[YOUR API KEY HERE]` and `[YOUR APPLICATION NAME HERE]` with the value of your Honeycomb API Ingest Key and the name of your service, respectively.
  We use the `serviceName` variable to name your dataset in Honeycomb, so replace it with a name that you will find useful.
</Info>

```javascript theme={}
// index.js or main.js

// other import statements...
import { HoneycombWebSDK } from '@honeycombio/opentelemetry-web';
import { getWebAutoInstrumentations } from '@opentelemetry/auto-instrumentations-web';

const configDefaults = {
  ignoreNetworkEvents: true,
  // propagateTraceHeaderCorsUrls: [
  // /.+/g, // Regex to match your backend URLs. Update to the domains you wish to include.
  // ]
}

const sdk = new HoneycombWebSDK({
  // endpoint: "https://api.eu1.honeycomb.io/v1/traces", // Send to EU instance of Honeycomb. Defaults to sending to US instance.
  debug: true, // Set to false for production environment.
  apiKey: '[YOUR API KEY HERE]', // Replace with your Honeycomb Ingest API Key.
  serviceName: '[YOUR APPLICATION NAME HERE]', // Replace with your application name. Honeycomb uses this string to find your dataset when we receive your data. When no matching dataset exists, we create a new one with this name if your API Key has the appropriate permissions.
  instrumentations: [getWebAutoInstrumentations({
    // Loads custom configuration for xml-http-request instrumentation.
    '@opentelemetry/instrumentation-xml-http-request': configDefaults,
    '@opentelemetry/instrumentation-fetch': configDefaults,
    '@opentelemetry/instrumentation-document-load': configDefaults,
  })],
});
sdk.start();

// Application instantiation code
```

#### Tips for Common JavaScript Frameworks

In this section, we show you how to set up your web instrumentation in popular JS frameworks.
Depending on the framework your application uses, instantiation may be handled differently.
If you run into trouble, please ask for help in our [Pollinators Community](/troubleshoot/community/).

<Tabs>
  <Tab title="React/Next.js">
    If you are using React (such as with `create-react-app` or via Next.js), you will need to wrap the Honeycomb code snippet in a component to instantiate it.

    1. Create a file called `observability.jsx|tsx` in your `components` directory, and insert the code:

       ```javascript theme={}
       // observability.jsx|tsx
       "use client"; // browser only: https://react.dev/reference/react/use-client
       import { HoneycombWebSDK } from '@honeycombio/opentelemetry-web';
       import { getWebAutoInstrumentations } from '@opentelemetry/auto-instrumentations-web';

       const configDefaults = {
         ignoreNetworkEvents: true,
         // propagateTraceHeaderCorsUrls: [
         // /.+/g, // Regex to match your backend URLs. Update to the domains you wish to include.
         // ]
       }
       export default function Observability(){
         try {
           const sdk = new HoneycombWebSDK({
             // endpoint: "https://api.eu1.honeycomb.io/v1/traces", // Send to EU instance of Honeycomb. Defaults to sending to US instance.
             debug: true, // Set to false for production environment.
             apiKey: '[YOUR API KEY HERE]', // Replace with your Honeycomb Ingest API Key.
             serviceName: '[YOUR APPLICATION NAME HERE]', // Replace with your application name. Honeycomb uses this string to find your dataset when we receive your data. When no matching dataset exists, we create a new one with this name if your API Key has the appropriate permissions.
             instrumentations: [getWebAutoInstrumentations({
               // Loads custom configuration for xml-http-request instrumentation.
               '@opentelemetry/instrumentation-xml-http-request': configDefaults,
               '@opentelemetry/instrumentation-fetch': configDefaults,
               '@opentelemetry/instrumentation-document-load': configDefaults,
             })],
           });
           sdk.start();
         } catch (e) {return null;}
         return null;
       }
       ```

    2. In your `Layout.jsx|tsx` file, import the component, and add it to your layout code:

       ```javascript theme={}
       // components/layout.tsx|jsx
       import Observability from "@/components/observability";
       // ...
         return (
           <html lang="en">
             <body>{children}</body>
             <Observability />
           </html>
         );
       ```
  </Tab>

  <Tab title="Vue">
    **Recommended file location:** `src/main.ts`

    Place code early, ideally just above the Vue instantiation, to ensure that your instrumentation is as accurate as possible.
    Find the line that imports `main.css`, and add the code directly afterwards:

    ```javascript theme={}
    import '._assets/main.css'

    // HONEYCOMB SNIPPET HERE

    // import { createApp } from 'vue'
    ```
  </Tab>

  <Tab title="Angular">
    <Warning>
      Make sure that your `package.js` lists version 0.37.0 or greater for `@opentelemetry/auto-instrumentations-web`. Versions prior to this had a conflict with Angular that prevented automatic instrumentation data from working.
    </Warning>

    The best place to put your code to instantiate early is the `src/main.ts` file, which is the [entry point](https://angular.io/guide/file-structure#application-source-files) of your application.
  </Tab>

  <Tab title="Ember">
    If you are using [ember-auto-import](https://github.com/embroider-build/ember-auto-import), then you can import the Honeycomb instrumentation packages directly.
    The best way to use Honeycomb in an Ember application is through an [Application Initializer](https://guides.emberjs.com/release/applications/initializers/#toc_application-initializers), which ensures that Honeycomb starts up as soon as possible.

    1. Use the Ember CLI to generate a new initializer named `observability`:

       ```shell theme={}
       ember generate instance-initializer observability
       ```

       You should see a new JS file named `observability.js` in the `app/instance-initializers/` directory.

    2. Add Honeycomb configuration code to the `observability.js` file:

       ```javascript theme={}
       import { HoneycombWebSDK } from '@honeycombio/opentelemetry-web';
       import { getWebAutoInstrumentations } from '@opentelemetry/auto-instrumentations-web';

       const configDefaults = {
         ignoreNetworkEvents: true,
         // propagateTraceHeaderCorsUrls: [
         // /.+/g, // Regex to match your backend URLs. Update to the domains you wish to include.
         // ]
       }

       export function initialize(owner) {
         const sdk = new HoneycombWebSDK({
           // endpoint: "https://api.eu1.honeycomb.io/v1/traces", // Send to EU instance of Honeycomb. Defaults to sending to US instance.
           debug: true, // Set to false for production environment.
           apiKey: '[YOUR API KEY HERE]', // Replace with your Honeycomb Ingest API Key.
           serviceName: '[YOUR APPLICATION NAME HERE]', // Replace with your application name. Honeycomb uses this string to find your dataset when we receive your data. When no matching dataset exists, we create a new one with this name if your API Key has the appropriate permissions.
           instrumentations: [getWebAutoInstrumentations({
             // Loads custom configuration for xml-http-request instrumentation.
             '@opentelemetry/instrumentation-xml-http-request': configDefaults,
             '@opentelemetry/instrumentation-fetch': configDefaults,
             '@opentelemetry/instrumentation-document-load': configDefaults,
           })],
         });
         sdk.start();
       }

       export default {
         initialize,
       };
       ```
  </Tab>
</Tabs>

### Confirm Data is Sent

Start up your application and look in the browser console to see debug information.
Once you see traces being sent in the console, you're ready to start using Honeycomb to explore your data.

If you encounter an error, visit [Troubleshooting](#troubleshooting) to explore solutions to common issues.

## Refine Your Implementation

Once data flows into Honeycomb, you can add custom data about your application to help with investigations.

### Add Application-Specific Resource Attributes

To optimize your data collection, we recommend that you add custom attributes that are specific to your application to every span.
You can specify extra attributes through the `resourceAttributes` configuration option.
This data will be available on every span your instrumentation emits, making it easier to correlate your data to the business information you care about.

```javascript theme={}
// index.js or main.js

// Other import statements
import { HoneycombWebSDK } from '@honeycombio/opentelemetry-web';
import { getWebAutoInstrumentations } from '@opentelemetry/auto-instrumentations-web';

const configDefaults = {
  ignoreNetworkEvents: true,
  // propagateTraceHeaderCorsUrls: [
  // /.+/g, // Regex to match your backend URLs. Update to the domains you wish to include.
  // ]
}

const sdk = new HoneycombWebSDK({
  // endpoint: "https://api.eu1.honeycomb.io/v1/traces", // Send to EU instance of Honeycomb. Defaults to sending to US instance.
  debug: true, // Set to false for production environment.
  apiKey: '[YOUR API KEY HERE]', // Replace with your Honeycomb Ingest API Key.
  serviceName: '[YOUR APPLICATION NAME HERE]', // Replace with your application name. Honeycomb uses this string to find your dataset when we receive your data. When no matching dataset exists, we create a new one with this name if your API Key has the appropriate permissions.
  instrumentations: [getWebAutoInstrumentations({
    // Loads custom configuration for xml-http-request instrumentation.
    '@opentelemetry/instrumentation-xml-http-request': configDefaults,
    '@opentelemetry/instrumentation-fetch': configDefaults,
    '@opentelemetry/instrumentation-document-load': configDefaults,
  })],
  resourceAttributes: { // Data in this object is applied to every trace emitted.
    "user.id": user.id, // Specific to your app.
    "user.role": user.role, // Specific to your app.
  },
});
sdk.start();

// Application instantiation code
```

### Connect Browser Traces with Backend Traces

To trace a request all the way from your browser through your distributed system, connect your frontend request traces to your backend traces.

If your application's backend and frontend are served from the same domain, you can connect traces automatically by using either `instrumentation-fetch` or `instrumentation-xml-http-request` automatic instrumentation.
If your browser application calls a separate API endpoint, then you must specify the requests to which you want the trace context header added in order to connect the traces.
To do this with the Honeycomb web instrumentation and send traces, uncomment the `propagateTraceHeaderCorsUrls` array and add regex to include all target domains.
This method allows you to propagate to your backend services without leaking trace IDs to third-party services.

```javascript theme={}
const configDefaults = {
  ignoreNetworkEvents: true,
  propagateTraceHeaderCorsUrls: [
    /.+/g, // Regex to match your backend URLs. Update to the domains you wish to include.
  ]
}
```

### Send Data to an OpenTelemetry Collector

In production, we recommend that you run an [OpenTelemetry Collector](/send-data/opentelemetry/collector/#browser-telemetry) and send traces to it from your browser application, which will allow you to control your Honeycomb API key and any data transformation.
Your OpenTelemetry Collector can then send the traces on to Honeycomb using your API key that you store in the Collector's configuration.

This example configuration of the Honeycomb Web Instrumentation package sends traces to your Collector:

```js theme={}
const sdk = new HoneycombWebSDK({
  debug: true, // Set to false for production environment.
  endpoint: "http(s)://<your-collector-url>",
  skipOptionsValidation: true // Because we are not including apiKey
  serviceName: '[YOUR APPLICATION NAME HERE]', // Replace with your application name. Honeycomb uses this string to find your dataset when we receive your data. When no matching dataset exists, we create a new one with this name if your API Key has the appropriate permissions.
  instrumentations: [getWebAutoInstrumentations({
    // Loads custom configuration for xml-http-request instrumentation.
    '@opentelemetry/instrumentation-xml-http-request': configDefaults,
    '@opentelemetry/instrumentation-fetch': configDefaults,
    '@opentelemetry/instrumentation-document-load': configDefaults,
  })],
});
sdk.start();
```

## Troubleshooting

Running into issues? Here are some common problems and ways to fix them.

<Tip>
  Still stuck?
  Check out our [Support Knowledge Base](/troubleshoot/customer-support/) or post a question in our [Pollinators Community](/troubleshoot/community/).
</Tip>

### Dataset Not Appearing in Honeycomb

We use the `apiKey` variable to send your data to Honeycomb.
Be sure you have replaced the placeholder value for it with your Honeycomb Ingest API Key and that your API key permissions include "Can create datasets".

If Honeycomb is successfully instantiating, but your API key is not included, you should see output similar to the following in your browser console:

<Frame>
  <img src="https://mintcdn.com/honeycomb/43K0N5kGXUhKPs19/_assets/images/start-building/web/debug-console.jpg?fit=max&auto=format&n=43K0N5kGXUhKPs19&q=85&s=1ebad638cb0b4d9bcc95b98dd2a56660" alt="Screenshot of Honeycomb UI on the API Keys page, showing the heading Ingest Keys and the button named 'Create Ingest Key'" width="1334" height="532" data-path="_assets/images/start-building/web/debug-console.jpg" />
</Frame>

### Dataset in Honeycomb has Unexpected Name

We use the `serviceName` variable to name your dataset in Honeycomb.
Be sure you have replaced the placeholder value for it with a name that you will find useful.

### "Navigator is undefined" Error in Next.js Application

If a "navigator is undefined" error appears when you attempt to start your local server while following Next.js instructions, it means the instrumentation is being run in a server-side rendering path.

To fix, try the first suggested solution before implementing the second solution:

1. **Add "use client" directive**: At the top of the file where you instantiate Honeycomb's web instrumentation, you can add the ["use client" directive](https://react.dev/reference/react/use-client), which tells React to only execute the file in a client environment.
   If that solution does not resolve the error, try step 2.

2. **Wrap the function in a try/catch block**: If you're using the client directive and still seeing an error, you can catch the error and avoid instantiation in server-side environments.
   By adding this, you ensure your app starts up even if the code is executed in a server-side environment.
   Refer to the example below:

   ```javascript theme={}
   try {
     const sdk = new HoneycombWebSDK({
       // endpoint: "https://api.eu1.honeycomb.io/v1/traces", // Send to EU instance of Honeycomb. Defaults to sending to US instance.
       debug: true, // Set to false for production environment.
       apiKey: '[YOUR API KEY HERE]', // Replace with your Honeycomb Ingest API Key.
       serviceName: '[YOUR APPLICATION NAME HERE]', // Replace with your application name. Honeycomb uses this string to find your dataset when we receive your data. When no matching dataset exists, we create a new one with this name if your API Key has the appropriate permissions.
       instrumentations: [getWebAutoInstrumentations({
         // Loads custom configuration for xml-http-request instrumentation.
         '@opentelemetry/instrumentation-xml-http-request': configDefaults,
         '@opentelemetry/instrumentation-fetch': configDefaults,
         '@opentelemetry/instrumentation-document-load': configDefaults,
       })],
     });
   } catch (e) {}
   ```

### Instrumentation is too Noisy

If there is an unexpected volume of events, we recommend disabling some auto-instrumentation.
The User Interaction instrumentation, specifically, can be quite noisy.
Disabling specific instrumentation is outlined in the example below:

```javascript theme={}
// index.js or main.js

// other import statements...
import { HoneycombWebSDK } from '@honeycombio/opentelemetry-web';
import { getWebAutoInstrumentations } from '@opentelemetry/auto-instrumentations-web';

const sdk = new HoneycombWebSDK({
  // ... rest of the config
  instrumentations: [getWebAutoInstrumentations({
    '@opentelemetry/instrumentation-xml-http-request': {
      enabled: false
    },
    '@opentelemetry/instrumentation-fetch': {
      enabled: false
    },
    '@opentelemetry/instrumentation-document-load': {
      enabled: false
    },
    '@opentelemetry/instrumentation-user-interaction': {
      enabled: false
    }
  })],
});
sdk.start();

// Application instantiation code
```
