# Activity Feed By Day

> Activity timeline grouped by day, with avatar rows for each actor, action, target, and relative timestamp connected by a vertical rail.

A shadcn/ui block from the [@7ovr](https://7ovr.com) registry, listed on [shadcn.exchange](https://shadcn.exchange/r/7ovr/timeline-3).

- Page, with a live preview: https://shadcn.exchange/r/7ovr/timeline-3
- Screenshot: https://abzjhwgwpdpz7ua5.public.blob.vercel-storage.com/shots/7ovr/timeline-3-light-318669d8f46c2139.png
- Registry item: https://7ovr.com/r/timeline-3.json
- Categories: timeline
- Listed: 2026-10-13, source last changed 2026-10-13

## Install

```bash
npx shadcn@latest add https://7ovr.com/r/timeline-3.json
```

Run it in a project that has shadcn/ui set up. It adds the files below and installs what they depend on.

## Dependencies

- npm packages: `@base-ui/react`
- Registry items: `avatar`, `badge`, `marker`

## Files

### `registry/blocks/timeline/3/timeline-block.tsx`

```tsx
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"
import { Badge } from "@/components/ui/badge"
import { Marker, MarkerContent } from "@/components/ui/marker"
import { IconPlaceholder } from "@/components/icons/icon-placeholder"
import type * as React from "react"

type IconProps = { className?: string; size?: number | string }

type IconRenderer = (props: IconProps) => React.ReactNode

type Event = {
  actor: string
  initials: string
  avatar: string
  action: string
  target: string
  time: string
  icon: IconRenderer
}

const groups: { label: string; events: Event[] }[] = [
  {
    label: "Today",
    events: [
      {
        actor: "Mara Ellis",
        initials: "ME",
        avatar: "https://i.pravatar.cc/80?img=47",
        action: "merged pull request",
        target: "feat/auth-refresh",
        time: "12 Min Ago",
        icon: (p: IconProps) => (
          <IconPlaceholder
            lucide="GitMerge"
            tabler="IconGitMerge"
            hugeicons="GitMergeIcon"
            phosphor="GitMerge"
            remixicon="RiGitMergeLine"
            {...p}
          />
        ),
      },
      {
        actor: "Leo Tanaka",
        initials: "LT",
        avatar: "https://i.pravatar.cc/80?img=12",
        action: "deployed to production",
        target: "v2.4.1",
        time: "1 Hour Ago",
        icon: (p: IconProps) => (
          <IconPlaceholder
            lucide="Upload"
            tabler="IconUpload"
            hugeicons="UploadIcon"
            phosphor="Upload"
            remixicon="RiUploadLine"
            {...p}
          />
        ),
      },
      {
        actor: "Priya Nair",
        initials: "PN",
        avatar: "https://i.pravatar.cc/80?img=32",
        action: "resolved a comment on",
        target: "Q3 Roadmap",
        time: "3 Hours Ago",
        icon: (p: IconProps) => (
          <IconPlaceholder
            lucide="MessageCircle"
            tabler="IconMessageCircle"
            hugeicons="Message01Icon"
            phosphor="ChatCircle"
            remixicon="RiChat3Line"
            {...p}
          />
        ),
      },
    ],
  },
  {
    label: "Yesterday",
    events: [
      {
        actor: "Sasha Kim",
        initials: "SK",
        avatar: "https://i.pravatar.cc/80?img=48",
        action: "completed task",
        target: "Migrate billing service",
        time: "Jun 24",
        icon: (p: IconProps) => (
          <IconPlaceholder
            lucide="Check"
            tabler="IconCheck"
            hugeicons="Tick02Icon"
            phosphor="Check"
            remixicon="RiCheckLine"
            {...p}
          />
        ),
      },
      {
        actor: "Owen Brooks",
        initials: "OB",
        avatar: "https://i.pravatar.cc/80?img=53",
        action: "invited a new member",
        target: "design@acme.com",
        time: "Jun 24",
        icon: (p: IconProps) => (
          <IconPlaceholder
            lucide="UserPlus"
            tabler="IconUserPlus"
            hugeicons="UserAddIcon"
            phosphor="UserPlus"
            remixicon="RiUserAddLine"
            {...p}
          />
        ),
      },
    ],
  },
]

export default function TimelineBlock() {
  return (
    <section className="flex w-full items-center justify-center bg-background px-6 py-16 text-foreground">
      <div className="mx-auto w-full max-w-xl">
        <div className="mb-8 flex items-center justify-between gap-4">
          <div>
            <h2 className="font-heading text-xl font-bold tracking-tight">
              Activity
            </h2>
            <p className="mt-1 text-sm text-muted-foreground">
              What your team has been working on lately.
            </p>
          </div>
          <Badge variant="secondary" className="shrink-0">
            5 Updates
          </Badge>
        </div>

        <div className="flex flex-col gap-6">
          {groups.map((group) => (
            <div key={group.label} className="flex flex-col gap-4">
              <Marker variant="separator">
                <MarkerContent>{group.label}</MarkerContent>
              </Marker>

              <ol className="relative flex flex-col gap-5">
                <span
                  className="absolute top-3 bottom-3 left-4 w-px bg-border"
                  aria-hidden="true"
                />
                {group.events.map((event) => (
                  <li
                    key={`${event.actor}-${event.target}`}
                    className="relative flex items-center gap-3"
                  >
                    <span className="relative shrink-0">
                      <Avatar className="size-9 border border-border">
                        <AvatarImage
                          src={event.avatar}
                          alt={event.actor}
                          className="grayscale"
                        />
                        <AvatarFallback>{event.initials}</AvatarFallback>
                      </Avatar>
                      <span className="absolute -right-1 -bottom-1 flex size-4 items-center justify-center rounded-full border border-border bg-card text-muted-foreground">
                        <event.icon className="size-2.5" aria-hidden="true" />
                      </span>
                    </span>

                    <p className="flex-1 text-sm text-muted-foreground">
                      <span className="font-medium text-foreground">
                        {event.actor}
                      </span>{" "}
                      {event.action}{" "}
                      <span className="font-medium text-foreground">
                        {event.target}
                      </span>
                    </p>

                    <span className="shrink-0 text-xs text-muted-foreground tabular-nums">
                      {event.time}
                    </span>
                  </li>
                ))}
              </ol>
            </div>
          ))}
        </div>
      </div>
    </section>
  )
}
```
