# Install the web widget

The web widget is the live chat bubble that visitors see on your website. This page shows you how to create a website channel, add the snippet to your site (by hand or through WordPress, Shopify, Haravan, LadiPage, Sapo, Google Tag Manager, Webflow or React), and check that it works.

## Before you start

- The **Support** module is turned on for your business. If it is off, the widget stays hidden.
- Your role can manage Support. **Support → Channels** needs the `chat:manage` permission (Owner, Admin, Developer, or a member with "manage" on Support). See [Team and roles](https://hatang.dev/help/account/employees-and-roles).
- You know the domain the widget runs on, for example `shop.vn`.

## 1. Create a website channel

Each website is a channel with its own `site_id`, snippet and settings. A business can have several.

1. In the dashboard, open **Support → Channels → Add channel → Website**.
2. Fill in the form:

   | Field | What to enter |
   |---|---|
   | **Site name** | A name for your team, for example "Main store". It is also the title in the widget header; you can change it later. |
   | **Domain** | The host the widget runs on, for example `shop.vn`. The domain and all its subdomains are always allowed. |
   | **Extra allowed origins** | Optional. One origin per line for other places the widget must load, for example `https://app.example.com` or `http://localhost:3000`. |

3. Click **Create site**. The site page opens with the snippet ready.

To find it later, open **Support → Channels** and pick the website.

## 2. Copy the snippet

The **Install** card on the site page shows the snippet with your `site_id` filled in. Click **Copy snippet**.

```html
<script>window.$hatang=window.$hatang||[];window.HATANG_SITE_ID="site_123...";</script>
<script src="https://chat.hatang.dev/widget.js" async></script>
```

The first tag creates the command queue and sets your site ID. The second tag loads the widget loader. The loader is small and cached for five minutes; it fetches your site settings and then loads the versioned widget bundle. The widget renders inside a shadow DOM, so your page CSS does not leak into it and its CSS does not leak into your page.

> **Tip**
>
> If you prefer one tag, the loader also reads the site ID from a `data-site-id` attribute:
> `<script src="https://chat.hatang.dev/widget.js" data-site-id="site_123..." async></script>`.
> Use the two-tag form if you also call `$hatang.push(...)` before the widget loads, because the first tag creates the queue.

## 3. Paste it on every page

Paste the snippet just before `</body>` on every page where visitors should be able to chat. Platform steps:

| Platform | Steps |
|---|---|
| WordPress | Install the [Ha Tang Chat](https://wordpress.org/plugins/hatang-chat/) plugin (Plugins → Add New, search "Ha Tang Chat") and activate it. Open **Settings → Ha Tang Chat**, paste the Site ID and save. Logged-in users are identified automatically. See [WordPress](https://hatang.dev/docs/guides/integrations/wordpress.md). Without the plugin: Appearance → Theme File Editor → `footer.php`, paste the snippet before `</body>`. |
| Shopify | Online Store → Themes → **Edit code**. Open `layout/theme.liquid` and paste the snippet just before `</body>`. Save. The widget appears on every storefront page, including checkout pages that use the theme layout. |
| Haravan | Website → Giao diện → **Chỉnh sửa mã nguồn** (Haravan's labels for Website → Theme → Edit source code). Open `layout/theme.liquid`, paste the snippet before `</body>` and save. |
| LadiPage | Open the landing page, then in the toolbar choose **Thiết lập → Mã JavaScript/CSS** (Settings → JavaScript/CSS code). Paste the snippet into the code box, before `</body>`. Save, then Publish; the page shows chat only after it is published again. Install it on each landing page. |
| Sapo | Website → Themes → Actions → **Chỉnh sửa HTML/CSS** (Edit HTML/CSS). Open `Theme.bwt` and paste the snippet just before `</body>`. Save. |
| Google Tag Manager | Tags → New → **Custom HTML**, paste the snippet. Trigger: **All Pages**. Save and publish the container. Tick "Support document.write" only if your container requires it; the snippet does not need it. |
| Webflow | Project settings → **Custom code** → Footer code, paste the snippet. Publish. |
| React / Next.js | Install `@hatang/chat-react` and render `<HatangChat siteId="..." />` once in your root layout. See below. |
| Any HTML site | Paste the snippet before `</body>` on every page. The second tag can use `defer` instead of `async`. |

### React and Next.js

```bash
npm install @hatang/chat-react
```

```tsx title="app/layout.tsx"
import { HatangChat } from "@hatang/chat-react";

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en">
      <body>
        {children}
        <HatangChat siteId="site_123..." />
      </body>
    </html>
  );
}
```

`HatangChat` renders nothing on the server and loads the widget once in the browser. Use `useHatang()` or `hatang()` to call the [JavaScript API](https://hatang.dev/docs/guides/support/web-widget/javascript-api.md). Details are on the [React SDK](https://hatang.dev/docs/guides/sdks/react.md) page.

## 4. Check the installation

Open your website in a browser. The launcher button appears in the bottom-right corner (or where you set it in [Configure the widget](https://hatang.dev/docs/guides/support/web-widget/configuration.md)).

Back on the site page, the **Install** card shows **Installed on** `shop.vn`, **last seen** a few minutes ago once the widget has loaded on your domain. Until then it shows **Not detected yet**. The status is refreshed at most once a minute.

Send a test message from the widget. It appears in **Support → Inbox** as a new conversation.

## Which origins can load the widget

Every request the widget makes is checked against the page's `Origin`:

- The site's **Domain** and every subdomain of it are always allowed. With `shop.vn`, both `https://shop.vn` and `https://www.shop.vn` work.
- Each entry in **Extra allowed origins** must match exactly, including scheme and port: `http://localhost:3000` does not allow `http://localhost:5173`.
- Requests without an `Origin` header (for example from a server) are rejected unless they come from a [mobile app](https://hatang.dev/docs/guides/support/mobile-apps.md) with an app key.

Add your local development and preview URLs to **Extra allowed origins** while you test.

## Several websites

Create one website channel per domain or brand: **Support → Channels → Add channel → Website**. Each site has its own snippet, settings, identity secret and channels (Zalo, Messenger and others are attached to a website). All conversations land in the same **Inbox**.

## Troubleshooting

When the widget cannot load, it stays silent: no launcher and no error on the page. Open your browser's developer tools, then the **Network** tab, and look at the `bootstrap` request to `chat.hatang.dev`.

| What you see | Cause | Fix |
|---|---|---|
| `bootstrap` returns 403 with `origin_not_allowed` | The page's origin is not the site's domain, a subdomain of it, or an extra allowed origin. | Fix **Domain** or add the origin under **Extra allowed origins**. |
| `bootstrap` returns 403 with `site_disabled` | The site's **Status** is **Disabled (widget hidden)**. | Set it back to **Active** in the site settings. |
| `bootstrap` returns 403 with `product_disabled` | The Support module is turned off for the business. | Turn Support on again. See [Businesses and modules](https://hatang.dev/help/getting-started/modules). |
| `bootstrap` returns 429 | More than 30 bootstrap calls per minute from one IP address. | Wait a minute; check that the snippet is not loaded in a loop. |
| No `bootstrap` request at all | The site ID is missing or the snippet is not on the page. | Check the page source for `HATANG_SITE_ID`. |
| `bootstrap` succeeds but no launcher | The page is excluded by **Where the widget shows**, **Hide the bubble entirely when offline** is on and nobody is online, `hideLauncher` is set in `window.HATANG_CONFIG`, or the page runs in a cross-origin iframe. | See [Configure the widget](https://hatang.dev/docs/guides/support/web-widget/configuration.md). |
| Script or socket blocked in the console | Your Content Security Policy does not allow the widget. | Allow `https://chat.hatang.dev` in `script-src` and `connect-src`, and `wss://chat.hatang.dev` in `connect-src`. |

## Next steps

- [Configure the widget](https://hatang.dev/docs/guides/support/web-widget/configuration.md): colors, launcher, greeting, pre-chat form, office hours.
- [JavaScript API](https://hatang.dev/docs/guides/support/web-widget/javascript-api.md): open the chat from your own buttons and listen to events.
- [Identity verification](https://hatang.dev/docs/guides/support/web-widget/identity-verification.md): link chats to your signed-in users.

Source: https://hatang.dev/docs/guides/support/web-widget/install
