Counting Page Views with Upstash Redis in a Next.js App Router Project
Learn how I integrated Upstash Redis into my personal website, sandimaulanajuhana.com, to create a fast, serverless page view counter with Next.js App Router. A practical guide to building lightweight and scalable features using edge-optimized technologies.

Improving Website Performance with Upstash Redis: A Case Study on sandimaulanajuhana.com
In today’s fast-paced digital landscape, tracking page views isn't just a vanity metric — it’s an essential insight into how your content resonates with visitors. When developing my personal website, sandimaulanajuhana.com, I chose Upstash Redis to handle page view counters in a lightweight, efficient, and scalable way.
Why Redis? Why Upstash?
Redis is well-known as a lightning-fast in-memory data store. But traditionally, setting up Redis means managing servers, memory usage, and region-specific latency. This is where Upstash shines: a serverless Redis that integrates seamlessly with edge environments like Vercel — where my site is hosted.
Benefits:
⚡ Speed: Data stored in-memory, not on disk.
🌍 Edge support: Super low latency globally.
💸 Affordable (even free for small projects): Perfect for personal sites and portfolios.
🔁 Stateless-friendly: No sessions required, great for edge functions.
View Counter Architecture
Each article on my website has a unique slug. When a visitor opens the page:
- A POST request to /api/views/[slug] increments the Redis counter.
- A GET request to the same endpoint fetches the current view count.
- The number is displayed live on the page. (👁️ 1,234 views)
API route example:
// /api/views/[slug]/route.ts
import { redis } from '@/lib/redis'
import { NextRequest, NextResponse } from 'next/server'
export async function POST(_: NextRequest, { params }: { params: { slug: string } }) {
const slug = params.slug
await redis.incr(`views:${slug}`)
return NextResponse.json({ success: true })
}
export async function GET(_: NextRequest, { params }: { params: { slug: string } }) {
const views = await redis.get<number>(`views:${params.slug}`)
return NextResponse.json({ views: views ?? 0 })
}
Frontend component:
// components/ViewCounter.tsx
'use client'
import { useEffect, useState } from 'react'
export default function ViewCounter({ slug }: { slug: string }) {
const [views, setViews] = useState<number | null>(null)
useEffect(() => {
fetch(`/api/views/${slug}`, { method: 'POST' })
fetch(`/api/views/${slug}`)
.then(res => res.json())
.then(data => setViews(data.views))
}, [slug])
if (views === null) return null
return <span className="text-sm text-gray-500 dark:text-gray-400">👁️ {views.toLocaleString()} views</span>
}
Telegram Notifications (Experimental)
As an experimental feature, I extended the view system to send a Telegram notification every time a page view occurs, including:
-
The article slug or URL
-
(Optionally) visitor IP address
-
Visitor device and location (via Vercel Analytics)
-
Timestamp
To avoid spamming:
-
Only 1 notification per IP per article can be triggered
-
Or restrict notifications by time intervals (e.g., every 1 hour)
Integrating Upstash Redis with a Next.js App Router project is more than just showing numbers — it’s about using modern, serverless tools to build smart and scalable web features.
Your personal site isn’t just a portfolio. It’s also a playground for experimentation — and Redis is one of the sharpest tools in the toolbox.