bigbullui logobigbullui
Componentmedia

CommentThread

Nested comments with avatars, collapse/reply actions, and dashed indent guides.

Preview

Live Preview

CommentThread

JD
Jordan3 hours ago

This is an amazing component library!

CS
Casey2 hours ago

Agreed, the animation quality is top-notch.

RL
Riley1.5 hours ago

The dark mode looks great too.

MG
Morgan4 hours ago

Question about the story viewer implementation.

Installation

Tokens guide →

Add to your project via CLI (zero dependencies, code you own):

npx bigbullui add comment-thread

Or install the full npm package:

npm install bigbullui

Usage

import { CommentThread } from "@/components/ui/comment-thread";

<CommentThread comments={comments} maxDepth={3} />

Source code

comment-thread.tsx

Zero dependencies (only React + utils). Copy and paste directly into your project:

"use client";

import * as React from "react";
import { cn } from "./lib/utils";

export interface CommentNode {
  id: string;
  author: string;
  initials: string;
  body: string;
  time?: string;
  children?: CommentNode[];
}

export interface CommentThreadProps {
  comments: CommentNode[];
  maxDepth?: number;
}

const CommentThread: React.FC<CommentThreadProps> = ({
  comments,
  maxDepth = 3,
}) => {
  const renderComment = (
    node: CommentNode,
    depth: number
  ): React.ReactNode => {
    if (depth > maxDepth) return null;

    const isLast = depth === 0; // simplified

    return (
      <div
        key={node.id}
        className={cn(
          "flex flex-col gap-1",
          depth > 0 && "pl-4 border-l-2 border-border/40"
        )}
      >
        <div className="flex items-start gap-2">
          <span
            className={cn(
              "rounded-full bg-border/20 p-1 text-[10px] mono uppercase text-muted-foreground",
              "w-6 h-6"
            )}
          >
            {node.initials}
          </span>
          <div className="flex-1 min-w-0">
            <div className="flex items-baseline gap-1 text-sm">
              <span className="mono uppercase text-muted-foreground">
                {node.author}
              </span>
              <span className="text-[10px] mono uppercase text-muted-foreground">
                {node.time || ""}
              </span>
            </div>
            <p className="text-sm break-words">{node.body}</p>
          </div>
        </div>

        {node.children && node.children.length > 0 && depth < maxDepth && (
          <div
            className={cn(
              "mt-2 pl-1 border-l-2 border-dashed border-border/30",
              "motion-reduce:transition-none"
            )}
          >
            {node.children.map((child) => renderComment(child, depth + 1))}
          </div>
        )}
      </div>
    );
  };

  return (
    <div className="space-y-2">
      {comments.map((comment) => renderComment(comment, 0))}
    </div>
  );
};

export { CommentThread };

Props

PropTypeDescription
commentsCommentNode[]Comment tree with optional children.
maxDepthnumberMaximum nesting depth (default 3).