Menu

Change language

GitHub repo

Activity Feed

blockproductivity

Record activity as a Timeline of avatar rows. Each row is spelled out inline — actor, verb, optional comment, relative time. Edit the rows after install; no items wrapper.

Installation

$npx shadcn@latest add https://uipkge.dev/r/lit/activity-feed.json
Manifest:https://uipkge.dev/r/lit/activity-feed.jsonInstalls to:src/uip/blocks/activity-feed/

Variants

Loading interactive previews…

Schema

Type aliases exported from this item's source. Use these to shape the data you pass in.

HTMLElementTagNameMap
interface HTMLElementTagNameMap {
    'uip-activity-feed': UipActivityFeed
  }
}

Files installed (1)

  • src/uip/blocks/activity-feed/activity-feed.ts5.1 kB
    import { LitElement, css, html } from 'lit'
    import { tailwind } from '../../lib/styles'
    import { ThemeController } from '../../lib/theme'
    // Side-effect imports: register the composed elements for standalone use
    // (consumers who install only this block still get working primitives).
    import '../../components/avatar/avatar'
    import '../../components/relative-time/relative-time'
    import '../../components/section-card/section-card'
    import '../../components/timeline/timeline'
    
    /**
     * <uip-activity-feed> — the registry ActivityFeed block as a web component.
     *
     * Class strings are React's ActivityFeed verbatim. Every row is spelled out
     * inline as in React (actor, verb, optional comment, relative time) — no
     * items wrapper, per the block boundary rules. Timestamps are pinned to the
     * same fixed `now` as React so the relative labels match the demo.
     */
    export class UipActivityFeed extends LitElement {
      static shadowRootOptions = { ...LitElement.shadowRootOptions, delegatesFocus: true }
      // :host display can't be a utility class (the host has no template of its own).
      static styles = [tailwind, css`:host { display: block; }`]
    
      constructor() {
        super()
        new ThemeController(this)
      }
    
      connectedCallback() {
        super.connectedCallback()
        this.setAttribute('data-uipkge', '')
        this.setAttribute('data-slot', 'activity-feed')
      }
    
      render() {
        const now = '2026-08-14T12:00:00.000Z'
        return html`
          <uip-section-card data-slot="activity-feed" heading="Activity" description="Latest changes on this record.">
            <uip-timeline>
              <uip-timeline-item status="success">
                <uip-timeline-media variant="avatar">
                  <uip-avatar size="sm">
                    <uip-avatar-fallback size="sm" class="text-foreground">MC</uip-avatar-fallback>
                  </uip-avatar>
                </uip-timeline-media>
                <uip-timeline-content>
                  <p class="text-sm">
                    <span class="font-medium">Maya Chen</span>
                    <span class="text-muted-foreground"> approved the leave request</span>
                  </p>
                  <uip-relative-time
                    date="2026-08-14T11:18:00.000Z"
                    now=${now}
                    locale="en"
                    class="mt-0.5 block text-xs"
                  ></uip-relative-time>
                </uip-timeline-content>
              </uip-timeline-item>
    
              <uip-timeline-item status="info">
                <uip-timeline-media variant="avatar">
                  <uip-avatar size="sm">
                    <uip-avatar-fallback size="sm" class="text-foreground">OF</uip-avatar-fallback>
                  </uip-avatar>
                </uip-timeline-media>
                <uip-timeline-content>
                  <p class="text-sm">
                    <span class="font-medium">Omar Farid</span>
                    <span class="text-muted-foreground"> added a comment</span>
                  </p>
                  <p class="text-muted-foreground mt-1 text-sm">Coverage is confirmed for Thursday and Friday.</p>
                  <uip-relative-time
                    date="2026-08-14T10:04:00.000Z"
                    now=${now}
                    locale="en"
                    class="mt-0.5 block text-xs"
                  ></uip-relative-time>
                </uip-timeline-content>
              </uip-timeline-item>
    
              <uip-timeline-item status="muted">
                <uip-timeline-media variant="avatar">
                  <uip-avatar size="sm">
                    <uip-avatar-fallback size="sm" class="text-foreground">PS</uip-avatar-fallback>
                  </uip-avatar>
                </uip-timeline-media>
                <uip-timeline-content>
                  <p class="text-sm">
                    <span class="font-medium">Priya Shah</span>
                    <span class="text-muted-foreground"> updated the title to “Q3 planning offsite”</span>
                  </p>
                  <uip-relative-time
                    date="2026-08-13T16:40:00.000Z"
                    now=${now}
                    locale="en"
                    class="mt-0.5 block text-xs"
                  ></uip-relative-time>
                </uip-timeline-content>
              </uip-timeline-item>
    
              <uip-timeline-item status="muted">
                <uip-timeline-media variant="avatar">
                  <uip-avatar size="sm">
                    <uip-avatar-fallback size="sm" class="text-foreground">LP</uip-avatar-fallback>
                  </uip-avatar>
                </uip-timeline-media>
                <uip-timeline-content>
                  <p class="text-sm">
                    <span class="font-medium">Leo Park</span>
                    <span class="text-muted-foreground"> opened this request</span>
                  </p>
                  <uip-relative-time
                    date="2026-08-13T09:12:00.000Z"
                    now=${now}
                    locale="en"
                    class="mt-0.5 block text-xs"
                  ></uip-relative-time>
                </uip-timeline-content>
              </uip-timeline-item>
            </uip-timeline>
          </uip-section-card>
        `
      }
    }
    
    customElements.get('uip-activity-feed') || customElements.define('uip-activity-feed', UipActivityFeed)
    
    declare global {
      interface HTMLElementTagNameMap {
        'uip-activity-feed': UipActivityFeed
      }
    }
    

Raw manifest:https://uipkge.dev/r/lit/activity-feed.json