Embed KorinAI on any HTML page
Drop the KorinAI chat widget into any website using a single CDN script. No framework required.
Quick Start
There’s a ready‑to‑use HTML playground: Playground. Open it in a browser to toggle Floating vs Page variants and copy generated embed code.
Include the script
<!-- jsDelivr (recommended) -->
<script src="https://cdn.jsdelivr.net/npm/@korinai/embed@latest/dist/embed.js"></script>
<!-- or unpkg -->
<!-- <script src="https://unpkg.com/@korinai/embed@latest/dist/embed.js"></script> -->
Optional stylesheet (nice defaults for the floating button/window):
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/@korinai/embed@latest/dist/embed.css"
/>
Add a container element
<div id="korin-floating-chat"></div>
Initialize
Prefer getAuthToken for short‑lived tokens. Use HTTPS origins.
<script>
// window.KorinAI is exposed by the IIFE build from the CDN
window.KorinAI.init({
target: "#korin-floating-chat",
variant: "floating", // 'floating' | 'page'
// Provider options
baseUrl: "https://api.korinai.com",
chatApi: "https://api.korinai.com/api/chat",
language: "en",
// authToken: "YOUR_STATIC_TOKEN", // not recommended
// getAuthToken: async () => "YOUR_DYNAMIC_TOKEN", // preferred
// Optional helpers
ensureStyles: true, // inject minimal positioning for the mount
// Component props (forwarded)
props: {
title: "Chat with KorinAI",
showFloatingButton: true,
triggerIconSize: 28,
// chatProps: { variant: 'flat', ui: { showAttach: true } }, // for PageChat
},
});
// Later if needed
// window.KorinAI.unmount('#korin-floating-chat');
// window.KorinAI.toggleFloatingChat(true); // open / close (floating)
</script>
Variants
- Floating widget: variant: 'floating' and mount to a positioned element (e.g. bottom‑right of the page).
- Full page widget: variant: 'page' and mount to a normal content container.
You can even mount both:
<div id="korin-page-chat" style="min-height: 520px"></div>
<div
id="korin-floating-chat"
style="position: fixed; bottom: 24px; right: 24px"
></div>
<script>
window.KorinAI.init({
target: "#korin-page-chat",
variant: "page",
ensureStyles: false,
baseUrl: "https://api.korinai.com",
chatApi: "https://api.korinai.com/api/chat",
language: "en",
props: { variant: "flat" },
});
window.KorinAI.init({
target: "#korin-floating-chat",
variant: "floating",
baseUrl: "https://api.korinai.com",
chatApi: "https://api.korinai.com/api/chat",
language: "en",
props: { title: "Chat" },
});
</script>
Notes
- Prefer getAuthToken for short‑lived tokens; avoid hardcoding secrets in HTML.
- Serve the CDN script over HTTPS and from trusted origins (jsDelivr or unpkg).
Translations
Override UI texts by passing a translations map (only the keys you need):
<script>
window.KorinAI.init({
target: "#korin-floating-chat",
baseUrl: "https://api.korinai.com",
variant: "floating",
language: "en",
translations: {
en: {
startChat: "Start chat",
thinking: "Thinking…",
preparingExperience: "Setting things up for you…",
},
id: {
startChat: "Mulai Obrolan",
thinking: "Memproses…",
},
},
props: { title: "Chat with KorinAI" },
});
</script>
For the full list of translation keys, see the ChatTranslations type in the @korinai/libs package.
Default chat translation object
Source of truth lives in packages/korin-libs/contexts/korinai-context.tsx as KORIN_TRANSLATIONS.
export const KORIN_TRANSLATIONS = {
en: {
startChat: "Start Chat",
closeChat: "Close Chat",
newChat: "New Chat",
chatHistory: "Chat History",
loadingConversation: "Loading conversation...",
noChatHistory: "No chat history yet",
startConversation: "Start a conversation to see it here",
previous: "Previous",
next: "Next",
page: "Page",
of: "of",
thinking: "Thinking...",
usingTool: "Using {toolName}...",
attachedFile: "Attached file",
sharedLink: "Shared a link",
failedToLoadHistory: "Failed to load chat history",
tryAgainLater: "Please try again later",
ai: "AI",
helloImYourAIAssistant: "Hello! I'm your AI assistant. How can I help you today? 👋",
preparingExperience: "Preparing your chat experience… Please ensure your API URL and API key are configured.",
templates: "Templates",
fileSizeError: "File size must be less than 10MB",
fileTypeError: "File type not supported",
uploadSuccess: "File uploaded successfully",
uploadFailed: "Upload failed",
dropFile: "Drop your file here",
retry: "Retry",
selectAgent: "Select Agent",
attachFile: "Attach File",
stopGenerating: "Stop generating",
sendMessage: "Send message",
noCredits: "No credits available",
selectFile: "Select File",
},
id: {
startChat: "Mulai Obrolan",
closeChat: "Tutup Obrolan",
newChat: "Obrolan Baru",
chatHistory: "Riwayat Obrolan",
loadingConversation: "Memuat percakapan...",
noChatHistory: "Belum ada riwayat obrolan",
startConversation: "Mulai percakapan untuk melihatnya di sini",
previous: "Sebelumnya",
next: "Selanjutnya",
page: "Halaman",
of: "dari",
thinking: "Memproses...",
usingTool: "Menggunakan {toolName}...",
attachedFile: "Berkas terlampir",
sharedLink: "Membagikan tautan",
failedToLoadHistory: "Gagal memuat riwayat obrolan",
tryAgainLater: "Silakan coba lagi nanti",
ai: "AI",
helloImYourAIAssistant: "Halo! Saya asisten AI Anda. Ada yang bisa saya bantu? 👋",
preparingExperience: "Menyiapkan pengalaman chat Anda… Pastikan URL API dan kunci API Anda telah dikonfigurasi.",
templates: "Template",
fileSizeError: "Ukuran file harus kurang dari 10MB",
fileTypeError: "Tipe file tidak didukung",
uploadSuccess: "File berhasil diunggah",
uploadFailed: "Gagal mengunggah",
dropFile: "Letakkan file Anda di sini",
retry: "Coba Lagi",
selectAgent: "Pilih Agen",
attachFile: "Lampirkan File",
stopGenerating: "Hentikan pembuatan",
sendMessage: "Kirim pesan",
noCredits: "Kredit tidak tersedia",
selectFile: "Pilih File",
},
} as const;
Troubleshooting
- Missing styles or odd positioning: set ensureStyles: true or include embed.css from the CDN.
- Auth errors: prefer getAuthToken and verify your token issuer, scope, and expiry.
- No messages: check baseUrl, chatApi (should be https://api.korinai.com/api/chat), and network console.
- Multiple mounts: you can call KorinAI.init twice (different targets). Use KorinAI.unmount(target) to tear down.
Reference
Below are the key configuration options supported by the embed script. These mirror the published @korinai/embed package.
InitOptions
Prop | Type | Description |
---|---|---|
target | Element | string | Element or CSS selector to mount into. |
variant | 'floating' | 'page' | Which UI to render. Default: 'floating'. |
props | Record<string, unknown> | Props forwarded to the rendered component. Default: {}. |
ensureStyles | boolean | Inject minimal positioning/styling for visibility. Default: true. |
debugOverlay | boolean | Shows a tiny “mounted” badge for debugging. Default: false. |
stylesheetHref | string | string[] | Load external stylesheet(s) if needed. |
verbose | boolean | Verbose logs to console. Default: false. |
baseUrl | string | API base URL for the provider. |
chatApi | string | Chat API endpoint. |
configLanguage | string | Provider config language field (advanced). |
minimumCreditsWarning | string | Threshold for low‑credits notice. |
authToken | string | Static token value. Prefer getAuthToken. |
language | string | Provider language prop. |
getAuthToken | () => Promise<string> | Async token retriever. |
translations | ChatTranslations | Optional i18n map to override built‑in texts. |
FloatingChat props
Prop | Type | Description |
---|---|---|
title | string | Chat title displayed in header. |
triggerIcon | ReactNode | Custom trigger content (replaces default icon). |
triggerIconSize | number | Size of the trigger icon in px. Default: 28. |
branding | { logoLightUrl?, logoDarkUrl?, logoSize?: {width:number; height:number}, headerLogoSize?: {width:number; height:number}, showHeaderLogo? } | Branding options for the UI. |
buttonClassName | string | Extra classes for the floating button wrapper. |
chatWindowClassName | string | Extra classes for the floating chat window container. |
open | boolean | Controlled open state. Pair with onOpenChange. |
defaultOpen | boolean | Uncontrolled initial open state. Default: false. |
onOpenChange | (open: boolean) => void | Called when open state changes. |
chatProps | PageChatProps | Props forwarded to the inner PageChat component. |
showFloatingButton | boolean | Whether to show the floating trigger button. Default: true. |
PageChat props
Prop | Type | Description |
---|---|---|
className | string | Extra class names for the outer container. |
title | string | Header title text. |
showCloseButton | boolean | Shows a close button. Default: false. |
onClose | () => void | Called when close is clicked. |
hideHistory | boolean | Hides history list. Default: false. |
pageSize | number | History page size. Default: 10. |
defaultRoomId | string | Initial room id. |
showRoomName | boolean | Show current room name. Default: true. |
onRoomChange | (roomId: string) => void | Notifies room change. |
onSend | ({ text: string, roomId: string }) => void | Called when sending a message. |
headerRightSlot | ReactNode | Custom content on header right. |
variant | 'card' | 'flat' | Visual style. Default: 'flat'. |
chatInputVariant | 'default' | 'compact' | Input density preset. |
throttleMs | number | Throttle user typing/sending. Default: 0. |
requestHeaders | Record<string,string> | Extra headers for API calls. Default: {}. |
requestBody | { requesterId?, participantEmail?, participantId?, roomId?, gallery_id?, file_caption?, file_url?, extra_context?, requesterEmail?, messageId?, secrets?: Array<{key:string; value:string}> } | Extra body fields for API calls. Default: {}. |
ui | { showStop?, showAttach?, showActions?, showAgentSelector?, defaultAgentUsername? } | UI feature toggles. |
branding | { logoLightUrl?, logoDarkUrl?, logoSize?: {width:number; height:number}, showHeaderLogo?, headerLogoSize?: {width:number; height:number} } | Branding for the header and content. |
Runtime API
- KorinAI.init(options) → { unmount(): void; el: Element }
- KorinAI.unmount(target)
- KorinAI.toggleFloatingChat(open?: boolean)
- Variant‑scoped globals: window.KorinAI.floating, window.KorinAI.page contain __open, reload(newOptions?), and diagnostic fields like __version?, __verbose?.
Examples:
<script>
const { unmount } = window.KorinAI.init({
target: "#korin-floating-chat",
variant: "floating",
baseUrl,
chatApi,
});
// Later
window.KorinAI.toggleFloatingChat(true);
unmount();
// Reload floating variant with new props at runtime
window.KorinAI.floating.reload({ props: { showFloatingButton: false } });
</script>