Xzero UI
Pro library

Response feedbackPro

A feedback control for AI replies where thumbs down opens an inline panel with reason chips and an optional note, thumbs up fills and asks what was good, and a submit shows a short thank-you before folding back into the row.

$ pnpm dlx shadcn@4.23.0 add @xzero-ui-pro/response-feedback
Live · keyboard ready

When to use

When not to use

Installation

Register both Xzero UI namespaces in your project's components.json:

{
  "registries": {
    "@xzero-ui": "https://mcp.xzero.co.uk/r/{name}.json",
    "@xzero-ui-pro": "https://mcp.xzero.co.uk/r/pro/{name}.json"
  }
}
npx shadcn@4.23.0 add @xzero-ui-pro/response-feedback

Install xzero-foundation once, and import @/components/xzero/foundation.css in your app root. No Xzero token is used by this snapshot. For sample portraits, photos, fonts, or logos, download https://mcp.xzero.co.uk/install-assets.mjs and run node xzero-install-assets.mjs <receiving-project>.

Usage

import { FeedbackActions, ResponseFeedback } from "@/components/xzero/response-feedback/response-feedback";

// The full reply card
<ResponseFeedback
  model={{ name: "Claude Opus 5.5" }}
  prompt={question}
  copyText={answerText}
  messageId={message.id}
  onFeedback={async (event, { signal }) => {
    await fetch("/api/feedback", { method: "POST", body: JSON.stringify(event), signal });
  }}
>
  <Markdown>{answerText}</Markdown>
</ResponseFeedback>

// Just the control, inside your own message row
<FeedbackActions
  messageId={message.id}
  defaultRating={message.rating}
  leading={<CopyButton value={answerText} iconOnly variant="plain" label="Copy reply" />}
  onFeedback={saveFeedback}
/>

API reference

ResponseFeedback

An AI reply card: optional prompt bubble, model mark and name, the reply body and an action row with copy, optional regenerate and the thumbs feedback control. With no props it renders a sample Claude Sonnet 5.5 answer with simulated feedback. Accepts every FeedbackActions prop except leading.

PropTypeDefaultDescription
model{ name: string; mark?: ReactNode }{ name: "Claude Sonnet 5.5" }The model that wrote the reply. The mark is drawn at 16px and defaults to the Claude mark.
promptReactNode | null–The person's message shown above the reply. Defaults to a sample question; pass null to hide it.
childrenReactNode–The reply body. Paragraphs, lists and inline code are styled. Defaults to a sample Postgres answer.
copyTextstring | null–Plain text for the copy action. Defaults to the sample reply when no children are given; pass null to hide copy.
onRegenerate() => void–Shows a regenerate action in the row.
onFeedback(event: FeedbackEvent, context: { signal: AbortSignal }) => void | Promise<void>–Persists feedback. Fires with stage "rating" on every thumb press and stage "details" when reasons or a note are submitted. Reject to show the inline error and Try again; the signal aborts when the panel closes or the rating changes. Without it, sends are simulated.
messageIdstring–Passed back on every event so feedback can be stored against a message.
rating"up" | "down" | null–Controlled rating. Leave undefined to let the control own it.
defaultRating"up" | "down" | nullnullInitial rating when uncontrolled, for example a rating loaded from your database.
onRatingChange(rating: "up" | "down" | null) => void–Called on every rating change, including clearing it by pressing the active thumb again.
negativeReasonsstring[]["Inaccurate", "Not helpful", "Too long", "Unsafe", "Other"]Reason chips offered after a thumbs down. A chip labelled Other moves focus to the note and changes its placeholder.
positiveReasonsstring[]["Accurate", "Easy to follow", "Good examples", "Saved me time"]Chips offered after a thumbs up. Pass [] to skip the follow-up row.
allowCommentbooleantrueShows the optional note field after a thumbs down.
commentPlaceholderstring"Add details (optional)"Placeholder for the note field.
commentMaxLengthnumber1000Maximum note length.
privacyNoteReactNode"Shares this reply for review"Quiet line next to Submit about what gets shared. Hidden below 400px of container width.
thanksMessageReactNode–Second line of the thank-you state. Defaults to a simulated note without onFeedback, otherwise "It helps improve future replies."
thanksDurationnumber1600How long the thank-you state holds before the panel folds away, in ms.
trailingReactNode–Content pinned to the end of the row, such as a timestamp or token count.
disabledbooleanfalseDisables the thumbs, for example while the reply is still streaming.
classNamestring–Class on the root element.

FeedbackActions

The feedback control on its own, for your own message rows: thumbs up and down, the reasons panel, the What was good row and the thank-you state.

PropTypeDefaultDescription
leadingReactNode–Actions rendered before the thumbs, such as copy or regenerate. Use 16px icons in control-height-sm buttons to match.
onFeedback(event: FeedbackEvent, context: { signal: AbortSignal }) => void | Promise<void>–Persists feedback. Fires with stage "rating" on every thumb press and stage "details" when reasons or a note are submitted. Reject to show the inline error and Try again; the signal aborts when the panel closes or the rating changes. Without it, sends are simulated.
messageIdstring–Passed back on every event so feedback can be stored against a message.
rating"up" | "down" | null–Controlled rating. Leave undefined to let the control own it.
defaultRating"up" | "down" | nullnullInitial rating when uncontrolled, for example a rating loaded from your database.
onRatingChange(rating: "up" | "down" | null) => void–Called on every rating change, including clearing it by pressing the active thumb again.
negativeReasonsstring[]["Inaccurate", "Not helpful", "Too long", "Unsafe", "Other"]Reason chips offered after a thumbs down. A chip labelled Other moves focus to the note and changes its placeholder.
positiveReasonsstring[]["Accurate", "Easy to follow", "Good examples", "Saved me time"]Chips offered after a thumbs up. Pass [] to skip the follow-up row.
allowCommentbooleantrueShows the optional note field after a thumbs down.
commentPlaceholderstring"Add details (optional)"Placeholder for the note field.
commentMaxLengthnumber1000Maximum note length.
privacyNoteReactNode"Shares this reply for review"Quiet line next to Submit about what gets shared. Hidden below 400px of container width.
thanksMessageReactNode–Second line of the thank-you state. Defaults to a simulated note without onFeedback, otherwise "It helps improve future replies."
thanksDurationnumber1600How long the thank-you state holds before the panel folds away, in ms.
trailingReactNode–Content pinned to the end of the row, such as a timestamp or token count.
disabledbooleanfalseDisables the thumbs, for example while the reply is still streaming.
classNamestring–Class on the root element.

Keyboard interactions

KeysAction
TabMoves through copy, thumbs up and thumbs down, then into the open panel.
Enter / SpacePresses a thumb, toggles a reason chip, or activates Submit, Send, Skip and Cancel. Pressing the active thumb clears the rating.
EscapeCloses the open panel and returns focus to the thumb that opened it. The rating stays.
Cmd / Ctrl + EnterSubmits from the note field.

Accessibility

Motion

Responsive behavior

Performance

Notes for AI

Also in ai

Guidance for AI tools

Response feedback: A feedback control for AI replies where thumbs down opens an inline panel with reason chips and an optional note, thumbs up fills and asks what was good, and a submit shows a short thank-you before folding back into the row. Follow the declared prop types and do not invent props. Keep keyboard access, reduced motion support, and both light and dark themes intact when adapting it.

Full library index: https://ui.xzero.co.uk/llms.txt

Browse all components