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

html
<!-- 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):

html
<link
  rel="stylesheet"
  href="https://cdn.jsdelivr.net/npm/@korinai/embed@latest/dist/embed.css"
/>

Add a container element

html
<div id="korin-floating-chat"></div>

Initialize

Prefer getAuthToken for short‑lived tokens. Use HTTPS origins.

html
<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:

html
<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):

html
<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.

ts
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

PropTypeDescription
targetElement | stringElement or CSS selector to mount into.
variant'floating' | 'page'Which UI to render. Default: 'floating'.
propsRecord<string, unknown>Props forwarded to the rendered component. Default: {}.
ensureStylesbooleanInject minimal positioning/styling for visibility. Default: true.
debugOverlaybooleanShows a tiny “mounted” badge for debugging. Default: false.
stylesheetHrefstring | string[]Load external stylesheet(s) if needed.
verbosebooleanVerbose logs to console. Default: false.
baseUrlstringAPI base URL for the provider.
chatApistringChat API endpoint.
configLanguagestringProvider config language field (advanced).
minimumCreditsWarningstringThreshold for low‑credits notice.
authTokenstringStatic token value. Prefer getAuthToken.
languagestringProvider language prop.
getAuthToken() => Promise<string>Async token retriever.
translationsChatTranslationsOptional i18n map to override built‑in texts.

FloatingChat props

PropTypeDescription
titlestringChat title displayed in header.
triggerIconReactNodeCustom trigger content (replaces default icon).
triggerIconSizenumberSize 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.
buttonClassNamestringExtra classes for the floating button wrapper.
chatWindowClassNamestringExtra classes for the floating chat window container.
openbooleanControlled open state. Pair with onOpenChange.
defaultOpenbooleanUncontrolled initial open state. Default: false.
onOpenChange(open: boolean) => voidCalled when open state changes.
chatPropsPageChatPropsProps forwarded to the inner PageChat component.
showFloatingButtonbooleanWhether to show the floating trigger button. Default: true.

PageChat props

PropTypeDescription
classNamestringExtra class names for the outer container.
titlestringHeader title text.
showCloseButtonbooleanShows a close button. Default: false.
onClose() => voidCalled when close is clicked.
hideHistorybooleanHides history list. Default: false.
pageSizenumberHistory page size. Default: 10.
defaultRoomIdstringInitial room id.
showRoomNamebooleanShow current room name. Default: true.
onRoomChange(roomId: string) => voidNotifies room change.
onSend({ text: string, roomId: string }) => voidCalled when sending a message.
headerRightSlotReactNodeCustom content on header right.
variant'card' | 'flat'Visual style. Default: 'flat'.
chatInputVariant'default' | 'compact'Input density preset.
throttleMsnumberThrottle user typing/sending. Default: 0.
requestHeadersRecord<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:

html
<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>