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

# JavaScript lifecycle events

> React to widget lifecycle events using settings hooks or standard JavaScript event listeners.

The Molin AI widget notifies your website at key moments in its lifecycle. You can use these hooks to trigger custom logic such as initializing integrations, adjusting page layout, or tracking analytics.

## Settings hooks (recommended)

The simplest and most reliable approach is to define callback functions directly in `window.molinSettings`. Because this object is defined before the widget script loads, the `onReady` hook is **guaranteed to fire**.

```html theme={null}
<script>
  window.molinSettings = {
    onReady: (e) => {
      console.log('Widget ready:', e.widgetId);
      window.Molin.openChat();
    },
    onChatOpen: (e) => {
      console.log('Chat opened:', e.widgetId);
    },
    onChatClose: (e) => {
      console.log('Chat closed:', e.widgetId);
    },
    onMessageSent: (e) => {
      console.log('Message sent:', e.widgetId);
    },
    onLinkClick: (e) => {
      console.log('Link clicked:', e.widgetId, e.url);
    },
  };
</script>
```

### Available hooks

| Hook            | Argument                            | Description                                         |
| --------------- | ----------------------------------- | --------------------------------------------------- |
| `onReady`       | `{ widgetId: string }`              | Called once when the widget has loaded and rendered |
| `onChatOpen`    | `{ widgetId: string }`              | Called whenever the chat window opens               |
| `onChatClose`   | `{ widgetId: string }`              | Called whenever the chat window closes              |
| `onMessageSent` | `{ widgetId: string }`              | Called when the user sends a message                |
| `onLinkClick`   | `{ widgetId: string, url: string }` | Called when the user clicks a link in the chat      |

<Note>Settings hooks can be combined with other `molinSettings` properties like `hidden` or `userData` in the same object.</Note>

## Window events (alternative)

The widget also dispatches standard `CustomEvent` events on the `window` object. Use this approach if you need multiple independent listeners for the same event, or if you prefer the DOM event API.

### Available events

#### `molin:ready`

Fired once when the widget has fully loaded its configuration and rendered for the first time.

```javascript theme={null}
window.addEventListener('molin:ready', (e) => {
  console.log('Molin widget is ready:', e.detail.widgetId);

  // safe to call widget methods now
  window.Molin.openChat({ message: 'Welcome!' });
});
```

| Property            | Type     | Description                            |
| ------------------- | -------- | -------------------------------------- |
| `e.detail.widgetId` | `string` | The unique ID of the widget that fired |

<Note>
  If your script loads after the widget, the `molin:ready` event may have already fired. In that case, check if `window.Molin` already exists as a fallback:

  ```javascript theme={null}
  if (window.Molin) {
    // widget is already ready
    onMolinReady();
  } else {
    window.addEventListener('molin:ready', onMolinReady);
  }
  ```

  This race condition does not exist with the settings hooks approach above, which is why it is recommended.
</Note>

#### `molin:chat-open`

Fired whenever the chat window is opened, whether by the user clicking the bubble, a popup click, the floating input, a programmatic call to `openChat()`, or a restored session on mobile.

```javascript theme={null}
window.addEventListener('molin:chat-open', (e) => {
  console.log('Chat opened for widget:', e.detail.widgetId);
});
```

| Property            | Type     | Description                            |
| ------------------- | -------- | -------------------------------------- |
| `e.detail.widgetId` | `string` | The unique ID of the widget that fired |

#### `molin:chat-close`

Fired whenever the chat window is closed, whether by the user clicking the close button, toggling the bubble, or a programmatic call to `closeChat()`.

```javascript theme={null}
window.addEventListener('molin:chat-close', (e) => {
  console.log('Chat closed for widget:', e.detail.widgetId);
});
```

| Property            | Type     | Description                            |
| ------------------- | -------- | -------------------------------------- |
| `e.detail.widgetId` | `string` | The unique ID of the widget that fired |

#### `molin:message-sent`

Fired when the user sends a message in the chat.

```javascript theme={null}
window.addEventListener('molin:message-sent', (e) => {
  console.log('Message sent in widget:', e.detail.widgetId);
});
```

| Property            | Type     | Description                            |
| ------------------- | -------- | -------------------------------------- |
| `e.detail.widgetId` | `string` | The unique ID of the widget that fired |

#### `molin:link-click`

Fired when the user clicks a link inside the chat conversation.

```javascript theme={null}
window.addEventListener('molin:link-click', (e) => {
  console.log('Link clicked:', e.detail.url);
});
```

| Property            | Type     | Description                            |
| ------------------- | -------- | -------------------------------------- |
| `e.detail.widgetId` | `string` | The unique ID of the widget that fired |
| `e.detail.url`      | `string` | The URL of the clicked link            |

### Full example

```html theme={null}
<script>
  function onMolinReady(e) {
    console.log('Widget ready:', e.detail.widgetId);
  }

  if (window.Molin) {
    onMolinReady({ detail: { widgetId: 'already-loaded' } });
  } else {
    window.addEventListener('molin:ready', onMolinReady);
  }

  window.addEventListener('molin:chat-open', () => {
    document.body.classList.add('molin-chat-open');
  });

  window.addEventListener('molin:chat-close', () => {
    document.body.classList.remove('molin-chat-open');
  });
</script>
```
