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 🙏

© 2026 – Pkg Stats / Ryan Hefner

react-agent-widget

v0.1.4

Published

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.

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