---
title: Embed Apps
description: >-
  Embed a published Datadog app in dashboards, notebooks, the Internal Developer
  Portal homepage, and the Catalog side panel.
breadcrumbs: Docs > Actions > Apps > Embed Apps
---

> For the complete documentation index, see [llms.txt](https://docs.datadoghq.com/llms.txt).

# Embed Apps

{% callout %}
##### Join the Preview!

Datadog Apps is in Preview. Use this form to request access.

[Request Access](https://www.datadoghq.com/product-preview/apps/)
{% /callout %}

Embed [Datadog Apps](https://docs.datadoghq.com/actions/datadog_apps.md) in other Datadog products to place operational tools directly in the surfaces where users investigate issues and take action. An embedded app can display its existing experience or respond to live context supplied by its host, such as a dashboard's time range and template variables.

## Prerequisites{% #prerequisites %}

- A built, uploaded, and published Datadog app
- Permission to use the app and edit the destination surface

For information about building, uploading, and publishing an app, see [Datadog Apps](https://docs.datadoghq.com/actions/datadog_apps.md).

## Embed an app{% #embed-an-app %}

{% alert level="info" %}
You can embed an app only in the Datadog UI. AI agents cannot embed apps.
{% /alert %}

After you upload and publish an app, you can add it to a supported Datadog surface without changing the app.

You can embed an app from the app itself, or from the surface where you want it to appear.

### Embed from the app{% #embed-from-the-app %}

1. In the [App list](https://app.datadoghq.com/app-builder/apps/list), open the published app you want to embed.
1. At the top of the page, click **+ Add to Dashboard**, or click the down arrow next to it and select a destination type from the menu.
1. Select the destination, then click **Save**.

### Embed from the destination{% #embed-from-the-destination %}

{% tab title="Dashboard" %}

1. Open the dashboard where you want to add the app.
1. Click **Add Widgets** to open the widget tray.
1. Select the **Apps** tab, then drag the **Apps** widget onto the dashboard.
1. In the editor, choose the app from the **Select app** dropdown. To limit the list to apps you own, enable **My Apps Only**.
1. Resize the widget, then save the dashboard.

{% /tab %}

{% tab title="Notebook" %}

1. Open the notebook where you want to add the app.
1. Add a cell and select the **Apps** cell type.
1. Choose the app from the dropdown.
1. Save the notebook.

The **Apps** cell uses the notebook's time range. To read the time range in the app, use the `datadogNotebook` input.
{% /tab %}

{% tab title="Catalog" %}

1. Navigate to [Catalog](https://app.datadoghq.com/services) and select a service.
1. In the service's side panel, select the **+ Add App** tab.
1. Choose the app from the dropdown. To limit the list to apps you own, enable **My Apps Only**.

{% /tab %}

After you embed the app, optionally configure the app to respond to its host's context, theme, or shared-link state. Then verify it. An embedded app displays the most recently published version, so publish your changes before embedding.

## Customize the embedded experience{% #customize-the-embedded-experience %}

If you haven't already, install the [`@datadog/apps-frontend`](https://www.npmjs.com/package/@datadog/apps-frontend) package in the app:

```shell
npm install @datadog/apps-frontend
```

To make an embedded app respond to its Datadog host, keep `DatadogAppProvider` at the application root. The provider establishes communication with the Datadog host, makes host inputs available to hooks, and preserves the app's URL state in shared Datadog links.

```typescript
import { DatadogAppProvider } from '@datadog/apps-frontend/embedding/react';

export function Root() {
  return (
    <DatadogAppProvider>
      <App />
    </DatadogAppProvider>
  );
}
```

The standard Datadog Apps scaffold includes this provider by default. Every `@datadog/apps-frontend` hook must render below it, so keep the provider in place when you restructure the app.

### Read host inputs{% #read-host-inputs %}

Host inputs are values and functions supplied by the Datadog product containing the app. For example, a dashboard can provide its current time range and template variables, while the Catalog side panel can provide the service it is displaying.

Host values update automatically when the surrounding product context changes.

Import `useDatadogAppInput` and the input schema for the relevant product surface:

```typescript
import { useDatadogAppInput } from '@datadog/apps-frontend/inputs/react';
import {
  datadogDashboard,
  datadogIdp,
  datadogNotebook,
  datadogServicePanel,
  datadogTheme,
} from '@datadog/apps-frontend/inputs/schema';
```

For the complete TypeScript definitions, see the [`@datadog/apps-frontend` source on npm](https://www.npmjs.com/package/@datadog/apps-frontend?activeTab=code).

#### Input states{% #input-states %}

`useDatadogAppInput` returns a state object whose `status` is `pending`, `ready`, `unavailable`, or `failed`. Values are available in `fields` only when the status is `ready`.

`unavailable` is a normal outcome rather than an error. It means the app is running on a surface that does not provide that input, such as reading `datadogDashboard` inside a notebook. Render a fallback for this case.

For example:

```typescript
function PanelHeader() {
  const panel = useDatadogAppInput(datadogServicePanel);

  switch (panel.status) {
    case 'pending':
      return <Spinner />;
    case 'unavailable':
      return <p>Open this app from a Catalog side panel.</p>;
    case 'failed':
      return <p>{panel.error.message}</p>;
    case 'ready':
      return (
        <header>
          {panel.fields.service}
          <button onClick={() => panel.fields.close()}>×</button>
        </header>
      );
  }
}
```

### Available inputs{% #available-inputs %}

{% tab title="Theme" %}
Use `datadogTheme` to respond to the theme currently used by the Datadog host.

```typescript
const theme = useDatadogAppInput(datadogTheme);
```

When the status is `ready`, `fields` provides:

```typescript
{
  theme: 'light' | 'dark';
}
```

The value updates when the user changes the Datadog theme.

If the app uses [DRUIDS](https://docs.datadoghq.com/actions/datadog_apps.md#ui-components), you can use `DruidsEnvironmentWithThemeInput` to apply the host theme automatically:

```typescript
import { DruidsEnvironmentWithThemeInput } from '@datadog/apps-frontend/druids/react';

<DatadogAppProvider>
  <DruidsEnvironmentWithThemeInput defaultThemePreference="light">
    <App />
  </DruidsEnvironmentWithThemeInput>
</DatadogAppProvider>
```

{% /tab %}

{% tab title="Dashboard" %}
Use `datadogDashboard` to read a dashboard's current time range and template variables.

```typescript
const dashboard = useDatadogAppInput(datadogDashboard);
```

When the status is `ready`, `fields` provides:

```typescript
{
  dashboard: {
    timeframe: {
      start: number;
      end: number;
      isLive: boolean;
    };
    templateVariables: {
      name: string;
      value: string;
      values?: string[];
      prefix?: string;
      default?: string;
    }[];
  };
}
```

Use the time range to scope the app's queries to the dashboard. Use template variables to apply the same service, environment, team, or other filters used by the surrounding dashboard.
{% /tab %}

{% tab title="Notebook" %}
Use `datadogNotebook` to read a notebook's current time range and template variables.

```typescript
const notebook = useDatadogAppInput(datadogNotebook);
```

When the status is `ready`, `fields` provides:

```typescript
{
  notebook: {
    timeframe: {
      start: number;
      end: number;
      isLive: boolean;
    };
    templateVariables: {
      name: string;
      value: string;
      values?: string[];
      availableValues?: string[];
      prefix?: string;
      default?: string;
    }[];
  };
}
```

Use this context to keep the app synchronized with the time range and filters applied to the surrounding notebook.
{% /tab %}

{% tab title="IDP homepage" %}
Use `datadogIdp` to read and change the app's position on the Internal Developer Portal homepage.

```typescript
const idp = useDatadogAppInput(datadogIdp);
```

When the status is `ready`, `fields` provides:

```typescript
{
  position: number;
  move: (inputs: { delta: number }) => number;
}
```

Use `position` to read the app's current position on the homepage. Call `move()` to move the app by a number of positions; it resolves to the app's new position.

```typescript
const newPosition = await idp.fields.move({ delta: 1 });
```

{% /tab %}

{% tab title="Catalog side panel" %}
Use `datadogServicePanel` to identify the service displayed in the Catalog side panel.

```typescript
const servicePanel = useDatadogAppInput(datadogServicePanel);
```

When the status is `ready`, `fields` provides:

```typescript
{
  service: string;
  close: () => undefined;
}
```

Use `service` to scope the app's content to the selected service. Call `close()` when an app action should close the side panel.
{% /tab %}

Do not assume that an input available on one product surface is available on another. Request only the context the app needs and provide a useful experience when that context is absent.

## Preserve state in shared links{% #preserve-state-in-shared-links %}

`DatadogAppProvider` preserves the app's query string and URL fragment in shared Datadog links. Store shareable state—such as selected services, filters, records, or in-app routes—in the URL. When another user opens the shared link, the embedded app can restore the same state. [Deep links](https://docs.datadoghq.com/actions/datadog_apps.md#share-embedded-app-state-with-deep-links) to a specific app state work without additional configuration, as long as the app remains wrapped in `DatadogAppProvider`.

## Verify the app{% #verify-the-app %}

After you embed the app, check the following on the surface where you embedded it:

- **Access**: Open the surface as a user who has permission to use the app but did not embed it. The app renders instead of a permission error.
- **Size**: Resize the app to the smallest size you expect users to configure. Content stays readable, without overflow or clipping.
- **Host context**: Change the host's context, such as a dashboard's time range or a template variable value, and confirm that the app updates. If the app renders its `unavailable` fallback instead, it is reading an input that the surface does not provide.
- **Shared links**: Change the state inside the app, copy the Datadog share link, and open it in a new browser session. The app restores the same state.

## Further reading{% #further-reading %}

Additional helpful documentation, links, and articles:

- [Datadog Apps](https://docs.datadoghq.com/actions/datadog_apps.md)
- [Embed App Builder Apps](https://docs.datadoghq.com/actions/app_builder/embedded_apps.md)
- [@datadog/apps-frontend package on npm](https://www.npmjs.com/package/@datadog/apps-frontend)
