JSPM

react-agent-widget

0.1.4
  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 771
  • Score
    100M100P100Q80954F
  • License MIT

Drop-in React chatbot and AI agent widget — streaming chat UI for OpenAI, Anthropic, AWS Bedrock, Azure OpenAI, or any custom API. Fully themeable, TypeScript-first, zero CSS-in-JS.

Package Exports

  • react-agent-widget

Readme

react-agent-widget

npm version npm downloads bundle size license TypeScript Open in CodeSandbox

→ Live demo on CodeSandbox — no install, no API key, works in your browser.

The fastest way to add a production-ready AI chatbot to any React app.

Drop in one component. Connect to OpenAI, Anthropic Claude, AWS Bedrock, Azure OpenAI, or your own API. Get a streaming chat UI with a floating launcher button, full theming, and zero style conflicts — in under 10 lines of code.


Why react-agent-widget?

Feature react-agent-widget
Works with any AI provider OpenAI, Anthropic, AWS Bedrock, Azure OpenAI, custom HTTP
Streaming responses Built-in SSE parser, token-by-token rendering
Fully themeable CSS custom properties — change every color, radius, shadow, font
Generative UI Agent renders React components (cards, forms, charts) inside the chat
TypeScript-first Full type safety, no any in the public API
Zero CSS conflicts Scoped raw- class prefix, no global styles, no CSS-in-JS runtime
Headless mode Use your own UI with the built-in state/streaming hooks
Lightweight ~44 KB minified ESM, React 18 peer dep, no mandatory runtime deps
SSR-safe No window access at render time — works with Next.js App Router

Installation

npm install react-agent-widget
# or
yarn add react-agent-widget
# or
pnpm add react-agent-widget

Requires React 18+ as a peer dependency.


Quick start — 10 lines

import { AgentWidget, createHttpAdapter } from 'react-agent-widget'

const adapter = createHttpAdapter({ url: '/api/chat' })

export default function App() {
  return (
    <AgentWidget
      adapter={adapter}
      welcomeMessage="Hi! How can I help you today?"
    />
  )
}

Your /api/chat endpoint receives { messages, stream } and returns a streaming SSE response or plain JSON. No API keys go in the browser — your server adds credentials.

That's it. A floating chat button appears at the bottom-right of the page.


Connecting to AI providers

All adapters point at a proxy endpoint you control on your server. Your server adds the API key, SigV4 signature, or managed identity — never the browser.

OpenAI / ChatGPT

import { AgentWidget, createOpenAIAdapter } from 'react-agent-widget'

<AgentWidget
  adapter={createOpenAIAdapter({
    proxyUrl: '/api/openai',   // your server forwards to api.openai.com + adds Authorization header
    model: 'gpt-4o',
  })}
/>

Anthropic Claude

import { AgentWidget, createAnthropicAdapter } from 'react-agent-widget'

<AgentWidget
  adapter={createAnthropicAdapter({
    proxyUrl: '/api/anthropic',   // your server adds x-api-key + Anthropic-Version headers
    model: 'claude-3-5-sonnet-20241022',
  })}
/>

AWS Bedrock

import { AgentWidget, createBedrockAdapter } from 'react-agent-widget'

<AgentWidget
  adapter={createBedrockAdapter({
    proxyUrl: '/api/bedrock',   // your server adds SigV4 auth
    model: 'anthropic.claude-3-5-sonnet-20241022-v2:0',
  })}
/>

Azure OpenAI

import { AgentWidget, createAzureOpenAIAdapter } from 'react-agent-widget'

<AgentWidget
  adapter={createAzureOpenAIAdapter({
    proxyUrl: '/api/azure',   // your server adds api-key / managed identity
    deploymentName: 'gpt-4o',
  })}
/>

Custom HTTP endpoint / BFF

import { AgentWidget, createHttpAdapter } from 'react-agent-widget'

<AgentWidget
  adapter={createHttpAdapter({
    url: '/api/chat',
    // optional: customize the request body
    formatRequest: (messages, options) => ({
      history: messages,
      config: { stream: true },
    }),
  })}
/>

Full API

<AgentWidget
  // required
  adapter={adapter}

  // launcher button
  launcher={{
    position: { side: 'right' },   // 'left' | 'right'
    label: 'Chat with AI',
    icon: <MyIcon />,              // custom icon
    badge: 3,                      // unread count bubble
  }}

  // theme — override any token
  theme={{
    mode: 'dark',                   // 'light' | 'dark'
    colors: {
      primary: '#7c3aed',
      userBubble: '#7c3aed',
      background: '#09090b',
    },
    radius: { panel: '20px', launcher: '12px' },
  }}

  // per-slot class names (add your own CSS on top)
  classNames={{
    panel: 'my-panel',
    messageUser: 'my-user-msg',
    messageAgent: 'my-agent-msg',
    composer: 'my-composer',
  }}

  // chat behavior
  welcomeMessage="Hi! How can I help?"
  suggestedPrompts={['Summarize this page', 'Help me write an email']}
  systemPrompt="You are a helpful assistant for Acme Corp."
  placeholder="Ask me anything…"
  renderMarkdown={true}            // render markdown in agent replies (default: true)

  // generative UI — agent renders React components
  components={{
    weatherCard: WeatherCard,
    approvalPrompt: ApprovalPrompt,
  }}

  // events
  onMessage={(msg) => analytics.track('chat_message', msg)}
  onError={(err) => errorReporter.capture(err)}
  onOpen={() => analytics.track('chat_opened')}
  onClose={() => analytics.track('chat_closed')}

  // controlled open state (optional)
  open={isOpen}
  onOpenChange={setIsOpen}
/>

Theming

The widget uses CSS custom properties for all visual tokens. Pass a theme object — the widget applies the values as inline CSS variables on its root element, so your host-app styles are never affected.

// Dark purple theme example
<AgentWidget
  adapter={adapter}
  theme={{
    mode: 'dark',
    colors: {
      primary: '#8b5cf6',
      primaryForeground: '#ffffff',
      background: '#09090b',
      surface: '#18181b',
      userBubble: '#8b5cf6',
      agentBubble: '#27272a',
      agentBubbleForeground: '#fafafa',
    },
    radius: {
      panel: '24px',
      message: '20px',
      launcher: '16px',
    },
  }}
/>

Available CSS variables (for stylesheet overrides)

/* Colors */
--raw-color-primary        --raw-color-primary-fg
--raw-color-background     --raw-color-surface      --raw-color-surface-fg
--raw-color-border         --raw-color-muted        --raw-color-muted-fg
--raw-color-error          --raw-color-error-fg
--raw-color-user-bubble    --raw-color-user-bubble-fg
--raw-color-agent-bubble   --raw-color-agent-bubble-fg

/* Typography */
--raw-font-family          --raw-font-size-sm   --raw-font-size-md   --raw-font-size-lg

/* Radius */
--raw-radius-panel         --raw-radius-message
--raw-radius-launcher      --raw-radius-composer

/* Shadow / z-index */
--raw-shadow-panel         --raw-shadow-launcher
--raw-z-panel              --raw-z-launcher

Generative UI

Let the AI render interactive React components directly inside the chat — not just text.

// 1. Define your component
function WeatherCard({ payload, onResult }) {
  const { city, temperature, condition } = payload.props
  return (
    <div style={{ padding: 16, borderRadius: 12, background: '#f0f9ff' }}>
      <h3>{city}{temperature}°C, {condition}</h3>
      <button onClick={() => onResult?.({ action: 'show_forecast', city })}>
        Show 7-day forecast
      </button>
    </div>
  )
}

// 2. Register it
<AgentWidget
  adapter={adapter}
  components={{ weatherCard: WeatherCard }}
/>
// 3. Your agent/tool emits this SSE event:
data: {"type":"component","payload":{"type":"weatherCard","props":{"city":"London","temperature":15,"condition":"Cloudy"}}}

The widget renders <WeatherCard> in-stream. When the user clicks the button, onResult fires and the widget sends the result back into the conversation automatically.


Headless mode

Bring your own UI — use just the state management and streaming logic.

import {
  WidgetProvider,
  useWidget,
  ThemeProvider,
  createOpenAIAdapter,
} from 'react-agent-widget'

const adapter = createOpenAIAdapter({ proxyUrl: '/api/openai' })

function MyChatUI() {
  const { messages, sendMessage, isStreaming, isOpen, setIsOpen } = useWidget()

  return (
    <div>
      {messages.map(msg => (
        <div key={msg.id} className={msg.role}>
          {msg.content}
        </div>
      ))}
      <input
        onKeyDown={(e) => {
          if (e.key === 'Enter') void sendMessage(e.currentTarget.value)
        }}
      />
    </div>
  )
}

export function App() {
  return (
    <ThemeProvider>
      <WidgetProvider adapter={adapter}>
        <MyChatUI />
      </WidgetProvider>
    </ThemeProvider>
  )
}

Writing a proxy endpoint

Your backend receives the messages and forwards them to the AI provider. Here is a minimal Next.js example for OpenAI:

// app/api/openai/route.ts
import OpenAI from 'openai'

const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY })

export async function POST(req: Request) {
  const { messages } = await req.json()

  const stream = openai.beta.chat.completions.stream({
    model: 'gpt-4o',
    messages,
  })

  return new Response(stream.toReadableStream(), {
    headers: { 'Content-Type': 'text/event-stream' },
  })
}

The widget's SSE parser understands OpenAI's native streaming format automatically.


SSE format (custom backends)

If you write your own backend, use this simple format:

data: {"type":"text-delta","delta":"Hello"}
data: {"type":"text-delta","delta":", world!"}
data: {"type":"component","payload":{"type":"weatherCard","props":{...}}}
data: {"type":"done"}

Or just return data: [DONE] as the final event (OpenAI convention — also understood).


Building a custom Adapter

Implement the Adapter interface to connect any AI service:

import type { Adapter } from 'react-agent-widget'

export const myAdapter: Adapter = {
  name: 'my-provider',
  capabilities: { streaming: true, tools: false, multimodal: false },
  async *send(messages, options) {
    const res = await fetch('/api/my-llm', {
      method: 'POST',
      body: JSON.stringify({ messages }),
      signal: options?.signal,
    })
    // yield chunks…
    yield { type: 'text-delta', delta: 'Hello' }
    yield { type: 'done' }
  },
}

TypeScript

Full type safety — no any in the public API.

import type {
  Adapter,
  AdapterCapabilities,
  StreamChunk,
  Message,
  Theme,
  ClassNamesConfig,
  AgentWidgetProps,
  GenerativeUIComponent,
} from 'react-agent-widget'

Accessibility

  • Keyboard navigable — Tab, Enter, Escape to close, full focus management
  • ARIA: role="dialog", aria-modal, aria-live="polite" on message list
  • Respects prefers-reduced-motion (animations disabled automatically)
  • Default theme meets WCAG 2.1 AA contrast ratios

Browser & framework support

Environment Supported
React 18+
Next.js (App Router) Yes — SSR-safe
Next.js (Pages Router) Yes
Vite Yes
Remix Yes
Webpack Yes
Browsers All modern evergreen (Chrome, Firefox, Safari, Edge)

License

MIT — TilangaPramith