Floating feedback card (bottom-right) with a 1–5 star rating. A comment box reveals for low scores, a brand Submit button appears once a rating is picked, and a success check plays on submit. Self-shows with a per-user cooldown and never touches host form state.
Click to open the feedback card.
Comments always show; the placeholder flips by score.
npx shadcn@latest add https://sdk-components.thesqd.com/r/experience-rating.jsonimport { ExperienceRating } from "@/components/blocks/experience-rating";import { ExperienceRating } from "@/components/blocks/experience-rating";
export function Feedback() {
return (
<ExperienceRating
// Anything here is passed straight through to onSubmit.
metadata={{ userId: "abc", formId: "welcome", route: "/onboarding" }}
onSubmit={async ({ rating, comments, metadata }) => {
// Persist however the host app likes (its own route, Supabase, …).
await fetch("/api/feedback", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ rating, comments, metadata }),
});
}}
/>
);
}{
"rating": 3,
"comments": "test",
"metadata": {}
}ExperienceRating — the floating feedback card (self-showing, cooldown-gated)
ExperienceRatingDemo — button-triggered preview
ExperienceRatingScoredDemo — score-aware placeholder preview| Prop | Type | Default | Description |
|---|---|---|---|
metadata | Record<string, any> | {} | Passed straight through to onSubmit and into the emitted object. |
onSubmit | (r: { rating, comments, metadata }) => void | Promise | — | Called on submit. Host decides how to persist. Success check awaits the promise. |
onDismiss | () => void | — | Fired when dismissed via the X. |
open | boolean | — | Controlled visibility. When set, auto-show + cooldown are bypassed. |
onOpenChange | (open: boolean) => void | — | Fires with the next open state. |
ratingStyle | "emoji" | "stars" | "emoji" | Rating control style. |
emojis | string[] | ["😞","🙁","😐","🙂","😄"] | Override the 5 emojis (low→high). Only used when ratingStyle="emoji". |
title | ReactNode | "How was your experience?" | Card heading. |
description | ReactNode | "Your feedback helps us improve." | Sub-line under the title. |
commentThreshold | number | 3 | Show the comment box when the rating is ≤ this value. Set to 5 to always show. |
commentPlaceholder | string | ((rating: number) => string) | "Tell us what we could do better…" | Comment placeholder. Pass a function of the rating to vary it by score, e.g. "Tell us what we did right" on 5. |
submitLabel | ReactNode | "Submit feedback" | Submit button label. |
successLabel | ReactNode | "Thanks for the feedback!" | Success-state message. |
cooldownDays | number | 7 | Don't re-prompt within this many days of last shown/dismissed/filled. |
storageKey | string | "squad-experience-rating" | localStorage namespace for the cooldown timestamps. |
anchor | "fixed" | "absolute" | "fixed" | "fixed" portals to <body>; "absolute" anchors to the nearest positioned parent. |