Installation
npx shadcn@latest add @pixeldosa/response-feedbackRequires `cn` at `@/lib/utils`. Route `onSubmit` somewhere a person actually reads.
Usage
"use client";
import { ResponseFeedback } from "@/components/ui/response-feedback";
<ResponseFeedback
destination="the team that trains this assistant"
reasons={[
{ id: "wrong", label: "Something is wrong" },
{ id: "source", label: "Wrong or missing source" },
]}
onSubmit={(feedback) => report(feedback)}
/>
Why it exists
A bare thumbs-down tells a team that something was wrong and nothing about what. Asking for the reason costs the user two seconds and turns a shrug into something a team can act on.
Features
- A thumbs up sends on its own. Praise shouldn't cost a form.
askOnUpchanges that when you want it. - Reasons are multi-select, in your users' words, because an answer is usually wrong in more than one way.
- The comment is optional, and an empty one is never sent as feedback.
- The confirmation names the destination. "Thanks, that goes to the team that trains this assistant."
- No double submits. The controls are replaced by the confirmation.
Variants
Ready
Was this answer useful?
Already sent
Thanks, that goes to the team that trains this assistant.
Props
| Prop | Type | Notes |
|---|---|---|
onSubmit | (submission) => void | Required. { verdict, reasonIds, comment? }. |
reasons | { id, label }[] | Yours. A default set ships. |
askOnUp | boolean | Ask for reasons after a thumbs up too. Defaults to false. |
destination | string | Where the feedback goes, named in the confirmation. |
submitted | boolean | Start in the confirmed state. |
label | string | Defaults to "Was this answer useful?". |
Accessibility
- The thumbs are a labelled group of toggle buttons with
aria-pressed. - Reasons are real checkboxes inside a
fieldsetwith a legend; the chips are their labels, so the whole chip is a hit target. - The comment has a visible label.
When not to use
- As the only way to report harm. Anything unsafe needs a route a person reads, with a way to follow up.
- As a rating that changes the answer. If a thumbs-down should trigger a retry, say so
and use
Response Versions. - For support. A ticket has a conversation; this doesn't.
why it's built this way
Engineering Notes
The asymmetry between up and down is the design: a thumbs up submits immediately and a thumbs down opens the reason panel, because the useful signal is concentrated in what went wrong and a form after praise costs goodwill for data nobody reads. Reasons are multi-select rather than a single choice, since an answer is usually wrong in more than one way, and they arrive as ids so the caller can route and aggregate them without parsing labels. The comment stays optional and is trimmed to undefined when empty, so a submission never carries a whitespace string that looks like feedback in a dashboard. After sending, the component collapses to a single sentence naming the destination instead of leaving the controls in place, which prevents the double-submit that turns one opinion into three.
how it moves, and when it doesn't
Motion Notes
Nothing moves except the confirmation, which fades in once over `--pd-duration-base`. The reason panel appears without a slide or a height animation: it is the user's turn, and an animating panel under a cursor that is already moving toward a chip costs a mis-click. Under `prefers-reduced-motion` the fade is dropped.