All components

Feedback Menu

Floating feedback panel with loading, success, and error states, customizable edge, and an optional submit handler.

Examples
Feedback menu with examples

Four edge placements

Each stage below is a position:relative container using container=\'parent\'. Triggers sit centered on each edge and expand inward.

edge=\'top\'
edge=\'bottom\'
edge=\'left\'
edge=\'right\'

Percentage offset

offset=\'75%\' positions the trigger three-quarters of the way down the left edge.

edge=\'left\' offset=\'75%\'

Pixel offset

offset={420} positions the trigger exactly 420px from the start of the top edge.

edge=\'top\' offset={420}

Icon-only, compact

Set iconOnly to render a minimal square trigger with a tooltip label - ideal for dense layouts.

iconOnly + badge

Alignment and collision padding

align=\'start\' anchors the trigger at the offset point instead of centering it. collisionPadding keeps the open panel inside the stage bounds.

edge=\'right\' offset=\'5%\' align=\'start\'

Customizable copy and fields

Override feedback types, hide the email field, and customize success and submit labels.

custom types + copy
1"use client";
2
3import { Separator } from "@/components/ui/separator";
4import { cn } from "@/lib/utils";
5import FeedbackMenu, {
6 FeedbackFormData,
7 FeedbackTypeOption,
8} from "@/registry/ui/feedback-menu";
9import { Bug, MessageSquareText } from "lucide-react";
10
11const CUSTOM_TYPES: FeedbackTypeOption[] = [
12 { value: "issue", label: "Report an issue", icon: Bug },
13 { value: "idea", label: "Share an idea", icon: MessageSquareText },
14];
15
16export function FeedbackMenuExample() {
17 const handleSubmit = (data: FeedbackFormData) => {
18 console.log("feedback submitted:", data);
19 };
20
21 return (
22 <div className="relative w-full bg-background text-foreground">
23 <FeedbackMenu
24 edge="right"
25 offset="50%"
26 label="Feedback"
27 dot
28 onSubmit={handleSubmit}
29 />
30
31 <div className="mx-auto flex max-w-3xl flex-col gap-12 px-6 py-16 sm:py-24">
32 <Section
33 title="Four edge placements"
34 description="Each stage below is a position:relative container using container=\'parent\'. Triggers sit centered on each edge and expand inward."
35 >
36 <div className="grid grid-cols-1 gap-4 sm:grid-cols-2">
37 <Stage label="edge=\'top\'">
38 <FeedbackMenu
39 edge="top"
40 offset="50%"
41 container="parent"
42 label="Top"
43 onSubmit={handleSubmit}
44 />
45 </Stage>
46 <Stage label="edge=\'bottom\'">
47 <FeedbackMenu
48 edge="bottom"
49 offset="50%"
50 container="parent"
51 label="Bottom"
52 onSubmit={handleSubmit}
53 />
54 </Stage>
55 <Stage label="edge=\'left\'">
56 <FeedbackMenu
57 edge="left"
58 offset="50%"
59 container="parent"
60 label="Left"
61 onSubmit={handleSubmit}
62 />
63 </Stage>
64 <Stage label="edge=\'right\'">
65 <FeedbackMenu
66 edge="right"
67 offset="50%"
68 container="parent"
69 label="Right"
70 onSubmit={handleSubmit}
71 />
72 </Stage>
73 </div>
74 </Section>
75
76 <Separator />
77
78 <Section
79 title="Percentage offset"
80 description="offset=\'75%\' positions the trigger three-quarters of the way down the left edge."
81 >
82 <Stage label="edge=\'left\' offset=\'75%\'" className="min-h-[960px]">
83 <FeedbackMenu
84 edge="left"
85 offset="75%"
86 container="parent"
87 label="Help"
88 onSubmit={handleSubmit}
89 />
90 </Stage>
91 </Section>
92
93 <Section
94 title="Pixel offset"
95 description="offset={420} positions the trigger exactly 420px from the start of the top edge."
96 >
97 <Stage label="edge=\'top\' offset={420}">
98 <FeedbackMenu
99 edge="top"
100 offset={420}
101 container="parent"
102 label="Report"
103 badge="1"
104 onSubmit={handleSubmit}
105 />
106 </Stage>
107 </Section>
108
109 <Section
110 title="Icon-only, compact"
111 description="Set iconOnly to render a minimal square trigger with a tooltip label - ideal for dense layouts."
112 >
113 <Stage label="iconOnly + badge">
114 <FeedbackMenu
115 edge="right"
116 offset="50%"
117 container="parent"
118 iconOnly
119 label="Feedback"
120 badge={3}
121 onSubmit={handleSubmit}
122 />
123 </Stage>
124 </Section>
125
126 <Separator />
127
128 <Section
129 title="Alignment and collision padding"
130 description="align=\'start\' anchors the trigger at the offset point instead of centering it. collisionPadding keeps the open panel inside the stage bounds."
131 >
132 <Stage label="edge=\'right\' offset=\'5%\' align=\'start\'">
133 <FeedbackMenu
134 edge="right"
135 offset="5%"
136 align="start"
137 collisionPadding={24}
138 container="parent"
139 label="Align start"
140 onSubmit={handleSubmit}
141 />
142 </Stage>
143 </Section>
144
145 <Separator />
146
147 <Section
148 title="Customizable copy and fields"
149 description="Override feedback types, hide the email field, and customize success and submit labels."
150 >
151 <Stage label="custom types + copy">
152 <FeedbackMenu
153 edge="left"
154 offset="50%"
155 container="parent"
156 label="Ideas"
157 feedbackTypes={CUSTOM_TYPES}
158 showEmail={false}
159 successTitle="Idea received"
160 successDescription="We review every suggestion during planning."
161 submitLabel="Send idea"
162 submittingLabel="Sending..."
163 onSubmit={handleSubmit}
164 />
165 </Stage>
166 </Section>
167 </div>
168 </div>
169 );
170}
171
172function Section({
173 title,
174 description,
175 children,
176}: {
177 title: string;
178 description: string;
179 children: React.ReactNode;
180}) {
181 return (
182 <section className="flex flex-col gap-4">
183 <div className="flex flex-col gap-1">
184 <h2 className="text-sm font-semibold tracking-tight text-foreground">
185 {title}
186 </h2>
187 <p className="text-pretty text-sm leading-relaxed text-muted-foreground">
188 {description}
189 </p>
190 </div>
191 {children}
192 </section>
193 );
194}
195
196// A bounded, position:relative stage used to demonstrate `container="parent"`.
197function Stage({
198 label,
199 children,
200 className,
201}: {
202 label: string;
203 children: React.ReactNode;
204 className?: string;
205}) {
206 return (
207 <div
208 className={cn(
209 "relative min-h-[680px] w-full overflow-visible rounded-xl border border-dashed border-border bg-muted/30",
210 className,
211 )}
212 >
213 <span className="absolute left-3 top-3 rounded-md bg-background px-2 py-1 text-xs font-medium text-muted-foreground shadow-sm">
214 {label}
215 </span>
216 {children}
217 </div>
218 );
219}

Installation & source

Install via the shadcn CLI or copy the registry files manually.

bash
npx shadcn@latest add @tt-ui/feedback-menu

Props

NameTypeDefaultDescription
edge"top" | "right" | "bottom" | "left"rightThe edge of the feedback menu
offsetnumber | string50%The offset of the feedback menu
align"start" | "center" | "end"centerCross-axis alignment relative to the offset point along the chosen edge
collisionPaddingnumber16Minimum inset from the viewport or parent bounds when clamping panel position
labelstringFeedbackThe label of the feedback menu
iconOnlybooleanfalseWhether the feedback menu is icon only
badgestring | numberundefinedThe badge of the feedback menu
dotbooleanfalseWhether the feedback menu is a dot
defaultOpenbooleanfalseWhether the feedback menu is open by default
openbooleanundefinedWhether the feedback menu is open
onOpenChange() => voidundefinedCallback when the feedback menu is opened or closed
onSubmit() => voidundefinedCallback when the feedback is submitted
container"viewport" | "parent"viewportThe container of the feedback menu
titlestringShare feedbackThe title of the feedback menu
descriptionstringTell us what's working, what's broken, or what you'd love to see next.The description of the feedback menu
infoTooltipstringundefinedOptional tooltip shown on an info icon beside the panel title
feedbackTypesFeedbackTypeOption[]undefinedCustom feedback type options shown in the select field
showEmailbooleantrueWhether to show the optional email field
successTitlestringThanks for your feedbackHeading shown in the success state
successDescriptionstringWe've received your note and truly appreciate you taking the time.Description shown in the success state
submitLabelstringSubmit feedbackLabel for the submit button
submittingLabelstringSendingLabel for the submit button while loading
maxLengthnumber500The maximum length of the feedback message
classNamestringundefinedThe class name of the feedback menu
triggerClassNamestringundefinedThe class name of the feedback menu trigger
panelClassNamestringundefinedThe class name of the feedback menu panel