> For the complete documentation index, see [llms.txt](https://alhena.gitbook.io/docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://alhena.gitbook.io/docs/developer-reference/website-sdk/examples/custom-launcher-nudge.md).

# Custom Launcher & Nudge UI

This example shows how to hide Alhena's default launcher button and nudge UI and render **your own** in their place — while still being driven by the same standard [nudge](/docs/features/chat-widget/nudges.md) logic (page matching, timer/scroll triggers, dismissal rules) configured in your dashboard.

This is useful when you want the chat entry point and proactive prompts to match your site's design system exactly, while keeping the targeting and behavior managed in Alhena.

## What You'll Learn

* Hide the built-in launcher and nudge with the [`hideDefaultUI`](/docs/developer-reference/website-sdk/javascript-api.md#configuration-options-reference) config option
* Build a custom launcher button that opens/closes the widget with [`toggle()`](/docs/developer-reference/website-sdk/javascript-api.md#toggle-options)
* Render a custom nudge from the [`nudge:loaded`](/docs/developer-reference/website-sdk/events.md#nudge-loaded) and [`nudge:triggered`](/docs/developer-reference/website-sdk/events.md#nudge-triggered) events
* Run the standard nudge click behavior from your own UI with [`clickNudge()`](/docs/developer-reference/website-sdk/javascript-api.md#clicknudge)

## How It Works

| Piece                 | Role                                                                                                                                        |
| --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
| `hideDefaultUI: true` | Hides Alhena's default launcher button **and** nudge UI. The nudge events still fire.                                                       |
| `nudge:loaded`        | Fires when a nudge is matched for the current page — use it to populate (but keep hidden) your custom nudge.                                |
| `nudge:triggered`     | Fires when the standard show logic activates (timer elapsed / scroll threshold met, widget closed, not dismissed) — reveal your nudge here. |
| `clickNudge()`        | Runs the same analytics + open-conversation behavior as the built-in nudge, so your custom nudge behaves identically.                       |

{% hint style="info" %}
Register your event handlers inside a [`widget:loaded`](/docs/developer-reference/website-sdk/events.md#widget-loaded) handler (or after the SDK `<script>` tag) so the SDK is ready before you subscribe.
{% endhint %}

## Code Example

```html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Custom Launcher + Nudge</title>
    <style>
        body {
            font-family: system-ui, sans-serif;
            margin: 0;
            padding: 40px;
            min-height: 200vh; /* tall page so scroll-triggered nudges can fire */
            background: #f4f5f7;
        }

        /* ---- Custom launcher button (replaces the default Alhena launcher) ---- */
        #my-launcher {
            position: fixed;
            bottom: 24px;
            right: 24px;
            width: 60px;
            height: 60px;
            border-radius: 50%;
            border: none;
            cursor: pointer;
            background: #6c2bd9;
            color: #fff;
            font-size: 26px;
            box-shadow: 0 6px 18px rgba(0, 0, 0, 0.25);
            z-index: 999999;
            display: flex;
            align-items: center;
            justify-content: center;
        }

        /* ---- Custom nudge UI (built from nudge:loaded / nudge:triggered) ---- */
        #my-nudge {
            position: fixed;
            bottom: 96px;
            right: 24px;
            max-width: 300px;
            background: #fff;
            border-radius: 14px;
            padding: 16px;
            box-shadow: 0 10px 32px rgba(24, 39, 75, 0.18);
            z-index: 999999;
            display: none; /* hidden until nudge:triggered */
            font-size: 15px;
            color: #1f2937;
        }
        #my-nudge.visible { display: block; }
        #my-nudge .nudge-text { cursor: pointer; line-height: 1.4; }
        #my-nudge .nudge-btn {
            margin-top: 12px;
            background: #6c2bd9;
            color: #fff;
            border: none;
            border-radius: 999px;
            padding: 8px 16px;
            cursor: pointer;
            font-weight: 600;
        }
        #my-nudge .nudge-close {
            position: absolute;
            top: -10px;
            right: -10px;
            width: 26px;
            height: 26px;
            border-radius: 50%;
            border: none;
            background: #fff;
            box-shadow: 0 2px 6px rgba(0, 0, 0, 0.2);
            cursor: pointer;
        }
    </style>
</head>
<body>
    <h1>Custom Launcher + Custom Nudge</h1>
    <p>Scroll down / wait for the nudge trigger. Watch the console for event logs.</p>

    <!-- Your own launcher button -->
    <button id="my-launcher" aria-label="Open chat">💬</button>

    <!-- Your own nudge container (populated from SDK events) -->
    <div id="my-nudge" role="dialog" aria-label="Suggestion">
        <button class="nudge-close" aria-label="Dismiss">×</button>
        <div class="nudge-text"></div>
        <button class="nudge-btn" style="display:none"></button>
    </div>

    <!-- ============ 1. SDK config: hide the default launcher + nudge ============ -->
    <script>
        document.gleenConfig = {
            company: 'your-company-key',
            apiBaseUrl: 'https://app.alhena.ai',
            hideDefaultUI: true, // <-- hides default launcher button AND default nudge
        };
    </script>

    <!-- ============ 2. Load the SDK ============ -->
    <script src="https://app.alhena.ai/sdk/gleenWidget.js"></script>

    <!-- ============ 3. Wire up the custom UI to the JS APIs ============ -->
    <script>
        window.gleenWidget.on('widget:loaded', function () {
            const g = window.gleenWidget;

            const launcher = document.getElementById('my-launcher');
            const nudgeEl = document.getElementById('my-nudge');
            const nudgeTextEl = nudgeEl.querySelector('.nudge-text');
            const nudgeBtnEl = nudgeEl.querySelector('.nudge-btn');
            const nudgeCloseEl = nudgeEl.querySelector('.nudge-close');

            function hideNudge() {
                nudgeEl.classList.remove('visible');
            }

            // --- Custom launcher toggles the chat window ---
            launcher.addEventListener('click', function () {
                g.toggle();
                hideNudge(); // opening chat should dismiss the nudge
            });

            // Keep the launcher icon in sync with widget state (optional nicety)
            g.on('widget:opened', function () { launcher.textContent = '✕'; });
            g.on('widget:closed', function () { launcher.textContent = '💬'; });

            // --- nudge:loaded: a nudge was matched for this page; prepare (but hide) UI ---
            g.on('nudge:loaded', function (nudge) {
                console.log('[nudge:loaded]', nudge);

                // nudge.nudge_texts is an array; show the first
                nudgeTextEl.textContent = nudge.nudge_texts[0] || '';

                if (nudge.nudge_button_text) {
                    nudgeBtnEl.textContent = nudge.nudge_button_text;
                    nudgeBtnEl.style.display = '';
                } else {
                    nudgeBtnEl.style.display = 'none';
                }
            });

            // --- nudge:triggered: standard logic says show it now (timer/scroll/etc.) ---
            g.on('nudge:triggered', function (nudge) {
                console.log('[nudge:triggered]', nudge);
                nudgeEl.classList.add('visible');
            });

            // --- Clicking your custom nudge runs the SAME logic as the built-in nudge ---
            function fireNudgeClick() {
                g.clickNudge();
                hideNudge();
            }
            nudgeTextEl.addEventListener('click', fireNudgeClick);
            nudgeBtnEl.addEventListener('click', fireNudgeClick);

            // --- Dismiss your custom nudge ---
            nudgeCloseEl.addEventListener('click', hideNudge);
        });
    </script>
</body>
</html>
```

## Walkthrough

1. **Hide the default UI.** Setting `hideDefaultUI: true` in `document.gleenConfig` suppresses both Alhena's floating launcher button and its built-in nudge. The widget itself still loads, and all nudge events still fire.
2. **Build your launcher.** A plain `<button>` calls [`window.gleenWidget.toggle()`](/docs/developer-reference/website-sdk/javascript-api.md#toggle-options) to open and close the chat. Listening to [`widget:opened`](/docs/developer-reference/website-sdk/events.md#widget-opened) / [`widget:closed`](/docs/developer-reference/website-sdk/events.md#widget-closed) lets you swap the icon to reflect state.
3. **Prepare your nudge on `nudge:loaded`.** When a nudge matches the current page, the [`nudge:loaded`](/docs/developer-reference/website-sdk/events.md#nudge-loaded) event delivers its text and button label. Populate your element but keep it hidden.
4. **Reveal on `nudge:triggered`.** When the standard trigger condition is met, [`nudge:triggered`](/docs/developer-reference/website-sdk/events.md#nudge-triggered) fires — show your element.
5. **Click through with `clickNudge()`.** When the visitor clicks your nudge, call [`window.gleenWidget.clickNudge()`](/docs/developer-reference/website-sdk/javascript-api.md#clicknudge) so it fires the same analytics and opens the conversation exactly like the built-in nudge.

## Related

* [JavaScript API](/docs/developer-reference/website-sdk/javascript-api.md) - `toggle()`, `clickNudge()`, and the `hideDefaultUI` config option
* [Events Reference](/docs/developer-reference/website-sdk/events.md) - `nudge:loaded` and `nudge:triggered`
* [AI Nudges](/docs/features/chat-widget/nudges.md) - Configuring nudges in the dashboard


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://alhena.gitbook.io/docs/developer-reference/website-sdk/examples/custom-launcher-nudge.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
