> ## Documentation Index
> Fetch the complete documentation index at: https://developer.watson-orchestrate.ibm.com/llms.txt
> Use this file to discover all available pages before exploring further.

# customPanels

The `customPanels` namespace provides methods for creating and controlling a custom panel overlay within the embedded chat interface. A custom panel is a full-size overlay that you can populate with arbitrary HTML content — such as login forms, terms and conditions screens, file download widgets, or any other custom UI element — without closing or replacing the chat.

Only one custom panel instance can be active at a time. To display different content, update the `hostElement` of the existing panel and call `open()` again rather than creating a new panel.

Custom panels are available only in embedded chat. They are not supported in standalone mode.

## Methods

| Method | Description |
| - | - |
| [`customPanels.getPanel()`](#getpanel) | Returns the single custom panel instance. |
| [`customPanel.open(config)`](#open) | Opens the custom panel and applies the given configuration. |
| [`customPanel.close()`](#close) | Closes and removes the custom panel. |

## Properties

| Property | Description |
| - | - |
| [`customPanel.hostElement`](#hostelement) | The `HTMLDivElement` where you inject the custom panel content. |

***

## `getPanel()`

Returns the single custom panel instance. Because only one panel can exist at a time, calling `getPanel()` always returns the same object. Obtain this reference before calling `open()` or `close()`.

### Syntax

```javascript theme={null}
const customPanel = instance.customPanels.getPanel();
```

### Returns

**CustomPanel**

Returns the custom panel object. Use the returned object to call `open()`, `close()`, and access `hostElement`.

***

## `open(config)`

Opens the custom panel and configures its header. Call this method after populating the panel's `hostElement` with the content you want to display.

You can call `open()` at any point in the conversation lifecycle: before the first user message, during a conversation, or in response to a tool result.

### Syntax

```javascript theme={null}
customPanel.open(config);
```

### Parameters

**config** `object`

Optional configuration object for the panel header. All properties are optional.

### `config` properties

**title** `string`

The title displayed in the panel header. Inherits the font family configured for the embedded chat.

* **Default**: No title is shown.

**hideBackButton** `boolean`

When `true`, hides the **Back** button in the panel header. The Back button returns the user to the previous panel view when clicked.

* **Default**: `false` (Back button is shown).

**hideCloseButton** `boolean`

When `true`, hides the **Close** button in the panel header. The Close button minimizes the entire chat window.

* **Default**: `false` (Close button is shown).

> [!NOTE]
>
> This property is ignored if `hideCloseButton` is set to `true` at the top-level chat configuration.

**hidePanelHeader** `boolean`

When `true`, hides the entire panel header bar, including the title and all buttons.

* **Default**: `false` (header is shown).

### Returns

**void**

Returns `void`.

***

## `close()`

Closes the currently open custom panel and removes it from the chat view. The chat returns to the previous view.

### Syntax

```javascript theme={null}
customPanel.close();
```

### Returns

**void**

Returns `void`.

***

## `hostElement`

The `HTMLDivElement` where you inject the content to display inside the custom panel. Set the `innerHTML` of this element before calling `open()`.

### Type

**HTMLDivElement**

A writable DOM element. Set `innerHTML` or append child elements to populate the panel.

***

## Examples

### Basic custom panel

The following example opens a custom panel with a title and a simple message:

```javascript theme={null}
function onChatLoad(instance) {
    instance.on('chat:ready', () => {
        const customPanel = instance.customPanels.getPanel();

        customPanel.hostElement.innerHTML = `
            <div style="padding: 24px;">
                <h2>Welcome</h2>
                <p>Please accept the terms and conditions before continuing.</p>
                <button id="accept-btn">Accept and continue</button>
            </div>
        `;

        customPanel.open({ title: 'Terms and Conditions' });

        document.getElementById('accept-btn').addEventListener('click', () => {
            customPanel.close();
        });
    });
}
```

### Panel without a header

The following example opens a custom panel with the header hidden:

```javascript theme={null}
const customPanel = instance.customPanels.getPanel();

customPanel.hostElement.innerHTML = `
    <div style="display: flex; flex-direction: column; align-items: center; padding: 32px;">
        <p>Please log in to continue.</p>
        <input type="text" placeholder="Username" />
        <input type="password" placeholder="Password" />
        <button id="login-btn">Log in</button>
    </div>
`;

customPanel.open({ hidePanelHeader: true });
```

### Replacing panel content without closing

Because only one panel instance exists at a time, update the `hostElement` and call `open()` again to display different content in the same panel:

```javascript theme={null}
const customPanel = instance.customPanels.getPanel();

// Show the first screen
customPanel.hostElement.innerHTML = `
    <div style="padding: 24px;">
        <p>Step 1: Verify your identity.</p>
        <button id="next-step">Continue</button>
    </div>
`;
customPanel.open({ title: 'Identity Verification', hideBackButton: true });

document.getElementById('next-step').addEventListener('click', () => {
    // Swap content without closing the panel
    customPanel.hostElement.innerHTML = `
        <div style="padding: 24px;">
            <p>Step 2: Enter your one-time password.</p>
            <input type="text" placeholder="OTP" />
            <button id="submit-otp">Submit</button>
        </div>
    `;
    customPanel.open({ title: 'One-Time Password', hideBackButton: true });
});
```

### Opening a panel in response to a tool result

The following example opens a custom panel when the agent returns a specific response type:

```javascript theme={null}
instance.on('receive', (event, instance) => {
    const content = event?.message?.content ?? [];
    const fileItem = content.find(item => item.response_type === 'file_download');

    if (fileItem) {
        const customPanel = instance.customPanels.getPanel();

        customPanel.hostElement.innerHTML = `
            <div style="padding: 24px;">
                <h3>Download Ready</h3>
                <p>Your file is available: <strong>${fileItem.title}</strong></p>
                <a href="${fileItem.source}" download>Download file</a>
            </div>
        `;

        customPanel.open({ title: 'File Download' });
    }
});
```

### Closing a panel programmatically

The following example closes the panel when a button inside the panel is clicked:

```javascript theme={null}
const customPanel = instance.customPanels.getPanel();

customPanel.hostElement.innerHTML = `
    <div style="padding: 24px;">
        <p>Your payment is confirmed.</p>
        <button id="done-btn">Return to chat</button>
    </div>
`;
customPanel.open({ title: 'Payment Complete', hideBackButton: true });

document.getElementById('done-btn').addEventListener('click', () => {
    customPanel.close();
});
```

***

## Considerations

### Single panel instance

Only one custom panel instance can exist at a time. Calling `getPanel()` always returns the same object. If you want to display different content, update `hostElement` and call `open()` again. The chat does not manage multiple panel instances.

### Panel sizing

The custom panel fills the entire chat window by default.

### Header color

The panel header uses the same background color as the main embedded chat header. This is configured in the chat settings and cannot be changed per-panel.

### Timing

> [!WARNING]
>
> Do not call `customPanels.getPanel()` before the chat instance initializes. Always access the panel inside the `onLoad` callback or after the `chat:ready` event fires.

```javascript theme={null}
// ✅ Correct: access panel after initialization
window.wxOConfiguration = {
    orchestrationID: '<tenantId>',
    chatOptions: {
        onLoad: function(instance) {
            const customPanel = instance.customPanels.getPanel();
            // Now safe to use customPanel
        }
    }
};
```

### Layout support

Custom panels are supported in the following embedded chat layouts:

* ✅ Floating
* ✅ Custom (including side panel)
* ✅ Fullscreen
* ❌ Standalone (not supported)

> **[Do you need practical examples?](https://www.ibm.com/docs/SSAVQO/deploy/web_chat_agent/tutorials/tutorials_lp.html)**
>
> Learn how to apply the features available for embedded chat into your implementation with guidance and examples.
