Live Push Feed
A real-time push feed where new items land at the top on a spring while the rest of the column re-layouts underneath and the oldest row exits past the visible cap. Four enter presets (scale, slide, fade, bounce), rendering fully delegated via renderItem, demoed as a payments stream with an interleaved risk hold.
NotificationReactMotionTailwind CSSSolar Icons
CSSTailwind
Manual
Create a file and paste the following code into it.
live-push-feed.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
"use client";
import { type ReactNode } from "react";
import { AnimatePresence, motion } from "motion/react";
import { cn } from "@/lib/cn";
/* ------------------------------------------------------------------ */
/* LivePushFeed */
/* ------------------------------------------------------------------ */
export type PushAnimation = "scale" | "slide" | "fade" | "bounce";
interface LivePushFeedProps<T> {
/** Newest item first [Required] */
items: T[];
renderItem: (item: T, index: number) => ReactNode;
/** Rows kept on screen before the oldest exits [Optional, default: 8] */
maxVisible?: number;
/** Row gap in px [Optional, default: 12] */
gap?: number;
/** Enter/exit preset [Optional, default: "scale"] */
animation?: PushAnimation;
className?: string;
}
function presetVariants(type: PushAnimation) {
switch (type) {
case "slide":
return {
initial: { opacity: 0, y: -30 },
animate: { opacity: 1, y: 0 },
exit: { opacity: 0, y: -20 },
};
case "fade":
return {
initial: { opacity: 0 },
animate: { opacity: 1 },
exit: { opacity: 0 },
};
case "bounce":
return {
initial: { opacity: 0, y: -20, scale: 0.8 },
animate: { opacity: 1, y: 0, scale: 1 },
exit: { opacity: 0, scale: 0.8 },
};
default:
return {
initial: { opacity: 0, y: -20, scale: 0.95 },
animate: { opacity: 1, y: 0, scale: 1 },
exit: { opacity: 0, scale: 0.9 },
};
}
}
/**
* A real-time push feed: new items land at the top on a spring while the
* rest of the column re-layouts underneath and the oldest row exits past
* maxVisible. Rendering is fully delegated via renderItem, so the feed works
* for payments, logs, or notifications alike.
* @param {T[]} items - Feed data, newest first [Required]
* @param {(item, index) => ReactNode} renderItem - Row renderer [Required]
* @param {PushAnimation} animation - Enter/exit preset [Optional, default: "scale"]
*/
export function LivePushFeed<T extends { id: string | number }>({
items,
renderItem,
maxVisible = 8,
gap = 12,
animation = "scale",
className,
}: LivePushFeedProps<T>) {
const visible = items.slice(0, maxVisible);
const variants = presetVariants(animation);
return (
<div className={cn("flex flex-col", className)} style={{ gap: `${gap}px` }}>
<AnimatePresence mode="popLayout" initial={false}>
{visible.map((item, index) => (
<motion.div
key={item.id}
layout
initial={variants.initial}
animate={variants.animate}
exit={variants.exit}
transition={{
type: "spring",
stiffness: 350,
damping: 28,
layout: { type: "spring", stiffness: 350, damping: 28 },
}}
>
{renderItem(item, index)}
</motion.div>
))}
</AnimatePresence>
</div>
);
}
Update the import paths to match your project setup.
Similar components
Install via CLI
Resource details
PublishedJuly 17, 2026
CategoryNotification
ReactMotionTailwind CSSSolar Icons
Install via CLI
Resource details
PublishedJuly 17, 2026
CategoryNotification
ReactMotionTailwind CSSSolar Icons