Comment

A single comment โ€” author, time, rich body, reactions and reply โ€” as a project feed draws it.

Marisol Vega

Here's Round 2. We leaned into the 70s marquee reference and softened the gradient behind the title.

Installation
$Terminal
npx shadcn@latest add https://sdk-components.thesqd.com/r/comment-card.json
Usage
One line: pass the author, the body and the handlers you want. Anything left out simply doesn't draw.
TSImport
import { CommentCard } from "@/components/blocks/comment-card";
TSExample
"use client";

import { CommentCard } from "@/components/blocks/comment-card";

export function Example() {
  return (
    <CommentCard
      author={{ name: "Marisol Vega" }}
      body="Looks great โ€” adding a couple pins."
      timestamp={new Date()}
      onReact={(emoji, added) => console.log(emoji, added)}
      onReply={() => console.log("reply")}
    />
  );
}
Composition
Anatomy of the avatar, the card, and its footer.
CommentCard                    (single component โ€” pass everything as props)
โ”œโ”€โ”€ avatar                     (author.avatar ?? Avatar(author.name, author.avatarUrl);
โ”‚                               inside the card, left of the name, by default โ€” avatarPosition)
โ””โ”€โ”€ card-surface               (brand tint when `mine`)
    โ”œโ”€โ”€ header
    โ”‚   โ”œโ”€โ”€ name ยท summary ยท (edited)   (name + avatar: author.card / onClick / href
    โ”‚   โ”‚                                โ†’ hover card, click, link; underline on hover)
    โ”‚   โ””โ”€โ”€ โ‹ฏ menu              (hover-revealed, `actions`)
    โ”œโ”€โ”€ body                   (string โ†’ rich markdown; any node as-is)
    โ””โ”€โ”€ footer
        โ”œโ”€โ”€ reaction chips + EmojiPicker popover + Reply + replies toggle   (left)
        โ””โ”€โ”€ time                                               (right; hover โ†’ full date)
Default
Avatar and name on top, the body, then reactions, Reply and the time in the footer. Hover the card for the โ‹ฏ menu.

Marisol Vega

Here's Round 2. We leaned into the 70s marquee reference and softened the gradient behind the title.

Custom avatar
`author.avatar` takes any node, so you can wire in your own face โ€” here Inky speaks for TheSquad. A string body renders as rich markdown.

TheSquad

Comments just got rich โœจ

Everything you can write in a comment now renders: lists, code, highlights and more.

Your comment
`mine` tints the card in the brand colour; `edited` marks a reworded note.
JV

Jacob Vendramin(edited)

Test

Read-only
Leave out `onReact`, `onReply` and `actions` and only the facts remain โ€” the chips still show how many agreed.
MV

Marisol Vega left a note on Sermon Bumper

Pushed the coral pop on the title โ€” let me know where to tighten.

Thread
Stack cards for a thread โ€” each carries its own face beside the name.

TheSquad

Comments just got rich โœจ

Everything you can write in a comment now renders: lists, code, highlights and more.

JV

Jacob Vendramin(edited)

Test

API Reference
Props for the card, its author, and a reaction chip.

CommentCard

PropTypeDefaultDescription
authorCommentAuthorโ€”Who left the comment โ€” name, photo, or your own avatar node.
bodyReactNodeโ€”A string renders as rich markdown; any other node as-is.
summaryReactNodeโ€”Muted words after the name, e.g. "left a note on Sermon Bumper".
headingReactNodeโ€”Replaces the name line (name, summary, (edited)) outright โ€” for a card that announces something.
timestampDate | string | numberโ€”"12:58 PM" today, "Sep 18 ยท 12:58 PM" otherwise; full date on hover.
timeReactNodeโ€”Replaces the formatted time outright.
editedbooleanfalseAdds "(edited)" after the name.
minebooleanfalseTints the card in the brand colour.
avatarPosition"outside" | "inside" | "none""inside"Where the avatar sits: in the card beside the name, beside the card (a column down a rail), or not at all.
reactionsCommentReaction[][]Chips in the footer. The card toggles them optimistically; a new array re-syncs.
reactionChoicesstring[]["๐Ÿ‘","โค๏ธ","๐ŸŽ‰","๐Ÿ˜‚","๐Ÿ‘€","๐Ÿ™"]The reaction picker's Popular row, shown until the viewer has recent picks.
emojiPickerPick<EmojiPickerProps, โ€ฆ>โ€”Passed to the reaction picker (the SDK EmojiPicker): saved `recent` / `skinTone` with their handlers, a phrase `suggester`, a self-hosted `emojibaseUrl`.
onReact(emoji: string, added: boolean) => voidโ€”Enables the picker and tappable chips.
onReply() => voidโ€”Shows the Reply button.
replyLabelstring"Reply"Reply button label.
actionsDropdownMenuItemSpec[]โ€”Items for the hover-revealed โ‹ฏ menu (same shape as DropdownMenu `items`).
actionsLabelstring"Show more actions"Tooltip + aria-label of the โ‹ฏ button.
classNamestringโ€”Classes on the outer row.
cardClassNamestringโ€”Classes on the card itself โ€” a highlight ring, an accent edge.

CommentAuthor

PropTypeDefaultDescription
namestringโ€”Display name; drives initials + avatar colour.
avatarUrlstring | nullโ€”Photo URL. Falls back to initials.
avatarReactNodeโ€”Your own avatar node (Inky, a presence ring, โ€ฆ). Wins over avatarUrl.
cardReactNodeโ€”Opened on hover or focus of the name or the avatar โ€” a person card, say.
onClick() => voidโ€”Fires on a click of the name or the avatar.
hrefstringโ€”Makes the name and the avatar a link. Any of card / onClick / href underlines the name on hover.

CommentReaction

PropTypeDefaultDescription
emojistringโ€”The glyph.
countnumberโ€”How many picked it.
minebooleanโ€”The viewer is one of them โ€” the chip reads pressed.
whostring[]โ€”Names for the hover label ("Josh, Ashley and 2 more").