# Feedback

> A feedback window with a topic, a subject, a message and a short thank-you after sending.

A shadcn/ui block from the [@cupertinoui](https://www.cupertino-ui.com/?utm_source=shadcn.exchange) registry, listed on [shadcn.exchange](https://shadcn.exchange/r/cupertinoui/feedback-01).

- Page, with a live preview: https://shadcn.exchange/r/cupertinoui/feedback-01
- Screenshot: https://shadcn.exchange/shot/cupertinoui/feedback-01/light.png
- Registry item: https://www.cupertino-ui.com/r/feedback-01.json
- Listed: 2026-11-05, source last changed 2026-11-05

## Install

```bash
npx shadcn@latest add https://shadcn.exchange/r/cupertinoui/feedback-01.json
```

Run it in a project that has shadcn/ui set up. It adds the files below and installs what they depend on.

## Dependencies

- npm packages: `lucide-react`
- Registry items: `@cupertinoui/macos-theme`, `@cupertinoui/button`, `@cupertinoui/checkbox`, `@cupertinoui/icon-tile`, `@cupertinoui/input`, `@cupertinoui/label`, `@cupertinoui/select`, `@cupertinoui/textarea`, `@cupertinoui/window`

## Files

### `registry/macos/blocks/feedback-01/feedback-01.tsx`

```tsx
"use client"

import * as React from "react"
import { CheckIcon } from "lucide-react"

import { cn } from "@/lib/utils"
import { Button } from "@/registry/macos/ui/button"
import { Checkbox } from "@/registry/macos/ui/checkbox"
import { IconTile } from "@/registry/macos/ui/icon-tile"
import { Input } from "@/registry/macos/ui/input"
import { Label } from "@/registry/macos/ui/label"
import {
  Select,
  SelectContent,
  SelectItem,
  SelectTrigger,
  SelectValue,
} from "@/registry/macos/ui/select"
import { Textarea } from "@/registry/macos/ui/textarea"
import {
  Window,
  WindowContent,
  WindowControls,
  WindowMain,
  WindowTitle,
  WindowTitlebar,
} from "@/registry/macos/ui/window"

const topics = {
  bug: "Bug",
  idea: "Idea",
  question: "Question",
  other: "Something else",
} as const

type Topic = keyof typeof topics

// A feedback window: topic, subject, message, then a short thank-you.
export function Feedback01({
  className,
  ...props
}: React.ComponentProps<typeof Window>) {
  const subjectId = React.useId()
  const messageId = React.useId()
  const [topic, setTopic] = React.useState<Topic>("idea")
  const [subject, setSubject] = React.useState("")
  const [message, setMessage] = React.useState("")
  const [sent, setSent] = React.useState(false)

  return (
    <Window className={cn("h-[440px] w-[400px]", className)} {...props}>
      <WindowMain>
        <WindowTitlebar>
          <WindowControls />
          <WindowTitle>Feedback</WindowTitle>
        </WindowTitlebar>
        <WindowContent className="flex flex-col px-5 py-4">
          {sent ? (
            <div className="flex flex-1 flex-col items-center justify-center text-center">
              <IconTile color="green" className="size-16 rounded-[16px] [&_svg]:size-8">
                <CheckIcon strokeWidth={2} />
              </IconTile>
              <h2 className="mt-4 text-mac-title-1 font-bold">Thanks for the note</h2>
              <p className="mt-1 max-w-[260px] text-mac-body text-mac-label-secondary">
                {`${subject ? `${topics[topic]}: ${subject}` : topics[topic]}. This is a sample, so nothing was sent.`}
              </p>
              <Button
                variant="secondary"
                className="mt-6"
                onClick={() => {
                  setSent(false)
                  setSubject("")
                  setMessage("")
                }}
              >
                Send Another
              </Button>
            </div>
          ) : (
            <form
              className="flex flex-1 flex-col gap-3"
              onSubmit={(event) => {
                event.preventDefault()
                setSent(true)
              }}
            >
              <div className="flex items-center justify-between gap-3">
                <Label>Topic</Label>
                <Select
                  value={topic}
                  onValueChange={(value) => value && setTopic(value as Topic)}
                  items={topics}
                >
                  <SelectTrigger aria-label="Topic">
                    <SelectValue />
                  </SelectTrigger>
                  <SelectContent>
                    {(Object.keys(topics) as Topic[]).map((value) => (
                      <SelectItem key={value} value={value}>
                        {topics[value]}
                      </SelectItem>
                    ))}
                  </SelectContent>
                </Select>
              </div>
              <div className="flex flex-col gap-1">
                <Label htmlFor={subjectId}>Subject</Label>
                <Input
                  id={subjectId}
                  value={subject}
                  onChange={(event) => setSubject(event.target.value)}
                  placeholder="A short summary"
                />
              </div>
              <div className="flex min-h-0 flex-1 flex-col gap-1">
                <Label htmlFor={messageId}>Message</Label>
                <Textarea
                  id={messageId}
                  value={message}
                  onChange={(event) => setMessage(event.target.value)}
                  placeholder="What happened, or what would help?"
                  className="min-h-24 flex-1 resize-none"
                  required
                />
              </div>
              <Label className="gap-[6px]">
                <Checkbox />
                Include a screenshot
              </Label>
              <div className="flex justify-end">
                <Button type="submit" disabled={!message.trim()}>
                  Send
                </Button>
              </div>
            </form>
          )}
        </WindowContent>
      </WindowMain>
    </Window>
  )
}
```
