How to Embed a Single Dify Chat Application into Your Website
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 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:
GET https://<your-dify-host>/api/v1/chat/app/{appId}/embed.js
In 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:
- Loads the compiled React application from the
platformpackage. - Creates a container element with
id="dify-chat-app"if it does not already exist. - Mounts the root
ChatAppcomponent into that container, initializing the chat interface with the specifiedappId.
The client-side bootstrap logic resides in 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.
<!-- 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:
<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. 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:
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 (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 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.jsas implemented inpackages/api/src/controllers/chat.controller.ts. - Automatic mounting – The script auto-creates the
dify-chat-appcontainer and renders the React component defined inpackages/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. - Security options – Protect embeds using JWT-style tokens validated by
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 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) is dynamically generated by the backend controller in 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 explicitly strips iframe elements (disallowedElements: ['iframe']) to prevent XSS vulnerabilities. The script-based approach in 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.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →