npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

axellero-chatbot-ui-react

v1.2.29

Published

Library for chatbot integrate

Readme

Axellero Chatbot UI

A modern, drop‑in chatbot UI component built with React. Works out‑of‑the‑box with OpenAI API and Open WebUI or any compatible backend such as Axellero Io.


✨ Features

  • Plug & Play — import, pass a token, chat
  • Multi-API Support — OpenAI API and OpenWebUI compatibility
  • File & Image Handling — upload, preview, and drag‑and‑drop support
  • Vision API Support — image analysis with base64 encoding
  • Voice Recording — live transcription and audio processing
  • Real-time Streaming — SSE support for live chat responses
  • Fully Themeable — light/dark themes with granular color overrides
  • Composable Components — header & footer slots for total branding control
  • TypeScript Support — full type safety and IntelliSense
  • SSR‑friendly — works with Next.js, Gatsby, and other frameworks
  • WCAG‑compliant — accessibility built-in

📦 Installation

npm i axellero-chatbot-ui-react  # or:  yarn add axellero-chatbot-ui-react

🚀 Quick Start

Basic Setup (Provider is required)

import 'axellero-chatbot-ui/style.css'
import { AxelleroChatProviders, AxelleroChatBotUi } from 'axellero-chatbot-ui-react'
;<AxelleroChatProviders
  config={{
    model: 'gpt-4o-mini',
    getAuthToken: () => 'Bearer sk-...',
    apiType: 'openai',
  }}
>
  <AxelleroChatBotUi />
</AxelleroChatProviders>

With Providers

import { AxelleroChatProviders, AxelleroChatBotUi } from 'axellero-chatbot-ui-react'
;<AxelleroChatProviders
  config={{
    model: 'gpt-4o-mini',
    getAuthToken: () => 'Bearer sk-...',
    apiType: 'openai',
  }}
  initialMessages={[{ id: '1', role: 'assistant', content: 'Hello!', timestamp: 0 }]}
>
  <AxelleroChatBotUi />
</AxelleroChatProviders>

OpenWebUI Setup

<AxelleroChatProviders
  config={{
    defaultUrl: 'http://localhost:8080/api', // OpenWebUI endpoint
    model: 'llama3.2-3b',
    getAuthToken: () => 'Bearer your-token',
    apiType: 'openwebui',
  }}
>
  <AxelleroChatBotUi />
</AxelleroChatProviders>

🏷️ Examples

Copy‑paste the snippet you need and tweak. Each example is self‑contained.

1. Basic Embed

<AxelleroChatProviders
  config={{
    defaultUrl: 'https://api.openai.com/v1',
    model: 'gpt-4o-mini',
    getAuthToken: () => 'Bearer sk-...',
  }}
>
  <AxelleroChatBotUi />
</AxelleroChatProviders>

2. Custom Header

import { AxelleroChatBotUi } from 'axellero-chatbot-ui-react'
import { ChatHeader } from './ChatHeader'
;<AxelleroChatProviders
  config={{
    defaultUrl: 'https://api.openai.com/v1',
    model: 'gpt-4o-mini',
    getAuthToken: () => 'Bearer sk-...',
  }}
>
  <AxelleroChatBotUi header={<ChatHeader />} />
</AxelleroChatProviders>
import { ClearMessagesButton, ChatHeaderProps } from 'axellero-chatbot-ui-react'

export const ChatHeader = ({ clearMessagesIcon }: ChatHeaderProps) => (
  <div className="axelleroChatBotUi-chat-header">
    <span className="axelleroChatBotUi-chat-title">My Assistant</span>
    <div />
    <ClearMessagesButton icon={clearMessagesIcon} />
  </div>
)

3. Custom Footer with File Handling

import './styles.css'
import {
  ChatFooterProps,
  ChatInput,
  ChatFileUploader,
  FilePreview,
  VoiceRecorder,
  useFilesUpload,
  useMessageSend,
  useRecordingAudio,
} from 'axellero-chatbot-ui-react'
import { FC } from 'react'

export const Footer: FC<ChatFooterProps> = ({ icons }) => {
  // File management hooks
  const {
    files,
    base64Images,
    countLoadingFiles,
    onUploadFiles: handleUploadFiles,
    onRemoveFile: handleRemoveFile,
    onClearFilesAndImages: handleClearFilesAndImages,
    onSetCountLoadingFiles: handleSetCountLoadingFiles,
  } = useFilesUpload()

  // Message handling hooks
  const {
    inputRef,
    message,
    messageGenerating,
    setMessage,
    onChangeMessage: handleChangeMessage,
    onSendMessage: handleSendMessage,
    onAbort: handleAbort,
  } = useMessageSend(files, base64Images, handleClearFilesAndImages)

  // Voice recording hooks
  const {
    isRecording,
    isAudioLoading,
    onTranscriptAudio: handleTranscriptAudio,
    onStartRecording: handleStartRecording,
  } = useRecordingAudio(setMessage, inputRef)

  return (
    <div className="axelleroChatBotUi-chat-footer-root">
      {/* File and image preview */}
      {(files.length > 0 || base64Images.length > 0 || countLoadingFiles > 0) && (
        <FilePreview
          filePreviewIcon={icons?.filePreviewIcon}
          fileDownloadIcon={icons?.downloadAttachment}
          fileRemoveIcon={icons?.removeAttachment}
          uploadingCount={countLoadingFiles}
          files={files}
          images={base64Images}
          onDelete={handleRemoveFile}
        />
      )}

      {/* Main footer row */}
      <div className="axelleroChatBotUi-chat-footer">
        {/* File uploader */}
        {!isRecording && (
          <ChatFileUploader
            uploadIcon={icons?.uploadAttachment}
            onFileUploaded={handleUploadFiles}
            onSetCountLoadingFiles={handleSetCountLoadingFiles}
          />
        )}

        {/* Text input */}
        {!isRecording && (
          <ChatInput
            ref={inputRef}
            message={message}
            onChange={handleChangeMessage}
            buttonDisabled={countLoadingFiles > 0}
            messageGenerating={messageGenerating}
            sendIcon={icons?.send}
            stopSendIcon={icons?.sendStop}
            onSendMessage={handleSendMessage}
            onStop={handleAbort}
          />
        )}

        {/* Voice recorder */}
        <VoiceRecorder
          isLoading={isAudioLoading}
          isRecording={isRecording}
          startRecordingIcon={icons?.voiceRecorder}
          stopRecordingIcon={icons?.voiceRecorderStop}
          onStart={handleStartRecording}
          onRecorded={handleTranscriptAudio}
        />
      </div>
    </div>
  )
}

4. Event Handlers

<AxelleroChatProviders config={config}>
  <AxelleroChatBotUi
    onFilesChange={(files) => console.log('Files changed:', files)}
    onAudioSend={(audio) => console.log('Audio sent:', audio)}
    onAudioTranscript={(transcript) => console.log('Transcript:', transcript)}
    onInputChange={(input) => console.log('Input changed:', input)}
  />
</AxelleroChatProviders>

🔌 API Reference

| Prop | Type | Default | Description | | ------------------------------------- | ------------------------------------ | --------------- | ------------------------------------------------ | | header | ReactNode | — | Custom element that replaces the default header. | | footer | ComponentType<FooterExternalProps> | — | Custom footer component with full control. | | theme | Theme | light palette | Light/dark + granular overrides. | | icons | Partial<IconSet> | built‑in icons | Replace any internal icon. | | messages | MessageValueContext[] | — | Controlled messages array. | | initialMessages | MessageValueContext[] | — | Initial messages to display. | | messageBubble | ComponentType<MessageValueContext> | — | Custom message bubble component. |

header

ReactNode rendered at the top of the chat container. When provided, the stock header is not rendered.

footer

ComponentType<FooterExternalProps> component that replaces the default footer. Provides full control over the chat interface with helper hooks for file management, message handling, and voice recording.

Provider: AxelleroChatProviders

Provider is required. It supplies config and message state to all chat components.

interface ConfigContextValue {
  getAuthToken: () => string // REQUIRED
  model: string // REQUIRED (e.g., 'gpt-4o-mini', 'llama3.2-3b')
  defaultUrl?: string // Optional: OpenWebUI endpoint; if omitted, OpenAI default URL is used
  apiType?: 'openai' | 'openwebui' // Optional: auto-detected by defaultUrl
  transport?: Transport // Optional: custom transport
  sendLastUserOnly?: boolean // Optional: if true, only last user message is sent
}

interface AxelleroChatProvidersProps {
  config: ConfigContextValue
  initialMessages?: MessageValueContext[]
  messages?: MessageValueContext[]
  onSetMessages?: Dispatch<SetStateAction<MessageValueContext[]>>
  withoutMsgHistory?: boolean // alias for sendLastUserOnly
}

API Detection:

  • If defaultUrl is provided → OpenWebUI API
  • If no defaultUrl → OpenAI API (https://api.openai.com/v1)

theme

Provide a plain object; unspecified keys fall back to the default palette.

Default Theme (Light)

const themeProp = {
  'root-width': '100%',
  'root-height': '100%',
  'font-family': 'system-ui, Avenir, Helvetica, Arial, sans-serif',
  'font-weight': 400,
  'line-height': 1.5,
  'font-size': '1rem',
  type: 'light' | 'dark',
  colors: {
    common: {
      gray50: '#f9fafb',
      gray100: '#f3f4f6',
      gray150: '#f0f0f0',
      gray200: '#e5e7eb',
      gray250: '#e6e6e6',
      gray300: '#d1d5db',
      gray500: '#6b7280',
      gray700: '#374151',
      gray900: '#222',
      blue500: '#2563eb',
      blue600: '#646cff',
      blue700: '#535bf2',
      'shadow-light': 'rgba(0,0,0,0.06)',
      'shadow-medium': 'rgba(0,0,0,0.08)',
      white87: 'rgba(255,255,255,0.87)',
      background: '#f9fafb',
      'font-color': '#222',
    },
    header: {
      border: '#00000017',
      title: '#646464',
    },
    voiceRecorder: {
      'loader-bg': 'rgba(0,0,0,0.10)',
    },
    messageBubble: {
      'bot-bg': '#f3f4f6',
      'bot-text': '#000',
      'user-bg': '#e0f2fe',
      'user-text': '#0c4a6e',
      'user-border': 'none',
      'bot-border': 'none',
      'table-bg': '#ffffff',
      'table-border': '#d1d5db',
      'table-head-bg': '#e2e8f0',
      'table-head-text': '#1e293b',
      'table-cell-text': '#1f2937',
      'table-row-even-bg': '#f9fafb',
      'table-row-hover-bg': '#f1f5f9',
      'table-shadow': 'rgba(0,0,0,0.06)',
    },
    emptyBox: {
      text: '#555',
      title: '#222',
      subtitle: '#666',
    },
    filesPreview: {
      'box-bg': '#f3f4f6',
      'icon-color': '#6b7280',
      'box-shadow': 'rgba(0,0,0,0.08)',
      'remove-bg': '#ef4444',
      'remove-border': '#fff',
      'remove-text': '#fff',
      filename: '#374151',
      'scrollbar-thumb': '#d1d5db',
      'overlay-bg': 'rgba(0,0,0,0.35)',
      'skeleton-mid': '#e5e7eb',
    },
    ui: {
      button: {
        bg: '#f9f9f9',
        text: '#222',
        'hover-bg': '#f0f0f0',
        'active-bg': '#e6e6e6',
        'hover-shadow': 'rgba(0,0,0,0.06)',
        'active-shadow': 'rgba(0,0,0,0.08)',
        'outline-green': '#16a34a',
        'outline-blue': '#2563eb',
        'gray-bg': '#e5e7eb',
        'gray-text': '#374151',
      },
      iconButton: {
        color: '#6b7280',
        'hover-bg': '#f0f0f0',
        'active-bg': '#e6e6e6',
        'hover-shadow': 'rgba(0,0,0,0.06)',
        'active-shadow': 'rgba(0,0,0,0.08)',
      },
      textArea: {
        border: '#e5e7eb',
        bg: '#f9fafb',
        text: '#374151',
        'focus-border': '#2563eb',
        shadow: 'rgba(0,0,0,0.06)',
      },
      loader: {
        'dot-bg': 'rgba(51,51,51,0.49)',
      },
    },
  },
}

icons

interface IconSet {
  clearMessages: ReactNode
  send: ReactNode
  sendStop: ReactNode
  voiceRecorder: ReactNode
  voiceRecorderStop: ReactNode
  uploadAttachment: ReactNode
  downloadAttachment: ReactNode
  removeAttachment: ReactNode
  filePreviewIcon: ReactNode
}

Pass only the icons you wish to override; the rest fall back to defaults.


🎯 Core Features

File & Image Handling

  • Multiple File Types: Support for images, documents, PDFs, and more
  • Image Preview: Base64 encoding for Vision API compatibility
  • Drag & Drop: Intuitive file upload interface
  • File Management: Upload, preview, download, and remove files

Voice Recording

  • Audio Capture: Record voice messages directly in the chat
  • Transcription: Automatic speech-to-text conversion
  • Real-time Processing: Live audio feedback and status

Message Streaming

  • SSE Support: Real-time streaming responses
  • Chunk Processing: Handle partial responses gracefully
  • Error Handling: Robust error handling for network issues

API Compatibility

  • OpenAI API: Full compatibility with GPT models and Vision API
  • OpenWebUI: Support for local and remote OpenWebUI instances
  • Custom Transport: Extensible transport layer for custom backends

🏗️ Architecture

The component follows a modular architecture with clear separation of concerns:

  • Entities: Core data models (messages, files, images)
  • Features: Business logic (chat, file upload)
  • Widgets: UI components (header, footer, messages)
  • Shared: Utilities, contexts, and common functionality

📝 License

MIT

© 2025 Axellero LLC

Custom API Implementation

You can completely replace the default API logic by providing a customCompletionsAPI function in your config. This gives you full control over the API requests and responses.

Basic Usage

const config: ConfigContextValue = {
  model: 'gpt-4',
  getAuthToken: () => 'your-token',
  customCompletionsAPI: async (config, history) => {
    // Your custom API logic here
    const response = await fetch('https://api.openai.com/v1/chat/completions', {
      method: 'POST',
      headers: {
        Authorization: `Bearer ${config.getAuthToken()}`,
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        model: config.model,
        messages: history,
        stream: true,
        temperature: 0.7,
        max_tokens: 1000,
        // Add any additional fields you need
      }),
    })

    // Handle the response as you prefer
    // You have full control over the implementation
  },
}

Advanced Example with Custom Fields

const config: ConfigContextValue = {
  model: 'gpt-4',
  getAuthToken: () => 'your-token',
  customCompletionsAPI: async (config, history) => {
    const response = await fetch('https://api.openai.com/v1/chat/completions', {
      method: 'POST',
      headers: {
        Authorization: `Bearer ${config.getAuthToken()}`,
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        model: config.model,
        messages: history,
        stream: true,
        // Advanced parameters
        temperature: 0.7,
        max_tokens: 1000,
        presence_penalty: 0.1,
        frequency_penalty: 0.1,
        top_p: 0.9,
        user: 'custom-user-id',
        logit_bias: { '1234': -100 }, // Block specific tokens
        // Function calling
        functions: [
          {
            name: 'get_weather',
            description: 'Get current weather',
            parameters: {
              type: 'object',
              properties: {
                location: { type: 'string' },
              },
            },
          },
        ],
        function_call: 'auto',
      }),
    })

    // Custom SSE handling
    const reader = response.body?.getReader()
    const decoder = new TextDecoder()

    while (true) {
      const { done, value } = await reader.read()
      if (done) break

      // Your custom chunk processing logic
      const chunk = decoder.decode(value, { stream: true })
      // Process chunks as needed
    }
  },
}

Advanced Example with Full Message Control

const config: ConfigContextValue = {
  model: 'gpt-4',
  getAuthToken: () => 'your-token',
  customCompletionsAPI: async (config, history) => {
    // Создаем ID для сообщения бота
    const botMsgId = nanoid()

    // Добавляем сообщение бота в UI (это нужно делать в контексте React)
    // Для этого вам нужно передать callback или использовать другой способ
    const addBotMessage = (content: string, error?: string) => {
      // Здесь вы обновляете сообщения в UI
      // Например, через callback или через контекст
    }

    try {
      const response = await fetch('https://api.openai.com/v1/chat/completions', {
        method: 'POST',
        headers: {
          Authorization: `Bearer ${config.getAuthToken()}`,
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({
          model: config.model,
          messages: history,
          stream: true,
          temperature: 0.7,
          max_tokens: 1000,
        }),
      })

      if (!response.ok) {
        throw new Error(`API request failed: ${response.status}`)
      }

      // Обрабатываем SSE и обновляем UI
      const reader = response.body?.getReader()
      const decoder = new TextDecoder()
      let fullText = ''

      while (true) {
        const { done, value } = await reader.read()
        if (done) break

        const chunk = decoder.decode(value, { stream: true })
        fullText += chunk

        // Обновляем сообщение бота в UI
        addBotMessage(fullText)
      }
    } catch (error) {
      // Обрабатываем ошибки и обновляем UI
      addBotMessage('', error.message)
    }
  },
}

Important: When using customCompletionsAPI, you need to handle all UI updates yourself. The library only provides the basic structure.

What the Library Does vs What You Control

When using customCompletionsAPI, here's the division of responsibilities:

Library does:

  • ✅ Adds user message to chat
  • ✅ Clears input and files
  • ✅ Calls your custom function

You control:

  • ❌ Bot message creation and management
  • ❌ Abort controller for cancellation
  • ❌ Error handling and display
  • ❌ Message content updates
  • ❌ Loading states
  • ❌ All API logic

Complete Custom Implementation Example

const config: ConfigContextValue = {
  model: 'gpt-4',
  getAuthToken: () => 'your-token',
  customCompletionsAPI: async (config, history) => {
    // 1. Создаем сообщение бота (это ваша ответственность)
    const botMsgId = nanoid()
    const addBotMessage = (content: string, error?: string) => {
      // Здесь вы добавляете/обновляете сообщение бота в UI
      // Например, через callback или контекст
    }

    // 2. Создаем abort controller (это ваша ответственность)
    const abortController = new AbortController()

    try {
      // 3. Добавляем сообщение бота в UI
      addBotMessage('')

      const response = await fetch('https://api.openai.com/v1/chat/completions', {
        method: 'POST',
        headers: {
          Authorization: `Bearer ${config.getAuthToken()}`,
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({
          model: config.model,
          messages: history,
          stream: true,
          temperature: 0.7,
          max_tokens: 1000,
        }),
        signal: abortController.signal, // Ваш abort controller
      })

      if (!response.ok) {
        throw new Error(`API request failed: ${response.status}`)
      }

      // 4. Обрабатываем SSE и обновляем UI
      const reader = response.body?.getReader()
      const decoder = new TextDecoder()
      let fullText = ''

      while (true) {
        const { done, value } = await reader.read()
        if (done) break

        const chunk = decoder.decode(value, { stream: true })
        fullText += chunk

        // Обновляем сообщение бота в UI
        addBotMessage(fullText)
      }
    } catch (error) {
      // 5. Обрабатываем ошибки (это ваша ответственность)
      if (error.name === 'AbortError') {
        addBotMessage('', 'Request cancelled')
      } else {
        addBotMessage('', error.message)
      }
    }
  },
}

When to Use

  • Custom API endpoints: Different API providers or endpoints
  • Additional parameters: Temperature, max_tokens, function calling, etc.
  • Custom processing: WebSocket integration, database logging, custom chunk handling
  • Rate limiting: Custom rate limiting logic
  • Authentication: Custom auth flows
  • Response transformation: Modify responses before processing

Important Notes

  • When customCompletionsAPI is provided, the default API logic is completely bypassed
  • You have full control over everything: request body, headers, response handling, and UI updates
  • The function receives the current config and message history
  • You're responsible for implementing your own message handling: adding bot messages, updating content, handling errors
  • You're responsible for implementing your own SSE (Server-Sent Events) handling
  • Make sure to handle errors appropriately in your custom implementation
  • The library only adds the user message to the chat - everything else (bot message, abort controller, error handling) is your responsibility

Integration with React Component

import { AxelleroChatBotUi } from 'axellero-chatbot-ui-react'

function App() {
  return (
    <AxelleroChatBotUi
      config={config}
      // ... other props
    />
  )
}