Installation
Theme
Mode

Collaboration

Activity Feed

The system log for a project or task — who changed what, when. A day-grouped timeline of typed events (status, assignment, comment, due date, label, attachment) each with an actor, an icon, and the values that changed. Filter by category to cut the noise.

$npx shadcn@latest add @orbit/activity-feed

Overview

Live — use the All / Comments / Status / People filter to narrow the log. Each event shows the actor, its type badge on the avatar rim, and the values that changed: status pills with an arrow, @mention chips, label chips, and mono value chips.

Activity10
Today
  1. Maya Rivera changed status of Rebuild empty states In reviewDone
  2. Sam Chen commented on Onboarding flow v2
    Updated the CTA copy — @Jon Lake take a look when you can.
  3. Priya Kapoor assigned API rate limiting to @Dana Wu
  4. Jon Lake completed Design review
Yesterday
  1. Dana Wu created Billing webhook retries
  2. Maya Rivera added Bug to Fix auth token refresh
  3. Sam Chen set the due date of Quarterly roadmap draft to Jul 15
  4. Priya Kapoor attached metrics-q2.pdf to Quarterly review
Mon · Jun 23
  1. Jon Lake changed status of Marketing site refresh TodoIn progress
  2. Dana Wu mentioned @Sam Chen in Synthesis & insights

Event vocabulary

Nine event types, each with a fixed badge colour and glyph so the type reads instantly — and a sentence template that shows the concrete diff, not just that something changed. Types map to filter categories: comment and mention under Comments; created, status, and completed under Status; assigned under People.

createdSam created Billing webhook retriesvar(--pri)
statusMaya changed status of … Todo → In progressvar(--pri)
assignedPriya assigned … to @Dana Wuvar(--acc)
commentSam commented on … (+ preview card)var(--text-3)
dueSam set the due date of … to Jul 15var(--warning)
labelMaya added [Bug] to …pri/danger mix
attachmentPriya attached metrics-q2.pdf to …var(--text-3)
completedJon completed Design reviewvar(--success)
mentionDana mentioned @Sam Chen in …var(--pri)

Compact, or filter-free

Tighten the timeline for a sidebar with density, or hide the filter when the feed is already scoped. Both instances are live.

Compactdensity="compact"
Activity10
Today
  1. Maya Rivera changed status of Rebuild empty states In reviewDone
  2. Sam Chen commented on Onboarding flow v2
    Updated the CTA copy — @Jon Lake take a look when you can.
  3. Priya Kapoor assigned API rate limiting to @Dana Wu
  4. Jon Lake completed Design review
Yesterday
  1. Dana Wu created Billing webhook retries
  2. Maya Rivera added Bug to Fix auth token refresh
  3. Sam Chen set the due date of Quarterly roadmap draft to Jul 15
  4. Priya Kapoor attached metrics-q2.pdf to Quarterly review
Mon · Jun 23
  1. Jon Lake changed status of Marketing site refresh TodoIn progress
  2. Dana Wu mentioned @Sam Chen in Synthesis & insights
No filtershowFilter={false}
Activity10
Today
  1. Maya Rivera changed status of Rebuild empty states In reviewDone
  2. Sam Chen commented on Onboarding flow v2
    Updated the CTA copy — @Jon Lake take a look when you can.
  3. Priya Kapoor assigned API rate limiting to @Dana Wu
  4. Jon Lake completed Design review
Yesterday
  1. Dana Wu created Billing webhook retries
  2. Maya Rivera added Bug to Fix auth token refresh
  3. Sam Chen set the due date of Quarterly roadmap draft to Jul 15
  4. Priya Kapoor attached metrics-q2.pdf to Quarterly review
Mon · Jun 23
  1. Jon Lake changed status of Marketing site refresh TodoIn progress
  2. Dana Wu mentioned @Sam Chen in Synthesis & insights

Props

PropTypeDefaultDescription
groupsActivityGroup[]Day-grouped events, newest first: { day, events }.
density"comfortable" | "compact""comfortable"Row rhythm: 20px gap between events, or 14px for sidebars.
showFilterbooleantrueShow the All / Comments / Status / People segmented filter in the header.
headingstring"Activity"Header title, next to the live count of visible events.
filter / defaultFilter / onFilterChangeActivityFilter"all"Controlled or uncontrolled filter state.
emptyTextstringMessage when the active filter matches nothing.
event.type"created" | "status" | "assigned" | "comment" | "due" | "label" | "attachment" | "completed" | "mention"Picks the badge tone + glyph, the sentence template, and the filter category.
event.actor{ name, initials?, color? }Who did it — 30px initials avatar (deterministic token colour by default) plus a bold name.
event.targetstringThe task / item the event is about, rendered bold.
event.from / to"todo" | "in_progress" | "in_review" | "done" | { label, color }status events: the before → after pills. Built-in keys or custom values.
event.assignee / mention / preview / due / label / filestringType-specific values: @mention chips, comment preview (with @Name highlighting), mono value chips, tinted label chip (+ labelColor).
event.time / datetimestringHuman-readable timestamp, plus an optional machine-readable value for the <time> element.
event.detailReactNodeCustom detail card under the sentence — replaces the comment preview.
…restHTMLAttributesEverything else is forwarded to the root element, including ref.

Usage

An activity feed is a read-only, system-generated log. For human discussion use the Comment thread; for transient confirmations use a Toast.

Do

Keep entries factual

System-generated, past-tense, one event per line. The feed’s value is a trustworthy record, not commentary.

Don’t

Mix in editable content

No inline replies or edits here — it’s a log. Route discussion to the Comment thread so the history stays immutable.

Do

Show what actually changed

Prefer “Todo → In progress” over “updated status”. Concrete diffs let people skim and trust the record.

Don’t

Dump every micro-event

Collapse noisy or repeated actions and offer a filter. A wall of low-signal rows gets ignored.

Keyboard & accessibility

The feed itself is presentational; the filter is the only interactive part.

listEach day renders as a semantic <ol>, so screen readers announce position and count per group.
filterFilter buttons are real <button>s with aria-pressed; the header count tracks what is visible.
textEvery event is a full sentence — meaning never depends on the badge colour or glyph alone.
timePass datetime to get a real <time> element behind the human-readable label.