Build Read-Next Rails with Medium Related and Recommended Articles
One article view is a bounce waiting to happen. Related and recommended endpoints let you show a second and third story without sending traffic back to medium.com.
Tool outcome:
<ReadNext articleId="…" />with 24h cache and editorial overrides.
Placement ideas
- End-of-article rail on embedded Medium posts
- Sidebar on documentation blogs
- “More from this author” after portfolio posts (widget guide)
Fetch recommendations
const API = 'https://api.zenndra.com';
const headers = { Authorization: `Bearer ${process.env.ZENNDRA_API_KEY}` };
async function getReadNext(articleId) {
const cacheKey = `readnext:${articleId}`;
const hit = await cache.get(cacheKey);
if (hit) return JSON.parse(hit);
const [related, recommended] = await Promise.all([
fetch(`${API}/article/${articleId}/related`, { headers }).then((r) => r.json()),
fetch(`${API}/article/${articleId}/recommended`, { headers }).then((r) => r.json()),
]);
const merged = dedupeById([
...(related.articles ?? []),
...(recommended.articles ?? []),
]).slice(0, 6);
await cache.set(cacheKey, JSON.stringify(merged), { ex: 86400 });
return merged;
}
function dedupeById(items) {
const seen = new Set();
return items.filter((a) => (seen.has(a.id) ? false : (seen.add(a.id), true)));
}
Mix your own rules
API suggestions + product logic wins:
function rankReadNext(candidates, { sameTag, excludeIds }) {
return candidates
.filter((a) => !excludeIds.has(a.id))
.sort((a, b) => (sameTag(a) === sameTag(b) ? 0 : sameTag(a) ? -1 : 1));
}
Exclude articles the reader already opened (localStorage or your analytics).
UX metrics
Track second-page views and scroll depth on rail—not just clicks. Session depth is the attribution game when Medium was the original source.
Keywords
medium related articles api, read next widget, medium recommendations, increase time on site, medium embed engagement.
Further reading
- Nielsen Norman: related content UX patterns
- Zenndra: Build read next and recommendation widgets
Top comments (0)