# How to Embed a Single Dify Chat Application into Your Website

> Easily embed a single Dify Chat application into your website with a simple script tag. Get your Dify chat UI powered on any page to enhance user interaction and support.

- Repository: [lexmin0412/dify-chat](https://github.com/lexmin0412/dify-chat)
- Tags: how-to-guide
- Published: 2026-03-06

---

**You can embed a Dify Chat application into any website by adding a single `<script>` tag pointing to `https://<your-host>/api/v1/chat/app/<APP_ID>/embed.js`, which automatically bootstraps the React chat UI into a `<div id="dify-chat-app">` container.**

The `lexmin0412/dify-chat` repository provides a lightweight embed solution that lets you run a standalone Dify chat interface on any HTML page without deploying the full React/Next.js codebase. This implementation exposes a dedicated endpoint in [`packages/api/src/controllers/chat.controller.ts`](https://github.com/lexmin0412/dify-chat/blob/main/packages/api/src/controllers/chat.controller.ts) that serves a compiled JavaScript bundle, enabling seamless integration with existing websites through a few lines of code.

## Prerequisites

Before embedding the chat widget, ensure you have the following:

- **A running Dify Chat instance** – Deployed via Docker Compose, Vercel, or another supported method as documented in the repository README.
- **A valid App ID** – The unique identifier for your Dify application, found in the Dify dashboard under *App Settings → App ID*.
- **Optional: Public API Key** – Required only if you have enabled access protection; generate this under *Settings → API Keys* in your Dify dashboard.

## How the Embed Endpoint Works

The backend exposes the embed script at the following REST endpoint:

```text
GET https://<your-dify-host>/api/v1/chat/app/{appId}/embed.js

```

In [`packages/api/src/controllers/chat.controller.ts`](https://github.com/lexmin0412/dify-chat/blob/main/packages/api/src/controllers/chat.controller.ts), the route is defined as `router.get('/app/:appId/embed.js'…)` (lines 132–162). When requested, this controller returns a compiled JavaScript bundle that handles three critical tasks:

1. Loads the compiled React application from the `platform` package.
2. Creates a container element with `id="dify-chat-app"` if it does not already exist.
3. Mounts the root `ChatApp` component into that container, initializing the chat interface with the specified `appId`.

The client-side bootstrap logic resides in [`packages/platform/src/pages/chat-embed.tsx`](https://github.com/lexmin0412/dify-chat/blob/main/packages/platform/src/pages/chat-embed.tsx) (lines 1–48), which serves as the entry point for the embedded widget.

## Adding the Embed Code to Your Website

Insert the following HTML into your page, typically before the closing `</body>` tag. The script is self-initializing and requires no additional JavaScript configuration.

```html
<!-- Container for the chat widget (optional - created automatically if omitted) -->
<div id="dify-chat-app"></div>

<!-- Embed script - replace <HOST> and <APP_ID> with your values -->
<script
  src="https://<HOST>/api/v1/chat/app/<APP_ID>/embed.js"
  defer
></script>

```

The `defer` attribute ensures the script loads after the HTML parsing completes, preventing render blocking. Once loaded, the script fetches all necessary UI assets—including bundled CSS, icons, and fonts—from the platform package, requiring no external dependencies.

## Customizing the Chat Widget Appearance

Dify Chat uses **Tailwind CSS v4** for styling. You can override default colors and design tokens by defining CSS variables on the container element:

```html
<div id="dify-chat-app" style="--dc-primary: #4F46E5; --dc-bg: #f9fafb;">
</div>

```

Supported CSS variables are defined in [`packages/platform/src/theme/tokens.ts`](https://github.com/lexmin0412/dify-chat/blob/main/packages/platform/src/theme/tokens.ts). Common customization options include primary brand colors, background shades, and border radii. Because the styles use CSS custom properties, you can dynamically adjust the appearance using JavaScript or media queries without rebuilding the embed script.

## Implementing Access Control (Optional)

To restrict access to the embedded chat, append a public access token as a query parameter:

```text
https://<HOST>/api/v1/chat/app/<APP_ID>/embed.js?token=YOUR_PUBLIC_TOKEN

```

The backend validates this token against the `DIFY_PUBLIC_API_KEY` environment variable. The validation logic is implemented in [`packages/api/src/middleware/auth.ts`](https://github.com/lexmin0412/dify-chat/blob/main/packages/api/src/middleware/auth.ts) (lines 78–101), which checks the token signature before serving the embed bundle. If validation fails, the endpoint returns a 401 Unauthorized error, preventing unauthorized websites from loading your chat application.

## Troubleshooting Common Issues

| Symptom | Cause | Solution |
|---------|-------|----------|
| **Blank container, no UI rendered** | Incorrect `HOST` or `APP_ID`, or network failure | Verify the URL matches your running Dify instance and check the browser's Network tab for 404 errors. |
| **Styles not applying** | Content Security Policy (CSP) blocking script execution | Add `script-src 'self' https://<HOST>;` to your CSP headers to allow the embed script to load. |
| **"Invalid token" console error** | Missing or incorrect `?token=` parameter | Regenerate the Public API Key from your Dify dashboard and ensure it is URL-encoded in the request. |
| **Chat fails to initialize in strict environments** | The page uses restrictive iframe policies | Ensure your page does not strip script tags; note that [`packages/react-app/src/components/markdown-renderer/index.tsx`](https://github.com/lexmin0412/dify-chat/blob/main/packages/react-app/src/components/markdown-renderer/index.tsx) deliberately strips iframes (`disallowedElements: ['iframe']`), so always use the embed script method rather than iframe insertion. |

## Summary

- **Single-script integration** – Embedding requires only one `<script>` tag pointing to `/api/v1/chat/app/{appId}/embed.js` as implemented in [`packages/api/src/controllers/chat.controller.ts`](https://github.com/lexmin0412/dify-chat/blob/main/packages/api/src/controllers/chat.controller.ts).
- **Automatic mounting** – The script auto-creates the `dify-chat-app` container and renders the React component defined in [`packages/platform/src/pages/chat-embed.tsx`](https://github.com/lexmin0412/dify-chat/blob/main/packages/platform/src/pages/chat-embed.tsx).
- **Theme customization** – Override Tailwind CSS variables via inline styles or external CSS, with tokens defined in [`packages/platform/src/theme/tokens.ts`](https://github.com/lexmin0412/dify-chat/blob/main/packages/platform/src/theme/tokens.ts).
- **Security options** – Protect embeds using JWT-style tokens validated by [`packages/api/src/middleware/auth.ts`](https://github.com/lexmin0412/dify-chat/blob/main/packages/api/src/middleware/auth.ts).
- **Zero dependencies** – The bundle includes all React, CSS, and font assets, eliminating external dependency risks.

## Frequently Asked Questions

### Can I embed multiple Dify chat applications on the same page?

Yes, though the standard embed script targets a single App ID. To display multiple applications, include separate script tags with different `appId` values and distinct container IDs. You may need to modify the bootstrap logic in [`packages/platform/src/pages/chat-embed.tsx`](https://github.com/lexmin0412/dify-chat/blob/main/packages/platform/src/pages/chat-embed.tsx) to accept a custom container selector via a data attribute (e.g., `data-container-id="custom-id"`) if you require multiple isolated instances.

### Does the embed script work with server-side rendering (SSR) frameworks like Next.js?

The embed script is client-side only and expects a browser environment to mount the React root. In Next.js or similar frameworks, place the `<script>` tag inside a `useEffect` hook or use the `next/script` component with `strategy="afterInteractive"` to ensure it executes only in the browser, preventing hydration mismatches.

### How do I update the chat widget without modifying my website's HTML?

Simply redeploy your Dify Chat instance with the updated code. The embed script ([`embed.js`](https://github.com/lexmin0412/dify-chat/blob/main/embed.js)) is dynamically generated by the backend controller in [`packages/api/src/controllers/chat.controller.ts`](https://github.com/lexmin0412/dify-chat/blob/main/packages/api/src/controllers/chat.controller.ts), meaning clients always fetch the latest version on page load. For aggressive caching scenarios, consider versioning your CDN or adding cache-busting query parameters to the script URL.

### Why does the embed method use a script tag instead of an iframe?

The repository deliberately avoids iframe-based embedding for security and styling flexibility. As noted in the source analysis, [`packages/react-app/src/components/markdown-renderer/index.tsx`](https://github.com/lexmin0412/dify-chat/blob/main/packages/react-app/src/components/markdown-renderer/index.tsx) explicitly strips iframe elements (`disallowedElements: ['iframe']`) to prevent XSS vulnerabilities. The script-based approach in [`packages/platform/src/pages/chat-embed.tsx`](https://github.com/lexmin0412/dify-chat/blob/main/packages/platform/src/pages/chat-embed.tsx) provides sandboxed style isolation through Shadow DOM or CSS scoping while allowing seamless theme customization via CSS variables.