Implementing Post View Counters in a Headless WordPress Setup with Next.js

August 23, 2025MD Pabel
Implementing Post View Counters in a Headless WordPress Setup with Next.js

Headless CMS architectures are becoming more popular in modern web development because they are flexible and improve performance. Developers can make fast, dynamic sites that use WordPress’s powerful content management features by separating the backend (like WordPress) from the frontend (like Next.js). However, some features, like tracking and showing how many people have seen a post, need to be carefully integrated using REST APIs.

This post will show you how to add view counters to your blog posts when you use WordPress and Next.js without a head. To make things easier, we will use the well-known “Post Views Counter” plugin, turn on its REST API mode, and set up a custom endpoint. This method makes sure that tracking is accurate without using standard WordPress themes.

Why it is important to have view counters in a website

View counters give you useful information about how popular and engaging your content is. Plugins make this easy with shortcodes or theme hooks on a regular WordPress site. But if you use Next.js to build the front end of a headless site, you have to use WordPress’s REST API to talk to it. This means getting post data (including views) on the server side during server-side rendering (SSR) or static site generation (SSG) and adding counts on the client side to get real user views.

What is the problem? A lot of plugins do not show view counts in the best way for apps that use APIs. That is when personalization comes into play.

Step 1: Set up and install the Post Views Counter plugin

The first step is to install the free “Post Views Counter” plugin from the WordPress plugin repository. It is light and has more than 100,000 active installations. It also works with REST API for headless use.

  1. Go to your WordPress admin dashboard and log in.
  2. To install or activate “Post Views Counter,” go to Plugins > Add New and search for it.
  3. Go to Post Views tab and then Counter Mode.
  4. Choose REST API from the list of options in Counter Mode. This lets you count using an API, which is important for headless setups because it does not need PHP to run in themes or JavaScript to run in the browser.
  5. Set up other options:
    • Post Types: Check the box next to “post” (or any other type you want).
    • Set the time between counts to “1 day” to get unique views per user.
  6. To register the new routes, save your changes and flush the permalinks (Settings > Permalinks > Save Changes).

With this, the plugin keeps track of views in post meta (post_views_count) and adds an endpoint for adding views: POST /wp-json/post-views-counter/view-post/{post_id}.

Step 2: The Limitation and Why We Need a Custom Endpoint

The default increment endpoint works perfectly; it follows rules for uniqueness (like using local storage or cookies) and updates the count on the server. But its answer does not include the new total number of views. It gives back something like this:

{
  "post_id": 1093,
  "counted": true,
  "storage": {
    "name": ["pvc_visits[0]"],
    "value": ["1756048474b1093"],
    "expiry": [1756048474]
  },
  "type": "post"
}

To make this work, we will make a custom REST API endpoint that adds to the view count and sends back the new count in one response.

Step 3: Putting the Custom Endpoint in functions.php

Change the functions.php file for your theme (or make a custom plugin to make it easier to keep up with). Put in the following code:

// Custom REST API endpoint to get and add to the view count
add_action('rest_api_init', function() {
    register_rest_route('custom/v1', '/post-views/(?P<id>\d+)', array(
        'methods' => 'POST',
        'callback' => 'custom_increment_and_get_views',
        'permission_callback' => '__return_true', // Public; add auth for safety
        'args' => array(
            'id' => array(
                'validate_callback' => function($param) {
                    return is_numeric($param) && get_post($param);
                },
            ),
        ),
    ));
});

function custom_increment_and_get_views($request) {
    $post_id = $request['id'];
    
    if (!function_exists('pvc_view_post') || !function_exists('pvc_get_post_views')) {
        return new WP_Error('plugin_missing', 'Post Views Counter functions not found.', array('status' => 500));
    }
    
    // Add one more view to the post
    pvc_view_post($post_id);
    
    // Get the new total
    $views = pvc_get_post_views($post_id);
    
    return array(
        'post_id' => $post_id,
        'views' => $views,
        'success' => true,
    );
}
  • Endpoint: POST /wp-json/custom/v1/post-views/{post_id}
  • Example response: {"post_id":1093,"views":42,"success":true}
  • Benefits: One call for both increment and fetch. It uses the plugin’s built-in features, so it gets all of its settings, like uniqueness and exclusions.

Use curl to test it: curl -X POST https://your-site.com/wp-json/custom/v1/post-views/1093. Add nonce checks or API keys to make it safe in production.

Step 4: Working with Next.js

In your Next.js app, use getStaticProps or getServerSideProps to get the initial post data, which includes views. Then, on page load, use a useEffect hook to call the custom endpoint and update the display with the count that comes back.

In pages/posts/[slug].js, for example:

import { useEffect, useState } from "react";

export default function Post({ post }) {
    const [views, setViews] = useState(post.meta?.post_views_count || 0);

    useEffect(() => {
        const handleViews = async () => {
            const storageKey = "pvc_visits[0]";
            
            // Check if already viewed
            if (localStorage.getItem(storageKey)) return;

            try {
                const response = await fetch(`https://your-site.com/wp-json/custom/v1/post-views/${post.id}`, {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/json'
                    },
                });
                
                if (response.ok) {
                    const data = await response.json();
                    setViews(data.views);
                    // Set storage for uniqueness
                    localStorage.setItem(storageKey, `some-value-b${post.id}`);
                }
            } catch (error) {
                console.error('Error:', error);
            }
        };

        handleViews();
    }, [post.id]);

    return (
        <div>
            <h1>{post.title.rendered}</h1>
            <p>Views: {views}</p>
            {/* Content of the post */}
        </div>
    );
}

This uses Incremental Static Regeneration (ISR) to refresh the page every so often and localStorage to mimic the plugin’s unique logic.

Final Thoughts

You can easily keep track of and show view counts in your headless WordPress + Next.js blog by turning on REST API mode in Post Views Counter and adding a custom endpoint. This setup works well, is accurate, and is easy to keep up with. If your endpoint gets a lot of traffic, you might want to add caching (like with Redis) or rate limiting.

Have you done something like this before? Please tell us about your experiences in the comments. If you want to learn more about headless CMS, read my other posts about how to make Next.js faster.

About the Author

MD Pabel

MD Pabel

MD Pabel is the Founder and CEO of 3Zero Digital, a leading agency specializing in custom web development, WordPress security, and malware removal. With over 7+ Years years of experience, he has completed more than3200+ projects, served over 2300+ clients, and resolved4500+ cases of malware and hacked websites.