Visitor Tracker in WordPress (Real-Time Count + Smooth Admin Pop-up)
Visitor Tracker in WordPress (Real-Time Count + Smooth Admin Pop-up)

Visitor Tracker in WordPress Want a lightweight way to see how many people are on your site right now—and get a subtle pop-up when someone new arrives? This tutorial gives you a clean PHP snippet you can drop into a snippets plugin. It tracks all visitors, updates counts in real time, and shows a floating pop-up and sticky counter button to administrators.

What you’ll get

  • Real-time online visitors count (updates every 5 seconds)
  • Smooth pop-up for admins when a new visitor arrives
  • Persistent “Online: X” button for quick glance
  • Works without heavy analytics plugins
  • Uses WordPress transients (no custom tables)

Requirements

  • WordPress with jQuery enabled on the front end (default in most themes)
  • A snippets plugin like “Code Snippets” or “WPCode”
  • Administrator role to see the pop-up and button

Step-by-step setup

  1. Copy the full code below
  2. Open your snippets plugin → Add New
  3. Paste the code, set it to run on the Frontend (and Backend optional), save and activate
  4. Load any page as an admin—you’ll see the floating online count button in the bottom right

The PHP snippet (paste as-is)

				
					// Hook to add the visitor tracking and popup for admins
add_action('wp_footer', 'admin_visitor_tracker');

function admin_visitor_tracker() {
    if (!current_user_can('administrator')) {
        return;
    }

    // Generate a temporary session ID
    if (!session_id()) {
        session_start();
    }
    $session_id = session_id();

    // Update visitor data
    $visitors = get_transient('online_visitors') ?: array();
    $visitors[$session_id] = time();
    set_transient('online_visitors', $visitors, 300); // Store for 5 minutes

    // Output HTML and JavaScript
    ?>
    
        #visitor-popup, #visitor-count-button {
            position: fixed;
            right: 20px;
            background: white;
            color: #333;
            padding: 15px;
            border-radius: 5px;
            box-shadow: 0 2px 10px rgba(0,0,0,0.1);
            z-index: 9999;
            font-size: 14px;
            font-family: Arial, sans-serif;
            transition: opacity 0.5s ease-in-out, transform 0.5s ease-in-out;
        }
        #visitor-popup {
            display: none;
            bottom: 80px;
            transform: translateY(20px);
            opacity: 0;
        }
        #visitor-count-button {
            bottom: 20px;
            cursor: pointer;
            opacity: 0;
            transform: translateY(20px);
            display: none;
        }
        #visitor-count-button:hover {
            background-color: #f0f0f0;
        }
    
    <div id="visitor-popup">
        <p>New visitor entered</p>
        <p>Online visitors: <span id="popup-visitor-count">0</span></p>
    </div>
    <div id="visitor-count-button">
        Online: <span id="button-visitor-count">0</span>
    </div>
    
    jQuery(document).ready(function($) {
        var lastCount = 0;
        var updateAfterPopup = false; // Flag to update the button count after popup

        function updateVisitorCount() {
            $.ajax({
                url: '',
                type: 'POST',
                data: {
                    action: 'update_visitor_count',
                    nonce: ''
                },
                success: function(response) {
                    if (response.count &gt; lastCount) {
                        updateAfterPopup = true; // Set flag to update after popup
                        $('#visitor-count-button').animate({opacity: 0, transform: 'translateY(20px)'}, 500, function() {
                            $(this).css('display', 'none');
                            $('#visitor-popup').css({display: 'block', opacity: 0, transform: 'translateY(20px)'})
                                .animate({opacity: 1, transform: 'translateY(0)'}, 500)
                                .delay(5000)
                                .animate({opacity: 0, transform: 'translateY(20px)'}, 500, function() {
                                    $(this).css('display', 'none');
                                    $('#visitor-count-button').css({display: 'block', opacity: 0, transform: 'translateY(20px)'})
                                        .animate({opacity: 1, transform: 'translateY(0)'}, 500, function() {
                                            if (updateAfterPopup) {
                                                $('#button-visitor-count').text(response.count); // Update button count after popup is hidden
                                                updateAfterPopup = false; // Reset flag
                                            }
                                        });
                                });
                        });
                    } else {
                        $('#button-visitor-count').text(response.count); // Update button count immediately if no new visitor
                    }
                    $('#popup-visitor-count').text(response.count); // Always update popup count
                    lastCount = response.count;
                }
            });
        }

        // Update immediately and then every 5 seconds
        updateVisitorCount();
        setInterval(updateVisitorCount, 5000);

        // Clear session when leaving the page
        $(window).on('beforeunload', function() {
            $.ajax({
                url: '',
                type: 'POST',
                async: false,
                data: {
                    action: 'remove_visitor',
                    nonce: ''
                }
            });
        });
    });
    
     $time) {
        if ($current_time - $time &gt; 300) {
            unset($visitors[$id]);
        }
    }

    set_transient('online_visitors', $visitors, 300);

    $count = count($visitors);

    wp_send_json(array('count' =&gt; $count));
}

// AJAX handler to remove visitor when leaving
add_action('wp_ajax_remove_visitor', 'remove_visitor');
add_action('wp_ajax_nopriv_remove_visitor', 'remove_visitor');

function remove_visitor() {
    check_ajax_referer('visitor_tracker_nonce', 'nonce');

    $session_id = session_id();
    $visitors = get_transient('online_visitors') ?: array();

    if (isset($visitors[$session_id])) {
        unset($visitors[$session_id]);
        set_transient('online_visitors', $visitors, 300);
    }

    wp_send_json_success();
}

// Track all visitors, not just admins
add_action('wp_head', 'track_visitor');

function track_visitor() {
    if (!session_id()) {
        session_start();
    }
    $session_id = session_id();

    $visitors = get_transient('online_visitors') ?: array();
    $visitors[$session_id] = time();
    set_transient('online_visitors', $visitors, 300);
}

				
			

How it works

  • Every visitor gets a temporary session ID and is stored in a transient (online_visitors) for 5 minutes
  • A cron-like refresh happens via AJAX every 5 seconds to keep the count accurate and remove inactive sessions
  • Admins see a pop-up only when the count increases, then a compact floating button with the latest number
  • When a visitor leaves, a final AJAX call removes their session from the count

Customization ideas

  • Change poll frequency: update the setInterval(updateVisitorCount, 5000) value
  • Adjust inactivity timeout: modify the 300 seconds in set_transient and the cleanup check
  • Move UI: tweak right and bottom values in the CSS for #visitor-popup and #visitor-count-button
  • Restrict to specific pages: wrap admin_visitor_tracker() content with conditionals (e.g., is_page())

Troubleshooting

  • Not seeing the button? Confirm you’re logged in as an Administrator and your theme prints wp_footer()
  • AJAX blocked? Check security plugins or caching layers; ensure admin-ajax.php is accessible
  • Counts stuck? Some page caches can delay updates—add don’t cache for logged-in users and exclude the admin role

Pro tip for teams

If you want editors to see the counter too, replace current_user_can('administrator') with a capability like edit_pages or check multiple roles before returning.

Faqs