Cnippet

Activity Log Sheet

Component 3

About

A slide-out sheet panel that shows a scrollable timeline of recent project activity with user avatars, colored event badges, and timestamps.

Built with React and Tailwind. Copy the prompt and paste it into Claude Code, Cursor or Lovable: your agent adds Activity Log Sheet to your project and fits it to your theme.

Code

How Activity Log Sheet is used: the demo's code. The component's own source comes with a free account.

Usage.tsx

import { ActivityIcon } from "lucide-react";
import {
  Avatar,
  AvatarFallback,
  AvatarImage,
} from "@/components/ui/v-sheet-8-utils/avatar";
import { Button } from "@/components/ui/v-sheet-8-utils/button";
import {
  Sheet,
  SheetClose,
  SheetDescription,
  SheetFooter,
  SheetHeader,
  SheetPanel,
  SheetPopup,
  SheetTitle,
  SheetTrigger,
} from "@/components/ui/v-sheet-8-utils/sheet";

type EventType = "deploy" | "merge" | "comment" | "invite" | "update";

type ActivityEvent = {
  id: number;
  user: { name: string; avatar?: string; initials: string };
  action: string;
  target: string;
  time: string;
  type: EventType;
};

const typeBadge: Record<EventType, { label: string; className: string }> = {
  comment: {
    className: "bg-blue-100 text-blue-700 dark:bg-blue-950 dark:text-blue-400",
    label: "Comment",
  },
  deploy: {
    className:
      "bg-emerald-100 text-emerald-700 dark:bg-emerald-950 dark:text-emerald-400",
    label: "Deploy",
  },
  invite: {
    className:
      "bg-violet-100 text-violet-700 dark:bg-violet-950 dark:text-violet-400",
    label: "Invite",
  },
  merge: {
    className:
      "bg-amber-100 text-amber-700 dark:bg-amber-950 dark:text-amber-400",
    label: "Merge",
  },
  update: {
    className:
      "bg-slate-100 text-slate-700 dark:bg-slate-800 dark:text-slate-300",
    label: "Update",
  },
};

const events: ActivityEvent[] = [
  {
    action: "deployed",
    id: 1,
    target: "v2.4.1 to production",
    time: "Just now",
    type: "deploy",
    user: {
      avatar:
        "https://cdn.21st.dev/assets/mirror/37/37e13852d9131364ad451046403c08bc497f86f914518aa6c2ba78c58d9e1811.jpg",
      initials: "SC",
      name: "Sarah Chen",
    },
  },
  {
    action: "merged PR #84",
    id: 2,
    target: "feat/dark-mode into main",
    time: "18 min ago",
    type: "merge",
    user: {
      avatar:
        "https://cdn.21st.dev/assets/mirror/54/54773704df065508594f531ad45d9eeb31e1c28d4ae58e5967979d7f341a4430.jpg",
      initials: "MK",
      name: "Marcus Kim",
    },
  },
  {
    action: "commented on",
    id: 3,
    target: "issue #112: Layout shift",
    time: "42 min ago",
    type: "comment",
    user: { initials: "AJ", name: "Aisha Johnson" },
  },
  {
    action: "invited",
    id: 4,
    target: "priya@example.com as Editor",
    time: "2 hr ago",
    type: "invite",
    user: { initials: "SC", name: "Sarah Chen" },
  },
  {
    action: "updated",
    id: 5,
    target: "project description and README",
    time: "5 hr ago",
    type: "update",
    user: {
      avatar:
        "https://cdn.21st.dev/assets/mirror/c0/c01c7571b7c8e890165e46e3383ec7ed3e0a8cf61bbe76e73d8f9fa3a4593db9.jpg",
      initials: "LR",
      name: "Liam Rivera",
    },
  },
  {
    action: "deployed",
    id: 6,
    target: "v2.3.9 hotfix to production",
    time: "Yesterday",
    type: "deploy",
    user: { initials: "AJ", name: "Aisha Johnson" },
  },
];

export default function Default() {
  return (
    <div className="flex min-h-72 w-full items-center justify-center p-6">
      <Sheet defaultOpen>
        <SheetTrigger render={<Button variant="outline" />}>
          <ActivityIcon aria-hidden="true" />
          Activity Log
        </SheetTrigger>
        <SheetPopup>
          <SheetHeader>
            <SheetTitle>Activity Log</SheetTitle>
            <SheetDescription>
              Recent actions taken across this project.
            </SheetDescription>
          </SheetHeader>
          <SheetPanel className="px-3">
            <div className="relative space-y-0">
              {events.map((event, index) => {
                const badge = typeBadge[event.type];
                return (
                  <div className="relative flex gap-3 pb-5" key={event.id}>
                    {index < events.length - 1 && (
                      <div className="absolute top-8 left-4 h-full w-px bg-border" />
                    )}
                    <Avatar className="z-10 size-8 shrink-0 ring-2 ring-background">
                      {event.user.avatar && (
                        <AvatarImage
                          alt={event.user.name}
                          src={event.user.avatar}
                        />
                      )}
                      <AvatarFallback className="text-xs">
                        {event.user.initials}
                      </AvatarFallback>
                    </Avatar>
                    <div className="min-w-0 flex-1 pt-0.5">
                      <div className="flex flex-wrap items-center gap-1.5">
                        <span className="font-medium text-sm">
                          {event.user.name}
                        </span>
                        <span className="text-muted-foreground text-sm">
                          {event.action}
                        </span>
                        <span
                          className={`rounded px-1.5 py-0.5 font-medium text-[10px] ${badge.className}`}
                        >
                          {badge.label}
                        </span>
                      </div>
                      <p className="mt-0.5 truncate text-muted-foreground text-xs">
                        {event.target}
                      </p>
                      <p className="mt-1 text-muted-foreground text-xs">
                        {event.time}
                      </p>
                    </div>
                  </div>
                );
              })}
            </div>
          </SheetPanel>
          <SheetFooter variant="bare">
            <SheetClose
              render={<Button className="w-full" variant="outline" />}
            >
              Close
            </SheetClose>
          </SheetFooter>
        </SheetPopup>
      </Sheet>
    </div>
  );
}

Dependencies

More by Cnippet

View all
  • Team

    Team

    1.3K
  • Davincho Hero-1

    Davincho Hero-1

    683
  • Aero Hero-3

    Aero Hero-3

    558
  • Features

    Features

    510
  • Random Letter Swap Navigation

    Random Letter Swap Navigation

    487
  • Services with Animated Hover Modal

    Services with Animated Hover Modal

    465
  • Blogs

    Blogs

    396
  • Aero Hero-1

    Aero Hero-1

    356

Explore more

Part of Cnippet — browse the full library on 21st.dev.