# Feedback Banner

> A banner that asks one question with a few answer buttons, says thanks after an answer, and can be dismissed.

- Name: `feedback-banner`
- Status: beta
- Categories: feedback, survey, banner, rating
- Files: the install writes `feedback-banner.tsx`, and any part files named `feedback-banner-*.tsx`, to your `components` alias, `@/components` by default. Edit them freely. They are yours.
- npm packages: `lucide-react`. The install adds them.
- Other registry items: `@txbrendan/card`, `@txbrendan/button`. The install adds them.
- Gallery: https://txbrendan.dev/blocks/feedback-banner
- Registry item: https://txbrendan.dev/r/feedback-banner.json

## Install

1. Add the registry to `components.json`. Do this first, once:

   ```bash
   npx shadcn@latest registry add "@txbrendan=https://txbrendan.dev/r/{name}.json"
   ```

   The command writes this entry. You can also add it by hand:

   ```json
   {
     "registries": {
       "@txbrendan": "https://txbrendan.dev/r/{name}.json"
     }
   }
   ```

   Without this step, the shadcn CLI looks up `@txbrendan` in a public list that txbrendan/ui does not control.

2. Add the theme once per app. It replaces your color tokens with ones checked for contrast:

   ```bash
   npx shadcn@latest add @txbrendan/theme
   ```

3. Add the block. It also adds every component it uses:

   ```bash
   npx shadcn@latest add @txbrendan/feedback-banner
   ```

## Use it for

- Asking how a new feature is going, right where people use it.
- A yes or no question at the end of a help article: "Did this answer your question?".
- Ask one short question with two or three answers. Send the answer in onValueChange.
- Save the answer or the dismissal, and stop showing the banner to that person.
- Write a question that the default Yes and No answer, or pass options with your own labels.

## Avoid it for

- Long surveys or free-text answers. Link to a form instead.
- Errors, warnings, or news. Use Alert for a whole page, or Notice under the thing it is about.
- Asking before people have used the thing you ask about.
- Showing it on every visit. Ask once, then respect the answer or the dismissal.

## Props

- `title`: `ReactNode`. The question, like "Is the new editor working for you?". It also names the answer buttons. Required.
- `description`: `ReactNode`. One short line under the question: why you ask, or how long it takes.
- `options`: `{ value: string; label: ReactNode; icon?: ReactNode }[]`. Default `[{ value: "yes", label: "Yes" }, { value: "no", label: "No" }]`. The answers, one button each, all the same weight. Each value must be unique.
- `onValueChange`: `(value: string) => void`. Called with the option's value when people pick one. Send it to your feedback store here.
- `value`: `string | null`. The picked option's value, when you control it. null shows the question.
- `defaultValue`: `string | null`. Default `null`. The picked option's value to start with. Set it to start on the thank-you message.
- `icon`: `ReactNode`. An icon element, like \<MessageCircle />. It sits in a round tile and turns into a check after an answer.
- `tone`: `"neutral" | "brand" | "danger" | "success" | "warning"`. Default `"neutral"`. Color of the icon tile.
- `thanksTitle`: `ReactNode`. Default `"Thanks for your feedback"`. Shown in place of the question after an answer.
- `thanksDescription`: `ReactNode`. One line under thanksTitle, like what happens next.
- `dismissible`: `boolean`. Default `true`. Shows the dismiss button.
- `dismissLabel`: `string`. Default `"Dismiss"`. The dismiss button's name for screen readers.
- `onDismiss`: `() => void`. Called when people dismiss the banner, after it hides. Save this, so you do not ask again.
- `className`: `string`. Extra classes on the outer div, which holds the container query.

## Accessibility

- The answer buttons sit in a group named by the question, so each one reads with the question.
- After an answer, the thank-you message takes the place of the question, and focus moves to it, so screen readers read it.
- The icon tile is hidden from screen readers. Write a question that makes sense without it.
- The dismiss button is named by dismissLabel and described by the question or the thanks. Dismissing removes the banner, so focus falls back to the page. If that matters, move focus in onDismiss.

## Examples

### Ask about a new feature

```tsx
<FeedbackBanner
  icon={<MessageCircle />}
  tone="brand"
  title="How is the new editor?"
  description="One click. It shapes what we fix next."
  options={[
    { value: "good", label: "Love it", icon: <ThumbsUp /> },
    { value: "bad", label: "Needs work", icon: <ThumbsDown /> },
  ]}
  onValueChange={(answer) => sendFeedback("editor", answer)}
  onDismiss={() => saveSetting("editorFeedbackHidden", true)}
/>
```

### Yes or no, at the end of an article

```tsx
<FeedbackBanner
  title="Did this answer your question?"
  thanksDescription="We read every answer."
  onValueChange={(answer) => sendFeedback(articleId, answer)}
  dismissible={false}
/>
```
