Imagine an online store that feels lifeless. Visitors don't see what others are buying or viewing — they leave with a sense of emptiness. One of our clients faced a 20% drop in conversion after a redesign. Analysis showed the new version didn't display other users' activity. We implemented a real-time activity feed. The result: conversion returned to its previous level and grew by an additional 12%.
We build a live activity feed — a continuous stream of events: "Ivan bought a product," "Maria left a review," "5 people are viewing now." This creates social proof and keeps visitors engaged. After implementation, conversion consistently rises by 10–15%. Our team has 7 years of experience with high-load systems and over 50 successful projects.
How the Activity Feed Solves the Social Proof Problem
The feed shows real user actions in real time, creating a sense of presence. Visitors see that a product is in demand and make decisions faster. Deduplication and anonymization make the stream natural, avoiding a spammy feel. By combining a real-time activity feed with SSE server-sent events and React EventSource, we deliver social proof efficiently.
Problems We Solve
- Empty, lifeless page. A mechanism for instant activity display is needed. Without it, users don't feel trust.
- Spam and manipulation. Without filtering, the feed becomes a channel for bots. We implement deduplication, a 30-second cooldown, and name anonymization.
- Performance under spikes. SSE on Redis pub/sub handles 10,000+ concurrent subscribers without losing events. Delivery time is under 200 ms.
How We Do It: Stack and Architecture
We use a combination: Node.js (Express) on the backend, Redis for pub/sub, and React 18 with EventSource on the frontend. The combination of SSE server-sent events and React EventSource enables efficient real-time updates. This approach is lighter and more reliable than WebSocket for one-way streaming, making it an ideal WebSocket alternative. According to the MDN documentation on Server-Sent Events, SSE works over standard HTTP and automatically reconnects on interruption. Our feed also supports push notifications website integration, allowing users to receive instant alerts.
Server-Side Event Generation
class ActivityFeedService {
async publishActivity(event: ActivityEvent): Promise<void> {
// Save to DB for new visitors
await this.activityRepo.create(event);
// Publish to Redis for live subscribers
await this.redis.publish('activity:feed', JSON.stringify(event));
// Clean old events (store for 24 hours)
await this.activityRepo.deleteOlderThan(24 * 60 * 60 * 1000);
}
}
// Integration with business logic
orderService.on('order:created', async (order) => {
const product = await productRepo.findById(order.items[0].productId);
await activityFeed.publishActivity({
type: 'purchase',
text: `${anonymizeName(order.customerName)} bought «${product.name}»`,
location: order.customerCity,
timestamp: new Date(),
metadata: { productId: product.id }
});
});
SSE Feed Endpoint
Example SSE endpoint implementation
app.get('/api/activity/stream', (req, res) => {
res.setHeader('Content-Type', 'text/event-stream');
res.setHeader('Cache-Control', 'no-cache');
res.setHeader('Connection', 'keep-alive');
// Send last 10 events
activityRepo.findRecent(10).then(events => {
res.write(`event: init\ndata: ${JSON.stringify(events)}\n\n`);
});
// Subscribe to new events
const subscriber = redis.duplicate();
subscriber.subscribe('activity:feed');
subscriber.on('message', (_, message) => {
res.write(`event: activity\ndata: ${message}\n\n`);
});
const heartbeat = setInterval(() => res.write(':ping\n\n'), 20000);
req.on('close', () => {
clearInterval(heartbeat);
subscriber.unsubscribe();
subscriber.quit();
});
});
React Component
function ActivityFeed() {
const [activities, setActivities] = useState<Activity[]>([]);
useEffect(() => {
const source = new EventSource('/api/activity/stream');
source.addEventListener('init', (e) => {
setActivities(JSON.parse(e.data));
});
source.addEventListener('activity', (e) => {
const activity = JSON.parse(e.data);
setActivities(prev => [activity, ...prev].slice(0, 20));
});
return () => source.close();
}, []);
return (
<div className="activity-feed">
{activities.map((activity, i) => (
<ActivityItem key={activity.id} activity={activity}
style={{ opacity: Math.max(0.3, 1 - i * 0.05) }} />
))}
</div>
);
}
function ActivityItem({ activity, style }) {
const icons = { purchase: '🛍', review: '⭐', view: '👁' };
return (
<div className="activity-item" style={style}>
<span className="icon">{icons[activity.type]}</span>
<span className="text">{activity.text}</span>
<span className="time">{formatRelativeTime(activity.timestamp)}</span>
</div>
);
}
Why SSE Is Faster Than WebSocket for This Task
SSE (Server-Sent Events) uses a single HTTP connection without additional protocols. Auto-reconnection is built in — on interruption, the browser automatically restores the stream. WebSocket requires a handshake and maintaining a connection, which is overkill for one-way streaming. Our Node.js streaming ensures smooth delivery. Comparison:
| Feature | SSE | WebSocket |
|---|---|---|
| Connection type | Unidirectional | Bidirectional |
| Protocol | HTTP | Custom |
| Auto-reconnect | Built-in | Requires code |
| Implementation | Simple | Complex |
Our feed handles 1M events per day with 99.9% uptime, ensuring reliable delivery of push notifications and real-time updates. Clients often save $2,000 per year in server costs by using SSE instead of WebSocket.
Ensuring a Realistic Feed
The event deduplication algorithm prevents repeated identical events in a row. We anonymize names (first character + *) and implement user anonymization to protect privacy. We introduce a random delay of 0–5 seconds, and limit frequency — no more than one event per 2 seconds per user. Our anti-spam feed mechanisms prevent abuse and maintain authenticity. Additionally, we store history for 24 hours so new visitors don't see an empty screen.
// Deduplication — don't show identical events consecutively
const recentTexts = new Set<string>();
async function shouldPublish(event: ActivityEvent): Promise<boolean> {
const key = `${event.type}:${event.metadata?.productId}`;
if (recentTexts.has(key)) return false;
recentTexts.add(key);
setTimeout(() => recentTexts.delete(key), 30 * 1000); // 30 sec cooldown
return true;
}
Our Process
- Analysis: Determine event types (purchases, reviews, views) and update frequency.
- Design: Design Redis schema, SSE endpoint, React components.
- Implementation: Write publish service, streaming, client part.
- Testing: Load testing (up to 10k subscribers), deduplication checks.
- Deployment: Configure Nginx for SSE, monitor Redis, document.
| Stage | Duration |
|---|---|
| Analysis and design | 1 day |
| Backend development (Redis + SSE) | 1–2 days |
| React component integration | 1 day |
| Testing and deployment | 1 day |
| Total | 3–5 days |
Estimated Timelines
Basic integration takes 3–5 days. If complex logic (CRM integration, custom filters) is needed, up to 10 days. The exact budget is determined after a free consultation — contact us to discuss your project. Pricing starts at $1,500 for the basic package, with complex integrations typically $3,000–$5,000. For example, an e-commerce store with 100k monthly visitors can expect an additional $10,000 in revenue per month after implementing the feed. Clients often save $2,000 per year in server costs by using SSE instead of WebSocket.
What's Included
- React ActivityFeed component with fade-out animation
- Node.js SSE endpoint with Redis pub/sub
- Anti-spam and deduplication
- Nginx configuration for long-lived connections
- Documentation for integrating new events
- Team training (1-hour online session)
- Additional optimizations can significantly reduce implementation time
Common Implementation Mistakes
- Missing heartbeat. Without ping messages every 20 seconds, load balancers drop the connection.
- No init event. A new visitor sees an empty list until the first event appears.
- Subscriber leaks. Not unsubscribing when the page closes causes Redis to accumulate dead channels.
We guarantee stable feed performance under load and provide full documentation. Contact us to discuss your project. Get a consultation on implementing an activity feed. We'll assess your task and propose the optimal solution.







