Comment Thread
Let collaborators discuss a piece of content and reach a conclusion without leaving it.
Comment
“revenue grew 38% year over year”
AL
Ada Lovelace2h
Should this number be Q3 or Q4? The chart above says Q4.
GH
Grace Hopper1h
Good catch, it is Q4. Fixing now.
Example result — your output may vary with your context and the prompt you pass.
# Comment Thread: Inline Anchored
## Context
Flexnative UI intent — a reference solution from the shadcn/ui registry (@flx). The intent frames the problem; the decision is one approach to it.
## Problem
Let collaborators discuss a piece of content and reach a conclusion without leaving it.
## Decision
- Best for: Feedback that points at a specific spot, like a sentence or a cell. The thread is pinned to the selection so the discussion keeps its context and resolving it clears the marker.
- Trade-off: Comments tied to a position break or orphan when the underlying content is edited or deleted, so anchoring needs careful handling.
## Registry Item
@flx/comment-thread-1
## Stack
- UI: shadcn/ui
- Styling: Tailwind CSS
- shadcn components: Avatar, Button, Card, Textarea
- npm: lucide-react
## Registry Source
https://ui.flexnative.com/r/comment-thread-1.json
## Preview
https://ui.flexnative.com/intents/comment-thread#1
## Reference Implementation
```tsx
'use client'
import { useState } from 'react'
import { Check, MessageSquare } from 'lucide-react'
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'
import { Button } from '@/components/ui/button'
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
import { Textarea } from '@/components/ui/textarea'
type Reply = {
id: string
name: string
avatar: string
time: string
text: string
}
const REPLIES: Reply[] = [
{
id: 'ada',
name: 'Ada Lovelace',
avatar:
'https://images.unsplash.com/photo-1494790108377-be9c29b29330?w=128&h=128&fit=crop&crop=faces',
time: '2h',
text: 'Should this number be Q3 or Q4? The chart above says Q4.',
},
{
id: 'grace',
name: 'Grace Hopper',
avatar:
'https://images.unsplash.com/photo-1438761681033-6461ffad8d80?w=128&h=128&fit=crop&crop=faces',
time: '1h',
text: 'Good catch, it is Q4. Fixing now.',
},
]
export function CommentThread1() {
const [replies, setReplies] = useState<Reply[]>(REPLIES)
const [draft, setDraft] = useState('')
const [resolved, setResolved] = useState(false)
const send = () => {
const text = draft.trim()
if (!text) return
setReplies((prev) => [
...prev,
{ id: `me-${prev.length}`, name: 'You', avatar: '', time: 'now', text },
])
setDraft('')
}
return (
<Card>
<CardHeader>
<CardTitle className="flex items-center gap-2">
<MessageSquare className="size-4" />
Comment
</CardTitle>
</CardHeader>
<CardContent className="flex flex-col gap-4">
<p className="border-primary bg-primary/5 border-l-2 py-1 pl-3 text-sm italic">
“revenue grew 38% year over year”
</p>
{resolved ? (
<div className="text-muted-foreground flex items-center gap-2 rounded-lg border border-dashed p-3 text-sm">
<Check className="size-4" />
Thread resolved
<Button
variant="link"
size="sm"
className="ml-auto h-auto p-0"
onClick={() => setResolved(false)}
>
Reopen
</Button>
</div>
) : (
<>
<div className="flex flex-col gap-4">
{replies.map((reply) => (
<div key={reply.id} className="flex gap-3">
<Avatar className="size-7">
<AvatarImage src={reply.avatar} alt={reply.name} />
<AvatarFallback className="bg-muted text-xs">
{reply.name
.split(' ')
.map((n) => n[0])
.join('')}
</AvatarFallback>
</Avatar>
<div className="flex flex-1 flex-col gap-0.5">
<div className="flex items-baseline gap-2">
<span className="text-sm font-medium">{reply.name}</span>
<span className="text-muted-foreground text-xs">
{reply.time}
</span>
</div>
<p className="text-sm">{reply.text}</p>
</div>
</div>
))}
</div>
<Textarea
placeholder="Reply..."
value={draft}
onChange={(e) => setDraft(e.target.value)}
className="min-h-16 resize-none"
/>
<div className="flex justify-end gap-2">
<Button
variant="ghost"
size="sm"
onClick={() => setResolved(true)}
>
<Check className="size-4" />
Resolve
</Button>
<Button size="sm" onClick={send} disabled={!draft.trim()}>
Reply
</Button>
</div>
</>
)}
</CardContent>
</Card>
)
}
```Paste into your AI tool and adapt it to your context.
Other ways
Reach for these when the context shifts. Each is copyable too.
Comments
ALAda Lovelace3 replies
on Executive summaryCan we lead with the revenue number instead?
ATAlan Turing1 replies
on Roadmap, Q3This milestone slipped, should we move it?
GHGrace Hopper2 replies
on Pricing tableTypo in the enterprise tier.
Side Panel
Activity
Grace Hopper was added as an editor3h
AL
Ada Lovelace2h
First draft is ready for review.
Alan Turing edited the pricing section2h
GH
Grace Hopper1h
Looks great. Shipping it.
Document shared with the design team40m
Activity Feed