Cobbl

React

First-class React support with proper hooks and lifecycle management

The React component provides first-class React support with proper hooks and lifecycle management.

Installation

npm install @cobbl-ai/feedback-widget

Basic Usage

import { FeedbackWidget } from '@cobbl-ai/feedback-widget/react'

const App = () => (
  <div>
    <h1>AI Response</h1>
    <p>Your AI-generated content...</p>

    <FeedbackWidget runId="prompt-run-id" />
  </div>
)

Props

PropTypeRequiredDefaultDescription
runIdstringYesRun ID from prompt execution
variant'trigger' | 'thumbs' | 'inline'No'trigger'Display variant
positionWidgetPositionNo'bottom-right'Flyout position
triggerButtonTextstringNo'Give Feedback'Trigger button text
colorScheme'auto' | 'light' | 'dark'No'auto'Color scheme mode
onSuccess(feedbackId: string) => voidNoSuccess callback
onError(error: Error) => voidNoError callback
classNamestringNoContainer CSS class
styleReact.CSSPropertiesNoContainer inline styles

Display Variants

Trigger Variant (Default)

A text button that opens the feedback flyout:

<FeedbackWidget
  runId="prompt-run-id"
  variant="trigger"
  triggerButtonText="Rate this response"
/>

Thumbs Variant

Thumbs up/down buttons that immediately register feedback:

<FeedbackWidget runId="prompt-run-id" variant="thumbs" />

Inline Variant

Full feedback form rendered directly:

<FeedbackWidget runId="prompt-run-id" variant="inline" />

Chat Example

'use client'

import { FeedbackWidget } from '@cobbl-ai/feedback-widget/react'

interface Message {
  id: string
  content: string
  runId: string
}

interface ChatComponentProps {
  messages: Message[]
}

const ChatComponent = ({ messages }: ChatComponentProps) => {
  return (
    <div className="chat-container">
      {messages.map((message) => (
        <div key={message.id} className="message">
          <p>{message.content}</p>
          <FeedbackWidget runId={message.runId} variant="thumbs" />
        </div>
      ))}
    </div>
  )
}

In this example, messages are fetched from your server (where they're stored in the database with their runId from prompt execution). The server handles running prompts via the Admin SDK and storing responses, while the client component simply displays them with the feedback widget.

Dynamic Run ID

The widget automatically updates when props change:

const DynamicExample = () => {
  const [runId, setRunId] = useState('initial-run-id')

  const generateNewResponse = async () => {
    const result = await client.runPrompt('my-prompt', { topic: 'New Topic' })
    setRunId(result.runId)
  }

  return (
    <div>
      <button onClick={generateNewResponse}>Generate New</button>
      <FeedbackWidget runId={runId} />
    </div>
  )
}

Event Handling

Handle success and error events:

<FeedbackWidget
  runId="prompt-run-id"
  onSuccess={(feedbackId) => {
    // Show success toast
    toast.success('Thanks for your feedback!')

    // Track analytics
    analytics.track('feedback_submitted', { feedbackId })
  }}
  onError={(error) => {
    // Show error toast
    toast.error('Failed to submit feedback')

    // Log error
    console.error('Feedback error:', error)
  }}
/>

Styling

Using className

<FeedbackWidget runId="prompt-run-id" className="my-feedback-widget" />
.my-feedback-widget {
  --cobbl-primary: #6366f1;
  --cobbl-primary-hover: #4f46e5;
}

Using style prop

<FeedbackWidget
  runId="prompt-run-id"
  style={
    {
      '--cobbl-primary': '#6366f1',
      '--cobbl-trigger-font-size': '14px',
    } as React.CSSProperties
  }
/>

Tailwind CSS

<FeedbackWidget
  runId="prompt-run-id"
  className="[--cobbl-primary:#6366f1] [--cobbl-flyout-width:400px]"
/>

See Styling for the complete list of CSS variables.

Flyout Positioning

Control the flyout position:

<FeedbackWidget runId="prompt-run-id" variant="trigger" position="top" />

Available positions:

  • top-left, top, top-right
  • left, right
  • bottom-left, bottom, bottom-right (default)

Color Scheme

Control the widget's color scheme:

// Auto (default) - follows system preference
<FeedbackWidget runId="prompt-run-id" colorScheme="auto" />

// Force light theme
<FeedbackWidget runId="prompt-run-id" colorScheme="light" />

// Force dark theme
<FeedbackWidget runId="prompt-run-id" colorScheme="dark" />

Syncing with Your Theme

Bind the colorScheme prop to your app's theme state:

import { useTheme } from 'next-themes' // or your theme provider

const MyComponent = () => {
  const { theme } = useTheme()

  return (
    <FeedbackWidget
      runId="prompt-run-id"
      colorScheme={theme === 'dark' ? 'dark' : 'light'}
    />
  )
}

See Styling for customizing colors per scheme.

Multiple Widgets

Render multiple widgets for different responses:

const ResponseList = ({ responses }) => (
  <div>
    {responses.map((response) => (
      <div key={response.id} className="response-card">
        <p>{response.content}</p>
        <FeedbackWidget runId={response.runId} variant="thumbs" />
      </div>
    ))}
  </div>
)

Server Components (Next.js)

The FeedbackWidget must be used in a client component:

app/chat/page.tsx
import { ChatMessages } from './ChatMessages'

// Server component
const ChatPage = async () => {
  const messages = await getMessages()
  return <ChatMessages initialMessages={messages} />
}
app/chat/ChatMessages.tsx
'use client'

import { FeedbackWidget } from '@cobbl-ai/feedback-widget/react'

// Client component
export const ChatMessages = ({ initialMessages }) => {
  return (
    <div>
      {initialMessages.map((msg) => (
        <div key={msg.id}>
          <p>{msg.content}</p>
          <FeedbackWidget runId={msg.runId} variant="thumbs" />
        </div>
      ))}
    </div>
  )
}

TypeScript

Full TypeScript support is included:

import { FeedbackWidget } from '@cobbl-ai/feedback-widget/react'
import type { FeedbackWidgetProps } from '@cobbl-ai/feedback-widget/react'

const MyWidget = (props: Partial<FeedbackWidgetProps>) => (
  <FeedbackWidget runId="required-run-id" {...props} />
)
import type {
  WidgetPosition,
  WidgetVariant,
  ColorScheme,
} from '@cobbl-ai/feedback-widget'

const position: WidgetPosition = 'top-right'
const variant: WidgetVariant = 'thumbs'
const colorScheme: ColorScheme = 'auto'

See TypeScript for type definitions.

Next Steps

On this page