Counting Page Views with Upstash Redis in a Next.js App Router Project

October 10, 2024⏱️ 3 min read

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.

Counting Page Views with Upstash Redis in a Next.js App Router Project

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:

  1. A POST request to /api/views/[slug] increments the Redis counter.
  2. A GET request to the same endpoint fetches the current view count.
  3. 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.