# Live Feed

> Real-time activity: LiveFeed (events as they happen, with pause and filters) and BarList (ranked values with inline bars, e.g.

Component · Data · Pro kit · [Live page](https://ui.twentyfirst.media/pro/components/LiveFeed/)

Real-time activity: `LiveFeed` (events as they happen, with pause and filters) and `BarList` (ranked values with inline bars, e.g. top countries or services).

LiveFeed: `items` ({id, type, icon, tint, title, text, amount, currency, time, fresh}), `types` ({value, label} filter chips), `paused` / `onPause`, `maxHeight`, `emptyText`. While paused, new events are held back and counted.
BarList: `items` ({id, label, mark, value, share (0–1), tint}), `format`, `onPick(item)`, `label`.

- Show at most ~50 events; older ones belong in a table.
- `fresh` slides a row in once; it never blinks or repeats.

## Example

```tsx
import * as React from "react";
import { BarList, LiveFeed, formatMoney } from "@twentyfirstmedia/pro";
import type { LiveFeedItem, LiveFeedProps } from "@twentyfirstmedia/pro";

const types: LiveFeedProps["types"] = [
  {
    value: "order",
    label: "Orders",
  },
  {
    value: "payment",
    label: "Payments",
  },
  {
    value: "error",
    label: "Errors",
  },
];
const seed: LiveFeedItem[] = [
  {
    id: 1,
    type: "order",
    icon: "cart",
    tint: "mint",
    title: "1,000 Instagram followers",
    text: "Order #48213 · Germany · PayPal",
    amount: 12.9,
    time: "2s",
  },
  {
    id: 2,
    type: "payment",
    icon: "card",
    tint: "sky",
    title: "Payment received",
    text: "Stripe · card ending 4242",
    amount: 39,
    time: "14s",
  },
  {
    id: 3,
    type: "error",
    icon: "zap",
    tint: "blush",
    title: "Provider timeout",
    text: "SMM provider B · retried",
    time: "41s",
  },
  {
    id: 4,
    type: "order",
    icon: "cart",
    tint: "mint",
    title: "5,000 TikTok views",
    text: "Order #48212 · Austria",
    amount: 4.9,
    time: "1m",
  },
];
function App() {
  const it = React.useState<LiveFeedItem[]>(seed);
  React.useEffect(() => {
    let n = 100;
    const t = setInterval(() => {
      n++;
      it[1]((l): LiveFeedItem[] => [
        {
          id: n,
          type: "order",
          icon: "cart",
          tint: "mint",
          title: ((n % 3) + 1) * 500 + " starter kits",
          text: "Order #" + (48213 + n - 100) + " · Switzerland",
          amount: 2.9 + (n % 5),
          time: "now",
          fresh: true,
        },
        ...l.slice(0, 7),
      ]);
    }, 2600);
    return () => {
      clearInterval(t);
    };
  }, []);
  return (
    <div
      className="tf-grid"
      style={
        {
          "--cols": "1.4fr 1fr",
          gap: 16,
          alignItems: "start",
        } as React.CSSProperties
      }
    >
      <div className="tf-panel">
        <LiveFeed items={it[0]} types={types} maxHeight={420} />
      </div>
      <div
        className="tf-panel"
        style={{
          gap: 12,
        }}
      >
        <b>Revenue by country</b>
        <BarList
          format={(v) => formatMoney(v)}
          items={[
            {
              label: "Germany",
              mark: "🇩🇪",
              value: 18240,
              share: 0.52,
            },
            {
              label: "Austria",
              mark: "🇦🇹",
              value: 6120,
              share: 0.17,
            },
            {
              label: "Switzerland",
              mark: "🇨🇭",
              value: 5480,
              share: 0.16,
              tint: "mint",
            },
            {
              label: "Netherlands",
              mark: "🇳🇱",
              value: 2210,
              share: 0.06,
              tint: "butter",
            },
            {
              label: "Other",
              value: 3100,
              share: 0.09,
              tint: "lilac",
            },
          ]}
        />
      </div>
    </div>
  );
}
export default App;
```

## Props

### BarList

| Prop | Type | Notes |
| --- | --- | --- |
| `items` | `{ id?: string; label: ReactNode; mark?: ReactNode; value: number; share?: number; tint?: Tint; }[]` | Required |
| `format?` | `(v: number) => string` |  |
| `onPick?` | `(item: NonNullable<BarListProps["items"]>[number]) => void` |  |
| `label?` | `string` |  |
| `className?` | `string` | Extra class on the root element. |
| `style?` | `CSSProperties` | Inline style on the root element. |

### LiveFeed

| Prop | Type | Notes |
| --- | --- | --- |
| `items` | `LiveFeedItem[]` | Required |
| `types?` | `{ value: string; label: string }[]` |  |
| `paused?` | `boolean` |  |
| `onPause?` | `(paused: boolean) => void` |  |
| `maxHeight?` | `number` |  |
| `emptyText?` | `string` |  |
| `className?` | `string` | Extra class on the root element. |
| `style?` | `CSSProperties` | Inline style on the root element. |
