Siteplane UI

Activity Feed

Pro

Show recent events by people with avatars, times and attachments.

Markdown

This Pattern is part of Siteplane UI Pro, and its source comes with a Pro license. Its previews are free to view and try.

Installation

pnpm dlx shadcn@4.18.0 add @siteplane-pro/activity-feed
Pro

Installs with a Siteplane UI Pro license key. Complete the one-time Pro setup first.

Get a license on the Pro page.

Usage

import {
  ActivityFeed,
  ActivityFeedItem,
} from "@/siteplane/components/activity-feed";

<ActivityFeed aria-label="Project activity">
  <ActivityFeedItem
    actor={{ name: "Mara Ellis" }}
    description="Added a file to Website redesign"
    timestamp="Just now"
  />
</ActivityFeed>;

Composition

ActivityFeed
└── ActivityFeedItem
    ├── ActivityFeedLink (in the description)
    ├── ActivityFeedAttachment
    ├── ActivityFeedMessage
    └── your own content, such as labels

The feed separates its items with a divider by default. Each item shows the portrait, the name and the time, then the description. An actor.href links the name and, for pointer users, the portrait to the same destination; the portrait stays out of the tab order, so keyboard and screen-reader users meet one link per person. Without avatarUrl the portrait shows initials taken from the name.

status adds the presence dot of Avatar to the portrait, as online, away, busy or offline. The dot is named for assistive technology by statusLabel, or else by the English status name. unreadLabel adds an unread marker with that text as its accessible name. Anything passed as children appears below the description, such as attachments, a message or your own content.

Connected layout

Set variant="connected" to draw a line through the portraits. It suits events that follow one another, such as the history of a task.

  1. Nora Lind
    Sent you a message
    We should discuss the draft today.
Pro

This example installs with a Siteplane UI Pro license key.

Get a license on the Pro page.

pnpm dlx shadcn@4.18.0 add @siteplane-pro/activity-feed-connected-example

Spaced layout

Set variant="spaced" to separate the items with space only, for a lighter feed inside a card or a sheet.

Messages

ActivityFeedMessage sets what a person wrote in a bordered message surface below the name. Use the description for a short line such as a handle. Messages work in all three layouts, and an attachment can follow a message.

  1. Lea Hartmann
    @lea
    Here is the latest draft.
    Prototype.mp4
    6.6 MB
Pro

This example installs with a Siteplane UI Pro license key.

Get a license on the Pro page.

pnpm dlx shadcn@4.18.0 add @siteplane-pro/activity-feed-messages-example

API reference

Component properties
PropTypeDefaultDescription
ActivityFeed.variantActivityFeedVariant: divided | connected | spaceddividedLayout of the items: a divider between them, a line through the portraits, or space only.
ActivityFeed.aria-labelstringActivity feedAccessible name of the list. Replace it with a name that fits the feed.
ActivityFeedItem.actor{ name, href?, avatarUrl?, initials? }requiredThe person. name is shown before the time, href links the name and the portrait, avatarUrl sets the portrait image and initials replace the ones taken from the name.
ActivityFeedItem.timestampReactNoderequiredVisible time of the event.
ActivityFeedItem.dateTimestringunsetMachine-readable time. With it the timestamp is a time element; without it, a plain span.
ActivityFeedItem.descriptionReactNodeunsetWhat happened, such as a sentence with an ActivityFeedLink.
ActivityFeedItem.statusonline | away | busy | offlineunsetAvatar's presence dot on the portrait.
ActivityFeedItem.statusLabelstringEnglish status nameAccessible name of the dot.
ActivityFeedItem.presence{ state: online | offline; label: string }unsetDeprecated: the earlier form of status and statusLabel, which it maps to. Use status and statusLabel.
ActivityFeedItem.unreadLabelstringunsetShows an unread marker. The text is its accessible name.
ActivityFeedItem.childrenReactNodeunsetContent below the description, such as attachments, labels or a message.
ActivityFeedAttachment.fileNamestringrequiredFile name, also used to choose the file type icon.
ActivityFeedAttachment.fileTypestringfrom fileNameExtension for the icon when it differs from the file name, for example for files without a suffix.
ActivityFeedAttachment.metadataReactNodeunsetSecondary line, such as the file size.

ActivityFeedProps extends native ol props, ActivityFeedItemProps native li props except the animation and drag handlers, which belong to the motion, ActivityFeedLink native anchor props, ActivityFeedMessage native div props and ActivityFeedAttachmentProps native figure props. All accept refs and string className, and all but ActivityFeedAttachment take children: the attachment renders its file icon, name and metadata itself. ActivityFeedLink renders an inline link button and ActivityFeedMessage sets its children in a Card with the message variant.

Usage notes

The feed is an ordered list of articles. Pass the items newest first and give the list an aria-label that names it. Pass dateTime so the timestamp becomes a time element with a machine-readable value; without it, the timestamp is plain text.

The feed does not sort, group by day, paginate, virtualize or announce new items. For a long or live feed, add these in the application and announce new entries with your own live region.

Entries that are added or removed animate on their own. A new entry expands and fades in, and a removed one fades out and collapses; the divider or the connecting line opens and closes with it while the other entries glide. Give every ActivityFeedItem a stable key, because an index as the key makes a removed entry leave as its neighbor, and pass the entries as direct children of the feed, not inside a fragment. A leaving entry is inert, so move focus before you remove one that holds it. With reduced motion, from the system setting or SiteplaneMotionProvider preset="reduced", every change is instant.

Status dots and unread markers are indicators only. An attachment shows the file and does not link or download it, so add a link or a button beside it when people need to open the file.